refactor(cli): convert stats command to effectCmd (#25474)
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
import type { Argv } from "yargs"
|
import { Effect } from "effect"
|
||||||
import { cmd } from "./cmd"
|
import { effectCmd } from "../effect-cmd"
|
||||||
import { Session } from "@/session/session"
|
import { Session } from "@/session/session"
|
||||||
import { bootstrap } from "../bootstrap"
|
|
||||||
import { Database } from "@/storage/db"
|
import { Database } from "@/storage/db"
|
||||||
import { SessionTable } from "../../session/session.sql"
|
import { SessionTable } from "../../session/session.sql"
|
||||||
import { Project } from "@/project/project"
|
import { Project } from "@/project/project"
|
||||||
import { Instance } from "../../project/instance"
|
import { InstanceRef } from "@/effect/instance-ref"
|
||||||
|
import { InstanceStore } from "@/project/instance-store"
|
||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
|
||||||
interface SessionStats {
|
interface SessionStats {
|
||||||
@@ -47,11 +47,11 @@ interface SessionStats {
|
|||||||
medianTokensPerSession: number
|
medianTokensPerSession: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StatsCommand = cmd({
|
export const StatsCommand = effectCmd({
|
||||||
command: "stats",
|
command: "stats",
|
||||||
describe: "show token usage and cost statistics",
|
describe: "show token usage and cost statistics",
|
||||||
builder: (yargs: Argv) => {
|
builder: (yargs) =>
|
||||||
return yargs
|
yargs
|
||||||
.option("days", {
|
.option("days", {
|
||||||
describe: "show stats for the last N days (default: all time)",
|
describe: "show stats for the last N days (default: all time)",
|
||||||
type: "number",
|
type: "number",
|
||||||
@@ -66,34 +66,39 @@ export const StatsCommand = cmd({
|
|||||||
.option("project", {
|
.option("project", {
|
||||||
describe: "filter by project (default: all projects, empty string: current project)",
|
describe: "filter by project (default: all projects, empty string: current project)",
|
||||||
type: "string",
|
type: "string",
|
||||||
})
|
}),
|
||||||
},
|
handler: Effect.fn("Cli.stats")(function* (args) {
|
||||||
handler: async (args) => {
|
const ctx = yield* InstanceRef
|
||||||
await bootstrap(process.cwd(), async () => {
|
if (!ctx) return
|
||||||
const stats = await aggregateSessionStats(args.days, args.project)
|
const store = yield* InstanceStore.Service
|
||||||
|
return yield* run(args, ctx.project).pipe(Effect.ensuring(store.dispose(ctx)))
|
||||||
let modelLimit: number | undefined
|
}),
|
||||||
if (args.models === true) {
|
|
||||||
modelLimit = Infinity
|
|
||||||
} else if (typeof args.models === "number") {
|
|
||||||
modelLimit = args.models
|
|
||||||
}
|
|
||||||
|
|
||||||
displayStats(stats, args.tools, modelLimit)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getCurrentProject(): Promise<Project.Info> {
|
const run = (args: { days?: number; tools?: number; models?: unknown; project?: string }, currentProject: Project.Info) =>
|
||||||
return Instance.project
|
Effect.promise(async () => {
|
||||||
}
|
const stats = await aggregateSessionStats(args.days, args.project, currentProject)
|
||||||
|
|
||||||
|
let modelLimit: number | undefined
|
||||||
|
if (args.models === true) {
|
||||||
|
modelLimit = Infinity
|
||||||
|
} else if (typeof args.models === "number") {
|
||||||
|
modelLimit = args.models
|
||||||
|
}
|
||||||
|
|
||||||
|
displayStats(stats, args.tools, modelLimit)
|
||||||
|
})
|
||||||
|
|
||||||
async function getAllSessions(): Promise<Session.Info[]> {
|
async function getAllSessions(): Promise<Session.Info[]> {
|
||||||
const rows = Database.use((db) => db.select().from(SessionTable).all())
|
const rows = Database.use((db) => db.select().from(SessionTable).all())
|
||||||
return rows.map((row) => Session.fromRow(row))
|
return rows.map((row) => Session.fromRow(row))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function aggregateSessionStats(days?: number, projectFilter?: string): Promise<SessionStats> {
|
export async function aggregateSessionStats(
|
||||||
|
days?: number,
|
||||||
|
projectFilter?: string,
|
||||||
|
currentProject?: Project.Info,
|
||||||
|
): Promise<SessionStats> {
|
||||||
const sessions = await getAllSessions()
|
const sessions = await getAllSessions()
|
||||||
const MS_IN_DAY = 24 * 60 * 60 * 1000
|
const MS_IN_DAY = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
@@ -117,7 +122,7 @@ export async function aggregateSessionStats(days?: number, projectFilter?: strin
|
|||||||
|
|
||||||
if (projectFilter !== undefined) {
|
if (projectFilter !== undefined) {
|
||||||
if (projectFilter === "") {
|
if (projectFilter === "") {
|
||||||
const currentProject = await getCurrentProject()
|
if (!currentProject) throw new Error("currentProject required when projectFilter is empty string")
|
||||||
filteredSessions = filteredSessions.filter((session) => session.projectID === currentProject.id)
|
filteredSessions = filteredSessions.filter((session) => session.projectID === currentProject.id)
|
||||||
} else {
|
} else {
|
||||||
filteredSessions = filteredSessions.filter((session) => session.projectID === projectFilter)
|
filteredSessions = filteredSessions.filter((session) => session.projectID === projectFilter)
|
||||||
|
|||||||
Reference in New Issue
Block a user