11import type { Argv } from "yargs"
22import { spawn } from "child_process"
3- import { Database } from "@/storage/db"
4- import { drizzle } from "drizzle-orm/bun-sqlite"
5- import { Database as BunDatabase } from "bun:sqlite"
6- import { UI } from "../ui"
7- import { cmd } from "./cmd"
8- import { JsonMigration } from "@/storage/json-migration"
9- import { EOL } from "os"
10- import { errorMessage } from "../../util/error"
3+ import { Database } from "@opencode-ai/core/database/database"
4+ import { Effect } from "effect"
5+ import { sql } from "drizzle-orm"
6+ import { effectCmd } from "../effect-cmd"
117
12- const QueryCommand = cmd ( {
8+ const QueryCommand = effectCmd ( {
139 command : "$0 [query]" ,
1410 describe : "open an interactive sqlite3 shell or run a query" ,
11+ instance : false ,
1512 builder : ( yargs : Argv ) => {
1613 return yargs
1714 . positional ( "query" , {
@@ -25,96 +22,41 @@ const QueryCommand = cmd({
2522 describe : "Output format" ,
2623 } )
2724 } ,
28- handler : async ( args : { query ?: string ; format : string } ) => {
25+ handler : Effect . fn ( "Cli.db.query" ) ( function * ( args : { query ?: string ; format : string } ) {
2926 const query = args . query as string | undefined
3027 if ( query ) {
31- const db = new BunDatabase ( Database . getPath ( ) , { readonly : true } )
32- try {
33- const result = db . query ( query ) . all ( ) as Record < string , unknown > [ ]
34- if ( args . format === "json" ) {
35- console . log ( JSON . stringify ( result , null , 2 ) )
36- } else if ( result . length > 0 ) {
37- const keys = Object . keys ( result [ 0 ] )
38- console . log ( keys . join ( "\t" ) )
39- for ( const row of result ) {
40- console . log ( keys . map ( ( k ) => row [ k ] ) . join ( "\t" ) )
41- }
42- }
43- } catch ( err ) {
44- UI . error ( errorMessage ( err ) )
45- process . exit ( 1 )
28+ const { db } = yield * Database . Service
29+ const result = yield * db . all < Record < string , unknown > > ( sql . raw ( query ) ) . pipe ( Effect . orDie )
30+ if ( args . format === "json" ) console . log ( JSON . stringify ( result , null , 2 ) )
31+ else if ( result . length > 0 ) {
32+ const keys = Object . keys ( result [ 0 ] )
33+ console . log ( keys . join ( "\t" ) )
34+ for ( const row of result ) console . log ( keys . map ( ( key ) => row [ key ] ) . join ( "\t" ) )
4635 }
47- db . close ( )
4836 return
4937 }
50- const child = spawn ( "sqlite3" , [ Database . getPath ( ) ] , {
38+ const child = spawn ( "sqlite3" , [ Database . path ( ) ] , {
5139 stdio : "inherit" ,
5240 } )
53- await new Promise ( ( resolve ) => child . on ( "close" , resolve ) )
54- } ,
41+ yield * Effect . promise ( ( ) => new Promise ( ( resolve ) => child . on ( "close" , resolve ) ) )
42+ } ) ,
5543} )
5644
57- const PathCommand = cmd ( {
45+ const PathCommand = effectCmd ( {
5846 command : "path" ,
5947 describe : "print the database path" ,
60- handler : ( ) => {
61- console . log ( Database . getPath ( ) )
62- } ,
63- } )
64-
65- const MigrateCommand = cmd ( {
66- command : "migrate" ,
67- describe : "migrate JSON data to SQLite (merges with existing data)" ,
68- handler : async ( ) => {
69- const sqlite = new BunDatabase ( Database . getPath ( ) )
70- const tty = process . stderr . isTTY
71- const width = 36
72- const orange = "\x1b[38;5;214m"
73- const muted = "\x1b[0;2m"
74- const reset = "\x1b[0m"
75- let last = - 1
76- if ( tty ) process . stderr . write ( "\x1b[?25l" )
77- try {
78- const stats = await JsonMigration . run ( drizzle ( { client : sqlite } ) , {
79- progress : ( event ) => {
80- const percent = Math . floor ( ( event . current / event . total ) * 100 )
81- if ( percent === last ) return
82- last = percent
83- if ( tty ) {
84- const fill = Math . round ( ( percent / 100 ) * width )
85- const bar = `${ "■" . repeat ( fill ) } ${ "・" . repeat ( width - fill ) } `
86- process . stderr . write (
87- `\r${ orange } ${ bar } ${ percent . toString ( ) . padStart ( 3 ) } %${ reset } ${ muted } ${ event . current } /${ event . total } ${ reset } ` ,
88- )
89- } else {
90- process . stderr . write ( `sqlite-migration:${ percent } ${ EOL } ` )
91- }
92- } ,
93- } )
94- if ( tty ) process . stderr . write ( "\n" )
95- if ( tty ) process . stderr . write ( "\x1b[?25h" )
96- else process . stderr . write ( `sqlite-migration:done${ EOL } ` )
97- UI . println (
98- `Migration complete: ${ stats . projects } projects, ${ stats . sessions } sessions, ${ stats . messages } messages` ,
99- )
100- if ( stats . errors . length > 0 ) {
101- UI . println ( `${ stats . errors . length } errors occurred during migration` )
102- }
103- } catch ( err ) {
104- if ( tty ) process . stderr . write ( "\x1b[?25h" )
105- UI . error ( `Migration failed: ${ errorMessage ( err ) } ` )
106- process . exit ( 1 )
107- } finally {
108- sqlite . close ( )
109- }
110- } ,
48+ instance : false ,
49+ handler : Effect . fn ( "Cli.db.path" ) ( function * ( ) {
50+ console . log ( Database . path ( ) )
51+ } ) ,
11152} )
11253
113- export const DbCommand = cmd ( {
54+ export const DbCommand = effectCmd ( {
11455 command : "db" ,
11556 describe : "database tools" ,
57+ instance : false ,
11658 builder : ( yargs : Argv ) => {
117- return yargs . command ( QueryCommand ) . command ( PathCommand ) . command ( MigrateCommand ) . demandCommand ( )
59+ return yargs . command ( QueryCommand ) . command ( PathCommand ) . demandCommand ( )
11860 } ,
119- handler : ( ) => { } ,
61+ handler : Effect . fn ( "Cli.db" ) ( function * ( ) { } ) ,
12062} )
0 commit comments