Skip to content

Commit c1686c6

Browse files
authored
refactor(cli): convert stats command to effectCmd (#25474)
1 parent 79b6ce5 commit c1686c6

1 file changed

Lines changed: 33 additions & 28 deletions

File tree

packages/opencode/src/cli/cmd/stats.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { Argv } from "yargs"
2-
import { cmd } from "./cmd"
1+
import { Effect } from "effect"
2+
import { effectCmd } from "../effect-cmd"
33
import { Session } from "@/session/session"
4-
import { bootstrap } from "../bootstrap"
54
import { Database } from "@/storage/db"
65
import { SessionTable } from "../../session/session.sql"
76
import { Project } from "@/project/project"
8-
import { Instance } from "../../project/instance"
7+
import { InstanceRef } from "@/effect/instance-ref"
8+
import { InstanceStore } from "@/project/instance-store"
99
import { AppRuntime } from "@/effect/app-runtime"
1010

1111
interface SessionStats {
@@ -47,11 +47,11 @@ interface SessionStats {
4747
medianTokensPerSession: number
4848
}
4949

50-
export const StatsCommand = cmd({
50+
export const StatsCommand = effectCmd({
5151
command: "stats",
5252
describe: "show token usage and cost statistics",
53-
builder: (yargs: Argv) => {
54-
return yargs
53+
builder: (yargs) =>
54+
yargs
5555
.option("days", {
5656
describe: "show stats for the last N days (default: all time)",
5757
type: "number",
@@ -66,34 +66,39 @@ export const StatsCommand = cmd({
6666
.option("project", {
6767
describe: "filter by project (default: all projects, empty string: current project)",
6868
type: "string",
69-
})
70-
},
71-
handler: async (args) => {
72-
await bootstrap(process.cwd(), async () => {
73-
const stats = await aggregateSessionStats(args.days, args.project)
74-
75-
let modelLimit: number | undefined
76-
if (args.models === true) {
77-
modelLimit = Infinity
78-
} else if (typeof args.models === "number") {
79-
modelLimit = args.models
80-
}
81-
82-
displayStats(stats, args.tools, modelLimit)
83-
})
84-
},
69+
}),
70+
handler: Effect.fn("Cli.stats")(function* (args) {
71+
const ctx = yield* InstanceRef
72+
if (!ctx) return
73+
const store = yield* InstanceStore.Service
74+
return yield* run(args, ctx.project).pipe(Effect.ensuring(store.dispose(ctx)))
75+
}),
8576
})
8677

87-
async function getCurrentProject(): Promise<Project.Info> {
88-
return Instance.project
89-
}
78+
const run = (args: { days?: number; tools?: number; models?: unknown; project?: string }, currentProject: Project.Info) =>
79+
Effect.promise(async () => {
80+
const stats = await aggregateSessionStats(args.days, args.project, currentProject)
81+
82+
let modelLimit: number | undefined
83+
if (args.models === true) {
84+
modelLimit = Infinity
85+
} else if (typeof args.models === "number") {
86+
modelLimit = args.models
87+
}
88+
89+
displayStats(stats, args.tools, modelLimit)
90+
})
9091

9192
async function getAllSessions(): Promise<Session.Info[]> {
9293
const rows = Database.use((db) => db.select().from(SessionTable).all())
9394
return rows.map((row) => Session.fromRow(row))
9495
}
9596

96-
export async function aggregateSessionStats(days?: number, projectFilter?: string): Promise<SessionStats> {
97+
export async function aggregateSessionStats(
98+
days?: number,
99+
projectFilter?: string,
100+
currentProject?: Project.Info,
101+
): Promise<SessionStats> {
97102
const sessions = await getAllSessions()
98103
const MS_IN_DAY = 24 * 60 * 60 * 1000
99104

@@ -117,7 +122,7 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
117122

118123
if (projectFilter !== undefined) {
119124
if (projectFilter === "") {
120-
const currentProject = await getCurrentProject()
125+
if (!currentProject) throw new Error("currentProject required when projectFilter is empty string")
121126
filteredSessions = filteredSessions.filter((session) => session.projectID === currentProject.id)
122127
} else {
123128
filteredSessions = filteredSessions.filter((session) => session.projectID === projectFilter)

0 commit comments

Comments
 (0)