oh-my-opencode 4.5.12 → 4.7.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/.agents/skills/opencode-qa/SKILL.md +194 -0
- package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
- package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
- package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
- package/.agents/skills/opencode-qa/references/sdk.md +96 -0
- package/.agents/skills/opencode-qa/references/server-api.md +200 -0
- package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
- package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
- package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
- package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
- package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
- package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
- package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
- package/README.ja.md +13 -3
- package/README.ko.md +13 -3
- package/README.md +24 -14
- package/README.ru.md +13 -3
- package/README.zh-cn.md +13 -3
- package/bin/oh-my-opencode.js +4 -3
- package/bin/oh-my-opencode.test.ts +35 -7
- package/bin/platform.d.ts +1 -1
- package/bin/platform.js +4 -4
- package/bin/platform.test.ts +31 -9
- package/bin/version-mismatch.js +47 -0
- package/bin/version-mismatch.test.ts +120 -0
- package/dist/cli/cleanup-command.d.ts +4 -0
- package/dist/cli/cleanup.d.ts +11 -0
- package/dist/cli/cli-program.d.ts +2 -1
- package/dist/cli/codex-ulw-loop.d.ts +12 -0
- package/dist/cli/doctor/checks/tui-plugin-config.d.ts +2 -0
- package/dist/cli/index.js +2189 -529
- package/dist/cli/install-codex/codex-cache.d.ts +1 -0
- package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
- package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
- package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
- package/dist/cli/install-codex/codex-config-reasoning.d.ts +2 -0
- package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
- package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
- package/dist/cli/install-codex/codex-model-catalog.d.ts +13 -0
- package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
- package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
- package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
- package/dist/cli/install-codex/git-bash.d.ts +35 -0
- package/dist/cli/install-codex/index.d.ts +4 -0
- package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
- package/dist/cli/install-codex/types.d.ts +20 -0
- package/dist/cli/run/event-state.d.ts +1 -0
- package/dist/cli/run/poll-for-completion.d.ts +1 -0
- package/dist/cli/run/prompt-start.d.ts +7 -0
- package/dist/cli/star-request.d.ts +9 -0
- package/dist/config/schema/hooks.d.ts +0 -1
- package/dist/create-hooks.d.ts +0 -1
- package/dist/features/background-agent/concurrency.d.ts +1 -0
- package/dist/features/background-agent/process-cleanup.d.ts +6 -0
- package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/features/claude-code-session-state/state.d.ts +1 -0
- package/dist/features/opencode-skill-loader/index.d.ts +1 -0
- package/dist/features/opencode-skill-loader/opencode-config-skills-reader.d.ts +5 -0
- package/dist/features/tmux-subagent/attachable-session-status.d.ts +1 -1
- package/dist/features/tmux-subagent/session-status-parser.d.ts +1 -0
- package/dist/hooks/comment-checker/cli.d.ts +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/tasks-todowrite-disabler/constants.d.ts +1 -1
- package/dist/index.js +1077 -563
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
- package/dist/plugin/messages-transform.d.ts +8 -1
- package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
- package/dist/shared/command-executor/execute-hook-command.d.ts +2 -0
- package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
- package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
- package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
- package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
- package/dist/shared/prompt-async-gate/types.d.ts +2 -0
- package/dist/shared/prompt-async-gate.d.ts +1 -1
- package/dist/tools/skill/description-formatter.d.ts +5 -1
- package/dist/tools/skill/types.d.ts +1 -0
- package/package.json +22 -18
- package/packages/ast-grep-mcp/dist/cli.js +53 -9
- package/packages/git-bash-mcp/dist/cli.js +367 -0
- package/packages/lsp-tools-mcp/dist/lsp/process.js +1 -1
- package/packages/omo-codex/plugin/.mcp.json +11 -0
- package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
- package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
- package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
- package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
- package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
- package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
- package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
- package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
- package/packages/omo-codex/plugin/components/rules/README.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +6 -4
- package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +0 -2
- package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +4 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +9 -8
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +32 -6
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +28 -205
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +231 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +12 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint.test.ts +19 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
- package/packages/omo-codex/plugin/hooks/hooks.json +35 -2
- package/packages/omo-codex/plugin/model-catalog.json +49 -0
- package/packages/omo-codex/plugin/package-lock.json +19 -0
- package/packages/omo-codex/plugin/package.json +3 -1
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +159 -0
- package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
- package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +269 -0
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +89 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -6
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +127 -0
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +9 -0
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +6 -6
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +33 -8
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +25 -5
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +28 -205
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +231 -0
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +17 -17
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +188 -20
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +129 -0
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +58 -11
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +146 -0
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
- package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +67 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +54 -2
- package/packages/omo-codex/scripts/install/cache.mjs +5 -3
- package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
- package/packages/omo-codex/scripts/install/config.mjs +23 -1
- package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
- package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
- package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
- package/packages/omo-codex/scripts/install/legacy-bins.mjs +1 -0
- package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
- package/packages/omo-codex/scripts/install/model-catalog.mjs +66 -0
- package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
- package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
- package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
- package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
- package/packages/omo-codex/scripts/install/reasoning-config.mjs +72 -0
- package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
- package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
- package/packages/omo-codex/scripts/install-bin-links.test.mjs +23 -0
- package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
- package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
- package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +141 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +205 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +157 -0
- package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
- package/packages/omo-codex/scripts/install-local.mjs +91 -8
- package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
- package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
- package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
- package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +127 -0
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +9 -0
- package/packages/shared-skills/skills/review-work/SKILL.md +33 -8
- package/packages/shared-skills/skills/start-work/SKILL.md +25 -5
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -11
- package/postinstall.mjs +36 -3
- package/dist/hooks/context-window-monitor.d.ts +0 -19
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# opencode SDK (@opencode-ai/sdk) - reference only
|
|
2
|
+
|
|
3
|
+
A TypeScript/Bun way to drive opencode for QA. Prefer the tested CLI/curl scripts for portability; reach for the SDK when you want typed access from a Bun script.
|
|
4
|
+
|
|
5
|
+
> IMPORTANT: method signatures differ between SDK versions and between the published docs and the generated client. ALWAYS check the installed version's types (node_modules/@opencode-ai/sdk) before relying on a signature, and verify against `GET /doc` (the OpenAPI spec the SDK is generated from).
|
|
6
|
+
|
|
7
|
+
## Entry points and exports
|
|
8
|
+
|
|
9
|
+
Package `@opencode-ai/sdk` subpath exports:
|
|
10
|
+
|
|
11
|
+
- `.` (src/index.ts)
|
|
12
|
+
- `./client`
|
|
13
|
+
- `./server`
|
|
14
|
+
- `./v2` (src/v2/index.ts)
|
|
15
|
+
- `./v2/client`
|
|
16
|
+
- `./v2/server`
|
|
17
|
+
- `./v2/gen/client`
|
|
18
|
+
|
|
19
|
+
Root and v2 entries export `createOpencode()`, `createOpencodeClient(...)`, `createOpencodeServer(...)`.
|
|
20
|
+
|
|
21
|
+
`createOpencodeServer()` spawns `opencode serve ...` and waits for the startup line. `createOpencodeClient({ baseUrl })` wraps the generated client, rewrites directory/workspace headers, installs error interception.
|
|
22
|
+
|
|
23
|
+
## Two ways to connect
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk/v2"
|
|
27
|
+
|
|
28
|
+
// A) embedded server (spawns opencode serve)
|
|
29
|
+
const server = await createOpencodeServer()
|
|
30
|
+
const client = createOpencodeClient({ baseUrl: server.url })
|
|
31
|
+
// ... use client ...
|
|
32
|
+
server.close()
|
|
33
|
+
|
|
34
|
+
// B) connect to an already-running server
|
|
35
|
+
const client2 = createOpencodeClient({ baseUrl: "http://127.0.0.1:4096" })
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Client namespaces
|
|
39
|
+
|
|
40
|
+
Top-level on OpencodeClient:
|
|
41
|
+
|
|
42
|
+
auth, app, global, event, config, experimental, tool, worktree, find, file, instance, path, vcs, command, lsp, formatter, mcp, project, pty, question, permission, provider, session, part, sync, v2, tui.
|
|
43
|
+
|
|
44
|
+
## Useful methods (shapes vary by version)
|
|
45
|
+
|
|
46
|
+
- `client.global.health()`, `client.global.event()`
|
|
47
|
+
- `client.app.log(...)`, `client.app.agents(...)`, `client.app.skills(...)`
|
|
48
|
+
- `client.config.get()`, `client.config.providers()`
|
|
49
|
+
- `client.event.subscribe()` - SSE on /event; iterate `for await (const event of events.stream) { event.type, event.properties }`
|
|
50
|
+
- `client.session` (legacy surface): list, create, status, get, update, delete, children, todo, diff, messages, message, deleteMessage, prompt, promptAsync, command, shell, fork, abort, init, share, unshare, summarize, revert, unrevert
|
|
51
|
+
- `client.v2.session` (newer read/stream surface): list, prompt, compact, wait, context, messages
|
|
52
|
+
- `client.part.delete(...)`, `client.part.update(...)`
|
|
53
|
+
|
|
54
|
+
## Minimal QA snippet
|
|
55
|
+
|
|
56
|
+
Arg shapes may differ by version. Treat this as a starting point, not a contract.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk/v2"
|
|
60
|
+
|
|
61
|
+
const server = await createOpencodeServer()
|
|
62
|
+
const client = createOpencodeClient({ baseUrl: server.url })
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const session = await client.session.create({ title: "QA session" })
|
|
66
|
+
await client.session.promptAsync({
|
|
67
|
+
sessionID: session.id,
|
|
68
|
+
parts: [{ type: "text", text: "Say hello in one line." }],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const sessions = await client.session.list({ limit: 10 })
|
|
72
|
+
console.log(sessions[0]?.title)
|
|
73
|
+
|
|
74
|
+
const messages = await client.v2.session.messages({
|
|
75
|
+
sessionID: session.id,
|
|
76
|
+
limit: 20,
|
|
77
|
+
})
|
|
78
|
+
console.log(messages.items.length)
|
|
79
|
+
} finally {
|
|
80
|
+
server.close()
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Key types
|
|
85
|
+
|
|
86
|
+
- Legacy Session: id, slug, projectID, directory, title, version, time.created/updated, optional workspaceID, path, parentID, summary, cost, tokens, share, agent, model, metadata, permission, revert.
|
|
87
|
+
- Message = UserMessage | AssistantMessage (role "user" | "assistant"; assistant adds time.completed?, modelID, providerID, agent, tokens, finish?, error?).
|
|
88
|
+
- Part union: TextPart, ReasoningPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart, AgentPart, RetryPart, CompactionPart, SubtaskPart.
|
|
89
|
+
|
|
90
|
+
## How it is generated
|
|
91
|
+
|
|
92
|
+
`packages/sdk/js/script/build.ts` runs `bun dev generate > openapi.json` from the opencode repo, feeds it to `@hey-api/openapi-ts.createClient`, writes output to `packages/sdk/js/src/v2/gen`, patches an SSE generic, prettifies and typechecks. Regenerate with `./packages/sdk/js/script/build.ts`.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
For version-stable QA, prefer the curl/CLI scripts in this skill; cross-check any SDK call against `GET /doc`.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# opencode HTTP server API for QA (Case B)
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Start a server](#start-a-server)
|
|
6
|
+
- [Authentication](#authentication)
|
|
7
|
+
- [Per-request workspace routing](#per-request-workspace-routing)
|
|
8
|
+
- [Introspect the API](#introspect-the-api)
|
|
9
|
+
- [Tested smoke calls](#tested-smoke-calls)
|
|
10
|
+
- [Route catalog](#route-catalog)
|
|
11
|
+
- [Triggering a prompt over HTTP](#triggering-a-prompt-over-http)
|
|
12
|
+
|
|
13
|
+
## Start a server
|
|
14
|
+
|
|
15
|
+
Run the server with a fixed port and host:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
opencode serve --port 4096 --hostname 127.0.0.1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Output:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
opencode server listening on http://127.0.0.1:4096
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Port 0 means the server will pick 4096, then fall back to a free port if that one is taken.
|
|
28
|
+
|
|
29
|
+
A bundled isolated smoke test is available at `scripts/server-smoke.sh`. It spawns an isolated server, checks `/global/health`, checks that `/doc` returns at least 100 paths, and confirms that no-auth requests get 401, then tears the server down.
|
|
30
|
+
|
|
31
|
+
## Authentication
|
|
32
|
+
|
|
33
|
+
Set the environment variable `OPENCODE_SERVER_PASSWORD` to require authentication. If it is unset, the server runs UNSECURED and prints a warning.
|
|
34
|
+
|
|
35
|
+
The username defaults to `opencode`. Override it with `OPENCODE_SERVER_USERNAME`.
|
|
36
|
+
|
|
37
|
+
Two ways to authenticate:
|
|
38
|
+
|
|
39
|
+
1. HTTP Basic Auth: `-u opencode:$PASS`
|
|
40
|
+
2. Query parameter: `?auth_token=<base64(user:pass)>`
|
|
41
|
+
|
|
42
|
+
Unauthenticated requests to protected routes return HTTP 401. This was verified.
|
|
43
|
+
|
|
44
|
+
## Per-request workspace routing
|
|
45
|
+
|
|
46
|
+
Most instance routes need the target project directory. Pass it as either:
|
|
47
|
+
|
|
48
|
+
- Query parameter: `?directory=$PWD`
|
|
49
|
+
- Header: `x-opencode-directory: $PWD`
|
|
50
|
+
|
|
51
|
+
Aliases also work: `x-opencode-workspace` header or `?workspace=` query parameter.
|
|
52
|
+
|
|
53
|
+
The server resolves an instance per request, so a single `serve` process can handle many projects.
|
|
54
|
+
|
|
55
|
+
## Introspect the API
|
|
56
|
+
|
|
57
|
+
The `/doc` endpoint returns the full OpenAPI spec. To list all documented paths:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
curl -s -u opencode:$PASS http://127.0.0.1:4096/doc | jq '.paths | keys'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
On v1.15.13 this returned 113 paths. This is the source of truth for exact request and response schemas.
|
|
64
|
+
|
|
65
|
+
## Tested smoke calls
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -s -u opencode:$PASS http://127.0.0.1:4096/global/health | jq .
|
|
69
|
+
# {"healthy":true,"version":"1.15.13"}
|
|
70
|
+
|
|
71
|
+
curl -s -u opencode:$PASS http://127.0.0.1:4096/doc | jq '.paths|length'
|
|
72
|
+
# 113
|
|
73
|
+
|
|
74
|
+
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4096/session?directory=$PWD
|
|
75
|
+
# 401 (no credentials)
|
|
76
|
+
|
|
77
|
+
curl -s -u opencode:$PASS "http://127.0.0.1:4096/session?directory=$PWD" | jq 'length'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Route catalog
|
|
81
|
+
|
|
82
|
+
This mirrors the structure returned by `/doc`. Each entry is grouped as `method path - purpose`.
|
|
83
|
+
|
|
84
|
+
### Global
|
|
85
|
+
|
|
86
|
+
- `GET /global/health` - health check
|
|
87
|
+
- `GET /global/event` - server-wide SSE event stream
|
|
88
|
+
- `GET /global/config` - read global configuration
|
|
89
|
+
- `PATCH /global/config` - update global configuration
|
|
90
|
+
- `POST /global/dispose` - dispose the server instance
|
|
91
|
+
- `GET /doc` - OpenAPI specification
|
|
92
|
+
|
|
93
|
+
### Session
|
|
94
|
+
|
|
95
|
+
- `GET /session` - list sessions
|
|
96
|
+
- `GET /session/status` - session status overview
|
|
97
|
+
- `GET /session/:id` - get session by ID
|
|
98
|
+
- `GET /session/:id/children` - list child sessions
|
|
99
|
+
- `GET /session/:id/todo` - get session todo items
|
|
100
|
+
- `GET /session/:id/diff` - get session diff
|
|
101
|
+
- `GET /session/:id/message` - list session messages
|
|
102
|
+
- `GET /session/:id/message/:messageID` - get a specific message
|
|
103
|
+
- `POST /session` - create a new session
|
|
104
|
+
- `DELETE /session/:id` - delete a session
|
|
105
|
+
- `PATCH /session/:id` - update a session
|
|
106
|
+
- `POST /session/:id/fork` - fork a session
|
|
107
|
+
- `POST /session/:id/abort` - abort a session
|
|
108
|
+
- `POST /session/:id/init` - initialize a session
|
|
109
|
+
- `POST /session/:id/share` - share a session
|
|
110
|
+
- `POST /session/:id/summarize` - summarize a session
|
|
111
|
+
- `POST /session/:id/revert` - revert a session
|
|
112
|
+
- `POST /session/:id/unrevert` - unrevert a session
|
|
113
|
+
- `DELETE /session/:id/share` - unshare a session
|
|
114
|
+
|
|
115
|
+
### Prompting
|
|
116
|
+
|
|
117
|
+
- `POST /session/:id/message` - send a prompt; streams JSON
|
|
118
|
+
- `POST /session/:id/prompt_async` - fire-and-forget prompt; returns 204
|
|
119
|
+
- `POST /session/:id/command` - execute a command in a session
|
|
120
|
+
- `POST /session/:id/shell` - run a shell command in a session
|
|
121
|
+
|
|
122
|
+
### Files and find
|
|
123
|
+
|
|
124
|
+
- `GET /find` - text search via ripgrep
|
|
125
|
+
- `GET /find/file` - file search
|
|
126
|
+
- `GET /find/symbol` - symbol search
|
|
127
|
+
- `GET /file` - file metadata
|
|
128
|
+
- `GET /file/content` - file contents
|
|
129
|
+
- `GET /file/status` - file status
|
|
130
|
+
|
|
131
|
+
### Instance and app
|
|
132
|
+
|
|
133
|
+
- `GET /path` - path resolution
|
|
134
|
+
- `GET /vcs` - version control info
|
|
135
|
+
- `GET /vcs/status` - VCS status
|
|
136
|
+
- `GET /vcs/diff` - VCS diff
|
|
137
|
+
- `GET /command` - available commands
|
|
138
|
+
- `GET /agent` - available agents
|
|
139
|
+
- `GET /skill` - available skills
|
|
140
|
+
- `GET /lsp` - LSP info
|
|
141
|
+
- `GET /formatter` - formatter info
|
|
142
|
+
|
|
143
|
+
### Permission and question
|
|
144
|
+
|
|
145
|
+
- `GET /permission` - list pending permission requests
|
|
146
|
+
- `POST /permission/:requestID/reply` - reply to a permission request
|
|
147
|
+
- `GET /question` - list pending questions
|
|
148
|
+
- `POST /question/:requestID/reply` - reply to a question
|
|
149
|
+
- `POST /question/:requestID/reject` - reject a question
|
|
150
|
+
|
|
151
|
+
### TUI control
|
|
152
|
+
|
|
153
|
+
These endpoints drive a running TUI over HTTP.
|
|
154
|
+
|
|
155
|
+
- `POST /tui/append-prompt` - append text to the TUI prompt
|
|
156
|
+
- `POST /tui/submit-prompt` - submit the current TUI prompt
|
|
157
|
+
- `POST /tui/execute-command` - execute a TUI command
|
|
158
|
+
- `POST /tui/show-toast` - show a toast in the TUI
|
|
159
|
+
- `GET /tui/control/next` - get the next TUI control event
|
|
160
|
+
- `POST /tui/control/response` - respond to a TUI control event
|
|
161
|
+
|
|
162
|
+
### PTY
|
|
163
|
+
|
|
164
|
+
- `GET /pty` - list PTY sessions
|
|
165
|
+
- `POST /pty` - create a PTY session
|
|
166
|
+
- `GET /pty/:id` - get PTY session info
|
|
167
|
+
- `DELETE /pty/:id` - delete a PTY session
|
|
168
|
+
- `POST /pty/:id/connect-token` - generate a PTY connect token
|
|
169
|
+
- `GET /pty/:id/connect` - WebSocket connection to the PTY
|
|
170
|
+
|
|
171
|
+
### Event
|
|
172
|
+
|
|
173
|
+
- `GET /event` - instance-level SSE event stream
|
|
174
|
+
|
|
175
|
+
### V2 API
|
|
176
|
+
|
|
177
|
+
- `GET /api/session` - list sessions (v2)
|
|
178
|
+
- `POST /api/session/:id/prompt` - prompt a session (v2)
|
|
179
|
+
- `POST /api/session/:id/compact` - compact a session (v2)
|
|
180
|
+
- `POST /api/session/:id/wait` - wait for a session (v2)
|
|
181
|
+
- `GET /api/session/:id/context` - get session context (v2)
|
|
182
|
+
- `GET /api/session/:id/message` - get session messages (v2)
|
|
183
|
+
- `GET /api/model` - list models (v2)
|
|
184
|
+
- `GET /api/provider` - list providers (v2)
|
|
185
|
+
|
|
186
|
+
## Triggering a prompt over HTTP (for hook and event QA)
|
|
187
|
+
|
|
188
|
+
Use `prompt_async` so the event stream is not blocked.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
curl -X POST -u opencode:$PASS -H 'Content-Type: application/json' \
|
|
192
|
+
-d '{"parts":[{"type":"text","text":"hello"}]}' \
|
|
193
|
+
"http://127.0.0.1:4096/session/<ses_id>/prompt_async?directory=$PWD"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This returns HTTP 204. Watching events is covered in `references/events-hooks.md`.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
Schemas are authoritative in `GET /doc`; for the event stream see `references/events-hooks.md`.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# opencode Test Harness (how opencode QAs itself)
|
|
2
|
+
|
|
3
|
+
This is reference material for writing and running tests against the opencode source. The skill's own QA scripts (CLI, curl, sqlite) do not require this, but it is the authoritative pattern when you need a unit or integration test.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Runner and the root guard](#runner-and-the-root-guard)
|
|
8
|
+
2. [Test bootstrap (in-memory, isolated)](#test-bootstrap-in-memory-isolated)
|
|
9
|
+
3. [Effect-based harness (test/lib/effect.ts)](#effect-based-harness-testlibeffectts)
|
|
10
|
+
4. [Instance and tmpdir fixtures (test/fixture/fixture.ts)](#instance-and-tmpdir-fixtures-testfixturefixturets)
|
|
11
|
+
5. [CLI subprocess harness (test/lib/cli-process.ts)](#cli-subprocess-harness-testlibcli-processts)
|
|
12
|
+
6. [Fake LLM server (test/lib/llm-server.ts)](#fake-llm-server-testlibllm-serverts)
|
|
13
|
+
7. [Representative test shapes](#representative-test-shapes)
|
|
14
|
+
8. [App e2e (Playwright)](#app-e2e-playwright)
|
|
15
|
+
9. [Test style conventions](#test-style-conventions)
|
|
16
|
+
|
|
17
|
+
## Runner and the root guard
|
|
18
|
+
|
|
19
|
+
The runner is `bun test` (Bun built-in, not vitest or jest).
|
|
20
|
+
|
|
21
|
+
Tests cannot run from the repo root. Two guards enforce this:
|
|
22
|
+
|
|
23
|
+
- `bunfig.toml` at repo root sets `root = "./do-not-run-tests-from-root"`
|
|
24
|
+
- Root `package.json` has `"test": "echo 'do not run tests from root' && exit 1"`
|
|
25
|
+
|
|
26
|
+
Run from a package directory instead:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd packages/opencode && bun test --timeout 30000
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run a single file:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bun test test/tool/read.test.ts
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Filter by test name:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bun test --grep "truncates large file"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
CI variant:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bun run test:ci
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Turbo dependency: `opencode#test` depends on `^build`.
|
|
51
|
+
|
|
52
|
+
## Test bootstrap (in-memory, isolated)
|
|
53
|
+
|
|
54
|
+
The preload file is `packages/opencode/test/preload.ts`. It is wired via `packages/opencode/bunfig.toml`:
|
|
55
|
+
|
|
56
|
+
```toml
|
|
57
|
+
[test]
|
|
58
|
+
preload = ["@opentui/solid/preload", "./test/preload.ts"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
What it does:
|
|
62
|
+
|
|
63
|
+
- Sets `XDG_DATA_HOME`, `XDG_CACHE_HOME`, `XDG_CONFIG_HOME`, and `XDG_STATE_HOME` to temp directories
|
|
64
|
+
- Sets `OPENCODE_TEST_HOME`
|
|
65
|
+
- Sets `OPENCODE_DB=":memory:"` (SQLite in-memory)
|
|
66
|
+
- Wipes all provider API keys from `process.env`
|
|
67
|
+
- Sets `OPENCODE_EXPERIMENTAL_EVENT_SYSTEM=true`
|
|
68
|
+
- Sets `OPENCODE_EXPERIMENTAL_WORKSPACES=true`
|
|
69
|
+
- Initializes `Log.init({ print: false })`
|
|
70
|
+
- Calls `initProjectors()`
|
|
71
|
+
|
|
72
|
+
## Effect-based harness (test/lib/effect.ts)
|
|
73
|
+
|
|
74
|
+
The `it` factory wraps `bun:test` with three variants:
|
|
75
|
+
|
|
76
|
+
- `it.effect(name, body)` ... TestClock + TestConsole (isolated time)
|
|
77
|
+
- `it.live(name, body)` ... real clock + TestConsole
|
|
78
|
+
- `it.instance(name, body, opts)` ... real clock + scoped tmpdir + a real Instance context
|
|
79
|
+
|
|
80
|
+
`testEffect(layer)` builds an `it` bound to an Effect layer:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const it = testEffect(Layer.mergeAll(readLayer(), testInstanceStoreLayer))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Instance and tmpdir fixtures (test/fixture/fixture.ts)
|
|
87
|
+
|
|
88
|
+
- `tmpdirScoped(options?)` ... scoped temp directory. Optional `git: true`, optional `config` (writes `opencode.json`), optional `init`.
|
|
89
|
+
- `provideInstance(directory)(effect)` ... runs an Effect inside a real instance for that directory.
|
|
90
|
+
- `withTmpdirInstance({ git?, config?, init? })(effect)` ... one-liner: make tmpdir, optional git init + config, provide instance.
|
|
91
|
+
- `testInstanceStoreLayer` ... instance store with a no-op bootstrap.
|
|
92
|
+
|
|
93
|
+
## CLI subprocess harness (test/lib/cli-process.ts)
|
|
94
|
+
|
|
95
|
+
`cliIt.live(name, body, timeoutMs?)` and `cliIt.concurrent(...)` spawn the real CLI (`bun run --conditions=browser src/index.ts`) in an isolated environment.
|
|
96
|
+
|
|
97
|
+
Exposed helpers:
|
|
98
|
+
|
|
99
|
+
- `opencode.run()`
|
|
100
|
+
- `opencode.serve()`
|
|
101
|
+
- `opencode.acp()`
|
|
102
|
+
- `expectExit`
|
|
103
|
+
- `parseJsonEvents`
|
|
104
|
+
|
|
105
|
+
Isolation environment keys:
|
|
106
|
+
|
|
107
|
+
- `OPENCODE_TEST_HOME`
|
|
108
|
+
- `OPENCODE_CONFIG_CONTENT` (inline provider config)
|
|
109
|
+
- `OPENCODE_DISABLE_PROJECT_CONFIG=1`
|
|
110
|
+
- `OPENCODE_PURE=1`
|
|
111
|
+
- `OPENCODE_DISABLE_AUTOUPDATE=1`
|
|
112
|
+
- `OPENCODE_DISABLE_AUTOCOMPACT=1`
|
|
113
|
+
- `OPENCODE_DISABLE_MODELS_FETCH=1`
|
|
114
|
+
|
|
115
|
+
Real example from `packages/opencode/test/cli/serve/serve-process.test.ts`:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
cliIt.live("spawns serve and health responds", async ({ opencode, expectExit }) => {
|
|
119
|
+
const server = await opencode.serve()
|
|
120
|
+
expect(server.port).toBeGreaterThan(0)
|
|
121
|
+
const res = await fetch(`${server.url}/global/health`)
|
|
122
|
+
expect(res.status).toBe(200)
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Fake LLM server (test/lib/llm-server.ts)
|
|
127
|
+
|
|
128
|
+
`TestLLMServer` is an in-process OpenAI-compatible SSE server to mock model responses deterministically.
|
|
129
|
+
|
|
130
|
+
Methods:
|
|
131
|
+
|
|
132
|
+
- `llm.text("hello")`
|
|
133
|
+
- `llm.tool("read", { filePath: "x" })`
|
|
134
|
+
- `llm.pushMatch(matchFn, reply)`
|
|
135
|
+
|
|
136
|
+
This is how tests avoid real provider calls.
|
|
137
|
+
|
|
138
|
+
## Representative test shapes
|
|
139
|
+
|
|
140
|
+
### 1. Tool test
|
|
141
|
+
|
|
142
|
+
From `packages/opencode/test/tool/read.test.ts`:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const it = testEffect(Layer.mergeAll(readLayer(), testInstanceStoreLayer))
|
|
146
|
+
|
|
147
|
+
it.instance("truncates large file over maxReadFileSize", () =>
|
|
148
|
+
Effect.gen(function* () {
|
|
149
|
+
const test = yield* TestInstance
|
|
150
|
+
// ... exercise the read tool, assert truncation
|
|
151
|
+
})
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 2. Session/event test
|
|
156
|
+
|
|
157
|
+
From `packages/opencode/test/session/session.test.ts`:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
test("session.created fires after session.create", async () => {
|
|
161
|
+
const deferred = Deferred.unsafeMake<void>(FiberId.none)
|
|
162
|
+
// ... listen for session.created event
|
|
163
|
+
await session.create({})
|
|
164
|
+
// ... assert deferred resolves
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Plain unit test
|
|
169
|
+
|
|
170
|
+
From `packages/opencode/test/cli/run/runtime.boot.test.ts`:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { describe, expect, mock, spyOn, test } from "bun:test"
|
|
174
|
+
|
|
175
|
+
test("boots runtime without errors", () => {
|
|
176
|
+
// ... standard assertions, no Effect
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## App e2e (Playwright)
|
|
181
|
+
|
|
182
|
+
The app lives in `packages/app` (SolidJS). Config is `packages/app/playwright.config.ts`. It starts the Vite dev server via `webServer`; the backend is expected at `localhost:4096`.
|
|
183
|
+
|
|
184
|
+
Commands (run from `packages/app`):
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
bunx playwright install chromium
|
|
188
|
+
bun run test:e2e:local
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Filter with grep:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
bun run test:e2e:local -- --grep "settings"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
UI mode:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
bun run test:e2e:ui
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
App unit tests:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
bun run test:unit
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This equals `bun test --preload ./happydom.ts ./src`.
|
|
210
|
+
|
|
211
|
+
## Test style conventions
|
|
212
|
+
|
|
213
|
+
Per opencode AGENTS.md:
|
|
214
|
+
|
|
215
|
+
- Avoid mocks where possible. Test the real implementation. Do not duplicate logic into tests.
|
|
216
|
+
- Run `bun typecheck` from the package directory (uses tsgo). Never run bare `tsc`.
|
|
217
|
+
|
|
218
|
+
For runtime or scriptable QA without writing tests, use the opencode-qa scripts (Cases A-D in SKILL.md).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# QAing the opencode TUI under tmux (Case C)
|
|
2
|
+
|
|
3
|
+
## Verdict first (be honest)
|
|
4
|
+
|
|
5
|
+
- tmux CAN launch the opencode TUI and `capture-pane` reads the rendered frame; `send-keys` delivers keystrokes to the composer. This is proven and good for SMOKE checks: did it boot, does it render, does it accept input.
|
|
6
|
+
- The TUI is a 60fps full-screen app (built on @opentui/solid) with a custom renderer, animations, and a worker thread. Asserting on conversation OUTPUT by scraping the frame is FRAGILE and not recommended.
|
|
7
|
+
- For real behavior assertions prefer: `opencode run` (Case A, references/cli-commands.md), the server API + SSE (Case B, references/server-api.md + events-hooks.md), or the TUI control HTTP API (below). The TUI talks to the same server, so API-level QA is equivalent to driving the screen.
|
|
8
|
+
|
|
9
|
+
## Safety: isolate so QA never pollutes the real DB
|
|
10
|
+
|
|
11
|
+
Launching the real TUI would create sessions in the real ~/.local/share/opencode DB. Run it under an isolated XDG sandbox. The bundled `scripts/tui-smoke.sh` does exactly this and verifies the real session count is unchanged before/after.
|
|
12
|
+
|
|
13
|
+
## Smoke test (bundled)
|
|
14
|
+
|
|
15
|
+
- `scripts/tui-smoke.sh --self-test` launches the TUI under tmux in an isolated sandbox, polls capture-pane for a render marker (version string / "Ask anything" / footer), sends a sentinel keystroke, then kills the tmux session and confirms the real DB is untouched.
|
|
16
|
+
|
|
17
|
+
## Manual tmux recipe (fenced) - for ad hoc smoke
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
SESS=oqa_tui_demo
|
|
21
|
+
DIR=$(mktemp -d)
|
|
22
|
+
tmux new-session -d -s "$SESS" -x 200 -y 50
|
|
23
|
+
# isolate XDG so no real session is written
|
|
24
|
+
tmux send-keys -t "$SESS" "XDG_DATA_HOME=$DIR/data XDG_CONFIG_HOME=$DIR/cfg XDG_STATE_HOME=$DIR/state XDG_CACHE_HOME=$DIR/cache OPENCODE_DISABLE_AUTOUPDATE=1 OPENCODE_DISABLE_MODELS_FETCH=1 opencode $DIR" Enter
|
|
25
|
+
sleep 7
|
|
26
|
+
tmux capture-pane -t "$SESS" -p | sed -n '1,30p' # inspect the rendered frame
|
|
27
|
+
tmux send-keys -t "$SESS" "hello" # type into the composer
|
|
28
|
+
sleep 1
|
|
29
|
+
tmux capture-pane -t "$SESS" -p | sed -n '1,30p'
|
|
30
|
+
tmux kill-session -t "$SESS" # teardown (kills the TUI)
|
|
31
|
+
rm -rf "$DIR"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Explain: capture-pane -p prints the visible frame; send-keys injects input; kill-session tears down the process tree. Always teardown and remove the temp dir.
|
|
35
|
+
|
|
36
|
+
## The reliable alternative: TUI control HTTP API
|
|
37
|
+
|
|
38
|
+
A running TUI is a client of the local server, so you can drive it over HTTP without scraping the screen:
|
|
39
|
+
|
|
40
|
+
- POST /tui/append-prompt - append text to the composer
|
|
41
|
+
- POST /tui/submit-prompt - submit the composer
|
|
42
|
+
- POST /tui/execute-command - run a TUI command
|
|
43
|
+
- POST /tui/show-toast - show a toast
|
|
44
|
+
- GET /tui/control/next + POST /tui/control/response - the control channel
|
|
45
|
+
|
|
46
|
+
Use these (with auth + ?directory=) to deterministically drive a TUI you launched, then assert via the event stream (references/events-hooks.md).
|
|
47
|
+
|
|
48
|
+
## Headless component testing (for source-level TUI tests)
|
|
49
|
+
|
|
50
|
+
opencode unit-tests TUI components headlessly with @opentui/core/testing `createTestRenderer` (see packages/opencode/test/cli/tui/, e.g. app-lifecycle.test.ts). This is the route for asserting TUI component behavior in the source repo; see references/testing-harness.md.
|
|
51
|
+
|
|
52
|
+
Bottom line: tmux for smoke, server/SSE or /tui/* control for assertions, createTestRenderer for source unit tests.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# db-session-by-id.sh - investigate an opencode session by its id (ses_...).
|
|
3
|
+
# Read-only against the LIVE opencode DB via `opencode db ... --format json`.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# db-session-by-id.sh ses_3a4ee6335ffedFB8f76BPU1Eb3
|
|
7
|
+
# db-session-by-id.sh --self-test
|
|
8
|
+
#
|
|
9
|
+
# Output: a JSON array with one row (id, slug, title, directory, agent, model,
|
|
10
|
+
# cost, token counts, human-readable created/updated times) or [] if not found.
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
. "$SCRIPT_DIR/lib/common.sh"
|
|
14
|
+
|
|
15
|
+
oqa_session_by_id() {
|
|
16
|
+
local id esc
|
|
17
|
+
id="$1"
|
|
18
|
+
esc="$(oqa_sql_escape "$id")"
|
|
19
|
+
oqa_db_query "SELECT
|
|
20
|
+
id, slug, title, directory, agent,
|
|
21
|
+
json_extract(model,'\$.modelID') AS model,
|
|
22
|
+
json_extract(model,'\$.providerID') AS provider,
|
|
23
|
+
cost, tokens_input, tokens_output,
|
|
24
|
+
datetime(time_created/1000,'unixepoch') AS created,
|
|
25
|
+
datetime(time_updated/1000,'unixepoch') AS updated
|
|
26
|
+
FROM session WHERE id='$esc'"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
oqa_self_test() {
|
|
30
|
+
oqa_require opencode jq || return 1
|
|
31
|
+
local id out got
|
|
32
|
+
id="$(oqa_db_query "SELECT id FROM session ORDER BY time_created DESC LIMIT 1" | jq -r '.[0].id // empty')"
|
|
33
|
+
if [ -z "$id" ]; then
|
|
34
|
+
oqa_log "FAIL: no sessions in DB to test against"; return 1
|
|
35
|
+
fi
|
|
36
|
+
out="$(oqa_session_by_id "$id")"
|
|
37
|
+
got="$(printf '%s' "$out" | jq -r '.[0].id // empty')"
|
|
38
|
+
if [ "$got" = "$id" ]; then
|
|
39
|
+
oqa_pass "db-session-by-id round-trips id ($id)"
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
oqa_log "FAIL: expected id '$id', got '$got'"; return 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case "${1:-}" in
|
|
46
|
+
--self-test) oqa_self_test; exit $? ;;
|
|
47
|
+
-h|--help|"")
|
|
48
|
+
sed -n '2,12p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
49
|
+
[ -z "${1:-}" ] && exit 2 || exit 0 ;;
|
|
50
|
+
*)
|
|
51
|
+
oqa_require opencode jq || exit 1
|
|
52
|
+
oqa_session_by_id "$1" ;;
|
|
53
|
+
esac
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# db-session-by-name.sh - find opencode sessions whose TITLE matches a substring.
|
|
3
|
+
# Read-only against the LIVE opencode DB. Title search is cheap (the session
|
|
4
|
+
# table is small; ~21k rows scan in milliseconds), so no bounding is needed.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# db-session-by-name.sh "auth refactor" # newest 20 matches
|
|
8
|
+
# db-session-by-name.sh "auth refactor" 50 # newest 50 matches
|
|
9
|
+
# db-session-by-name.sh --self-test
|
|
10
|
+
#
|
|
11
|
+
# Output: JSON array of {id, title, created, updated} ordered newest first.
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
. "$SCRIPT_DIR/lib/common.sh"
|
|
15
|
+
|
|
16
|
+
oqa_session_by_name() {
|
|
17
|
+
local needle limit esc
|
|
18
|
+
needle="$1"
|
|
19
|
+
limit="${2:-20}"
|
|
20
|
+
case "$limit" in (*[!0-9]*|"") limit=20 ;; esac
|
|
21
|
+
esc="$(oqa_sql_escape "$needle")"
|
|
22
|
+
oqa_db_query "SELECT
|
|
23
|
+
id, title,
|
|
24
|
+
datetime(time_created/1000,'unixepoch') AS created,
|
|
25
|
+
datetime(time_updated/1000,'unixepoch') AS updated
|
|
26
|
+
FROM session
|
|
27
|
+
WHERE title LIKE '%$esc%'
|
|
28
|
+
ORDER BY time_created DESC
|
|
29
|
+
LIMIT $limit"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
oqa_self_test() {
|
|
33
|
+
oqa_require opencode jq || return 1
|
|
34
|
+
# Derive a guaranteed-present needle: the first 5 chars of a real title.
|
|
35
|
+
local needle out n
|
|
36
|
+
needle="$(oqa_db_query "SELECT substr(title,1,5) AS t FROM session WHERE length(title)>=5 ORDER BY time_created DESC LIMIT 1" | jq -r '.[0].t // empty')"
|
|
37
|
+
if [ -z "$needle" ]; then
|
|
38
|
+
oqa_log "FAIL: could not derive a title needle"; return 1
|
|
39
|
+
fi
|
|
40
|
+
out="$(oqa_session_by_name "$needle" 5)"
|
|
41
|
+
n="$(printf '%s' "$out" | jq 'length')"
|
|
42
|
+
if [ "${n:-0}" -ge 1 ]; then
|
|
43
|
+
oqa_pass "db-session-by-name found $n row(s) for needle '$needle'"
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
oqa_log "FAIL: expected >=1 row for needle '$needle', got '$n'"; return 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
case "${1:-}" in
|
|
50
|
+
--self-test) oqa_self_test; exit $? ;;
|
|
51
|
+
-h|--help|"")
|
|
52
|
+
sed -n '2,14p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
53
|
+
[ -z "${1:-}" ] && exit 2 || exit 0 ;;
|
|
54
|
+
*)
|
|
55
|
+
oqa_require opencode jq || exit 1
|
|
56
|
+
oqa_session_by_name "$1" "${2:-20}" ;;
|
|
57
|
+
esac
|