Răsfoiți Sursa

feat(cli): add db migrate command for JSON to SQLite migration (#13874)

Dax 1 lună în urmă
părinte
comite
fdad823edc
1 a modificat fișierele cu 51 adăugiri și 1 ștergeri
  1. 51 1
      packages/opencode/src/cli/cmd/db.ts

+ 51 - 1
packages/opencode/src/cli/cmd/db.ts

@@ -4,6 +4,8 @@ import { Database } from "../../storage/db"
 import { Database as BunDatabase } from "bun:sqlite"
 import { Database as BunDatabase } from "bun:sqlite"
 import { UI } from "../ui"
 import { UI } from "../ui"
 import { cmd } from "./cmd"
 import { cmd } from "./cmd"
+import { JsonMigration } from "../../storage/json-migration"
+import { EOL } from "os"
 
 
 const QueryCommand = cmd({
 const QueryCommand = cmd({
   command: "$0 [query]",
   command: "$0 [query]",
@@ -58,11 +60,59 @@ const PathCommand = cmd({
   },
   },
 })
 })
 
 
+const MigrateCommand = cmd({
+  command: "migrate",
+  describe: "migrate JSON data to SQLite (merges with existing data)",
+  handler: async () => {
+    const sqlite = new BunDatabase(Database.Path)
+    const tty = process.stderr.isTTY
+    const width = 36
+    const orange = "\x1b[38;5;214m"
+    const muted = "\x1b[0;2m"
+    const reset = "\x1b[0m"
+    let last = -1
+    if (tty) process.stderr.write("\x1b[?25l")
+    try {
+      const stats = await JsonMigration.run(sqlite, {
+        progress: (event) => {
+          const percent = Math.floor((event.current / event.total) * 100)
+          if (percent === last) return
+          last = percent
+          if (tty) {
+            const fill = Math.round((percent / 100) * width)
+            const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}`
+            process.stderr.write(
+              `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.current}/${event.total}${reset} `,
+            )
+          } else {
+            process.stderr.write(`sqlite-migration:${percent}${EOL}`)
+          }
+        },
+      })
+      if (tty) process.stderr.write("\n")
+      if (tty) process.stderr.write("\x1b[?25h")
+      else process.stderr.write(`sqlite-migration:done${EOL}`)
+      UI.println(
+        `Migration complete: ${stats.projects} projects, ${stats.sessions} sessions, ${stats.messages} messages`,
+      )
+      if (stats.errors.length > 0) {
+        UI.println(`${stats.errors.length} errors occurred during migration`)
+      }
+    } catch (err) {
+      if (tty) process.stderr.write("\x1b[?25h")
+      UI.error(`Migration failed: ${err instanceof Error ? err.message : String(err)}`)
+      process.exit(1)
+    } finally {
+      sqlite.close()
+    }
+  },
+})
+
 export const DbCommand = cmd({
 export const DbCommand = cmd({
   command: "db",
   command: "db",
   describe: "database tools",
   describe: "database tools",
   builder: (yargs: Argv) => {
   builder: (yargs: Argv) => {
-    return yargs.command(QueryCommand).command(PathCommand).demandCommand()
+    return yargs.command(QueryCommand).command(PathCommand).command(MigrateCommand).demandCommand()
   },
   },
   handler: () => {},
   handler: () => {},
 })
 })