Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
36160ef
feat(core): add sqlite schema sync
thdxr May 21, 2026
eaf5697
progress
thdxr May 22, 2026
0cab11f
refactor(core): move database schema ownership
thdxr May 24, 2026
977f48a
Merge remote-tracking branch 'origin/dev' into refactor/core-database…
thdxr May 24, 2026
46422a4
core: support native node sqlite database runtime
thdxr May 24, 2026
31d8959
core: isolate native node sqlite provider
thdxr May 24, 2026
077c91b
core: avoid session schema import cycle
thdxr May 24, 2026
4695346
refactor(core): centralize project and workspace schemas
thdxr May 24, 2026
aa8ad49
refactor(core): centralize legacy session schemas
thdxr May 24, 2026
9b812d9
fix(core): migrate sync database clients
thdxr May 24, 2026
9ded717
refactor(core): unify sqlite database clients
thdxr May 24, 2026
b18ff8f
progress
thdxr May 24, 2026
dedcb9b
refactor(core): project session events in core
thdxr May 25, 2026
05dca9f
feat(core): persist sync event projections
thdxr May 25, 2026
06bd34c
feat(core): add sync event replay controls
thdxr May 25, 2026
13e9e31
chore: merge dev into core database branch
thdxr May 25, 2026
9440f7f
test(core): cover sync event replay controls
thdxr May 25, 2026
7180c3a
fix(core): add session list cursor schema
thdxr May 25, 2026
dcbe09b
refactor(core): mark session methods unimplemented
thdxr May 25, 2026
b819034
fix(opencode): align v2 session endpoint errors
thdxr May 25, 2026
dad0f57
refactor(opencode): remove storage db from domain services
thdxr May 25, 2026
08c17e8
refactor(opencode): move session reads to core database
thdxr May 25, 2026
087d356
chore(core): format event service
thdxr May 25, 2026
b97d254
refactor(opencode): migrate session events to core
thdxr May 25, 2026
c3743b2
fix(opencode): preserve session event routing types
thdxr May 25, 2026
876ce31
refactor(opencode): remove legacy database wrapper
thdxr May 25, 2026
7c14627
chore: merge dev into database wrapper branch
thdxr May 25, 2026
d0dae93
sync
thdxr May 26, 2026
0364de0
refactor(opencode): replace instance bus with event v2
thdxr May 26, 2026
b5912f5
chore: merge dev into event v2 branch
thdxr May 26, 2026
9b5fbbe
fix(opencode): share httpapi exercise service graph
thdxr May 26, 2026
3077ff3
fix(opencode): dispose booted worktrees before removal
thdxr May 26, 2026
714cbe6
fix(opencode): preserve worktree path when disposing instances
thdxr May 26, 2026
c1e744f
fix(core): include domain schemas in migrations
thdxr May 26, 2026
50f6df3
test(core): check migration generation is clean
thdxr May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .opencode/opencode.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"$schema": "https://opencode.ai/config.json",
"provider": {},
"permission": {},
"reference": {
"effect": "github.com/Effect-TS/effect-smol",
},
"mcp": {},
"tools": {
"github-triage": false,
Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ obj.b
const { a, b } = obj
```

### Imports

- Never alias imports. Do not use `import { foo as bar } from "..."` or renamed imports like `resolve as pathResolve`.
- Never use star imports. Do not use `import * as Foo from "..."` or `import type * as Foo from "..."`.
- If a namespace-style value is needed, import the module's own exported namespace by name, for example `import { Project } from "@opencode-ai/core/project"`, then reference `Project.ID`.

### Variables

Prefer `const` over `let`. Use ternaries or early returns instead of reassignment.
Expand Down
20 changes: 19 additions & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineConfig } from "drizzle-kit"

export default defineConfig({
dialect: "sqlite",
schema: "./src/**/*.sql.ts",
schema: ["./src/**/*.sql.ts", "./src/**/sql.ts"],
out: "./migration",
dbCredentials: {
url: "/home/thdxr/.local/share/opencode/opencode.db",
Expand Down
17 changes: 15 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"license": "MIT",
"private": true,
"scripts": {
"db": "bun drizzle-kit",
"migration": "bun run script/migration.ts",
"test": "bun test",
"test:ci": "mkdir -p .artifacts/unit && bun test --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml",
"typecheck": "tsgo --noEmit"
Expand All @@ -16,14 +18,21 @@
"exports": {
"./*": "./src/*.ts"
},
"imports": {},
"imports": {
"#sqlite": {
"bun": "./src/database/sqlite.bun.ts",
"node": "./src/database/sqlite.node.ts",
"default": "./src/database/sqlite.bun.ts"
}
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/cross-spawn": "catalog:",
"@types/npm-package-arg": "6.1.4",
"@types/npmcli__arborist": "6.3.3",
"@types/semver": "catalog:"
"@types/semver": "catalog:",
"drizzle-kit": "catalog:"
},
"dependencies": {
"@ai-sdk/alibaba": "1.0.17",
Expand All @@ -49,15 +58,19 @@
"@aws-sdk/credential-providers": "3.993.0",
"@effect/opentelemetry": "catalog:",
"@effect/platform-node": "catalog:",
"@effect/sql-sqlite-bun": "catalog:",
"@npmcli/arborist": "9.4.0",
"@npmcli/config": "10.8.1",
"@opencode-ai/effect-drizzle-sqlite": "workspace:*",
"@opencode-ai/effect-sqlite-node": "workspace:*",
"@opentelemetry/api": "1.9.0",
"@opentelemetry/context-async-hooks": "2.6.1",
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
"@opentelemetry/sdk-trace-base": "2.6.1",
"@openrouter/ai-sdk-provider": "2.8.1",
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"drizzle-orm": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
Expand Down
113 changes: 113 additions & 0 deletions packages/core/script/migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bun

import { $ } from "bun"
import fs from "fs/promises"
import os from "os"
import path from "path"
import { pathToFileURL } from "url"

const root = path.resolve(import.meta.dirname, "../../..")
const sqlDir = path.join(root, "packages/core/migration")
const tsDir = path.join(root, "packages/core/src/database/migration")
const registry = path.join(root, "packages/core/src/database/migration.gen.ts")

if (Bun.argv.includes("--check")) {
await check()
process.exit(0)
}

await $`bun drizzle-kit generate`.cwd(path.join(root, "packages/core"))

const sqlMigrations = (await Array.fromAsync(new Bun.Glob("*/migration.sql").scan({ cwd: sqlDir })))
.map((file) => file.split("/")[0])
.filter((name) => name !== undefined)
.sort()

for (const name of sqlMigrations) {
if (await Bun.file(path.join(tsDir, `${name}.ts`)).exists()) continue
await Bun.write(path.join(tsDir, `${name}.ts`), renderMigration(name, await Bun.file(path.join(sqlDir, name, "migration.sql")).text()))
}

await Bun.write(registry, renderRegistry(sqlMigrations))

async function check() {
const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-core-migration-check-"))
const output = path.join(temporary, "migration")
try {
await fs.cp(sqlDir, output, { recursive: true })
const config = path.join(temporary, "drizzle.config.ts")
await Bun.write(
config,
`import config from ${JSON.stringify(pathToFileURL(path.join(root, "packages/core/drizzle.config.ts")).href)}

export default { ...config, out: ${JSON.stringify(output)} }
`,
)
const before = await snapshot(output)
await $`bun drizzle-kit generate --config ${config}`.cwd(path.join(root, "packages/core"))
const after = await snapshot(output)
if (JSON.stringify(after) !== JSON.stringify(before)) {
throw new Error("Core schema has ungenerated database migrations. Run `bun script/migration.ts` from packages/core.")
}

const migrations = before
.map((entry) => entry.path.split("/")[0])
.filter((name, index, all) => name !== undefined && all.indexOf(name) === index)
.sort()
for (const name of migrations) {
if (await Bun.file(path.join(tsDir, `${name}.ts`)).exists()) continue
throw new Error(`Database migration TypeScript wrapper is missing for ${name}. Run \`bun script/migration.ts\` from packages/core.`)
}
if ((await Bun.file(registry).text()) !== renderRegistry(migrations)) {
throw new Error("Database migration registry is stale. Run `bun script/migration.ts` from packages/core.")
}
} finally {
await fs.rm(temporary, { recursive: true, force: true })
}
}

async function snapshot(directory: string) {
const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: directory, onlyFiles: true }))
return Promise.all(
files.sort().map(async (file) => ({ path: file, contents: await Bun.file(path.join(directory, file)).text() })),
)
}

function renderMigration(name: string, sql: string) {
return `import { Effect } from "effect"
import type { DatabaseMigration } from "../migration"

export default {
id: ${JSON.stringify(name)},
up(tx) {
return Effect.gen(function* () {
${sql
.split("--> statement-breakpoint")
.map((statement) => statement.trim())
.filter((statement) => statement.length > 0)
.map(renderRun)
.join("\n")}
})
},
} satisfies DatabaseMigration.Migration
`
}

function renderRun(statement: string) {
const lines = statement.replaceAll("\t", " ").split("\n")
if (lines.length === 1) return ` yield* tx.run(\`${escapeTemplate(lines[0])}\`)`
return ` yield* tx.run(\`\n${lines.map((line) => ` ${escapeTemplate(line)}`).join("\n")}\n \`)`
}

function escapeTemplate(line: string) {
return line.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("${", "\\${")
}

function renderRegistry(names: string[]) {
return `import type { DatabaseMigration } from "./migration"

export const migrations = (await Promise.all([
${names.map((name) => ` import("./migration/${name}"),`).join("\n")}
])).map((module) => module.default) satisfies DatabaseMigration.Migration[]
`
}
Loading
Loading