refactor(cli): convert session subcommands to effectCmd (#25483)

This commit is contained in:
Kit Langton
2026-05-02 18:15:28 -04:00
committed by GitHub
parent dfe1325fca
commit 1986a6e817
+48 -52
View File
@@ -1,8 +1,9 @@
import type { Argv } from "yargs" import type { Argv } from "yargs"
import { Effect } from "effect"
import { cmd } from "./cmd" import { cmd } from "./cmd"
import { effectCmd, fail } from "../effect-cmd"
import { Session } from "@/session/session" import { Session } from "@/session/session"
import { SessionID } from "../../session/schema" import { SessionID } from "../../session/schema"
import { bootstrap } from "../bootstrap"
import { UI } from "../ui" import { UI } from "../ui"
import { Locale } from "@/util/locale" import { Locale } from "@/util/locale"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
@@ -11,7 +12,8 @@ import { Process } from "@/util/process"
import { EOL } from "os" import { EOL } from "os"
import path from "path" import path from "path"
import { which } from "../../util/which" import { which } from "../../util/which"
import { AppRuntime } from "@/effect/app-runtime" import { InstanceRef } from "@/effect/instance-ref"
import { InstanceStore } from "@/project/instance-store"
function pagerCmd(): string[] { function pagerCmd(): string[] {
const lessOptions = ["-R", "-S"] const lessOptions = ["-R", "-S"]
@@ -47,36 +49,35 @@ export const SessionCommand = cmd({
async handler() {}, async handler() {},
}) })
export const SessionDeleteCommand = cmd({ export const SessionDeleteCommand = effectCmd({
command: "delete <sessionID>", command: "delete <sessionID>",
describe: "delete a session", describe: "delete a session",
builder: (yargs: Argv) => { builder: (yargs) =>
return yargs.positional("sessionID", { yargs.positional("sessionID", {
describe: "session ID to delete", describe: "session ID to delete",
type: "string", type: "string",
demandOption: true, demandOption: true,
}) }),
}, handler: Effect.fn("Cli.session.delete")(function* (args) {
handler: async (args) => { const ctx = yield* InstanceRef
await bootstrap(process.cwd(), async () => { if (!ctx) return
const store = yield* InstanceStore.Service
return yield* Effect.gen(function* () {
const svc = yield* Session.Service
const sessionID = SessionID.make(args.sessionID) const sessionID = SessionID.make(args.sessionID)
try { // Match legacy try/catch — Session.get surfaces NotFoundError as a defect.
await AppRuntime.runPromise(Session.Service.use((svc) => svc.get(sessionID))) yield* svc.get(sessionID).pipe(Effect.catchCause(() => fail(`Session not found: ${args.sessionID}`)))
} catch { yield* svc.remove(sessionID)
UI.error(`Session not found: ${args.sessionID}`)
process.exit(1)
}
await AppRuntime.runPromise(Session.Service.use((svc) => svc.remove(sessionID)))
UI.println(UI.Style.TEXT_SUCCESS_BOLD + `Session ${args.sessionID} deleted` + UI.Style.TEXT_NORMAL) UI.println(UI.Style.TEXT_SUCCESS_BOLD + `Session ${args.sessionID} deleted` + UI.Style.TEXT_NORMAL)
}) }).pipe(Effect.ensuring(store.dispose(ctx)))
}, }),
}) })
export const SessionListCommand = cmd({ export const SessionListCommand = effectCmd({
command: "list", command: "list",
describe: "list sessions", describe: "list sessions",
builder: (yargs: Argv) => { builder: (yargs) =>
return yargs yargs
.option("max-count", { .option("max-count", {
alias: "n", alias: "n",
describe: "limit to N most recent sessions", describe: "limit to N most recent sessions",
@@ -87,47 +88,42 @@ export const SessionListCommand = cmd({
type: "string", type: "string",
choices: ["table", "json"], choices: ["table", "json"],
default: "table", default: "table",
}) }),
}, handler: Effect.fn("Cli.session.list")(function* (args) {
handler: async (args) => { const ctx = yield* InstanceRef
await bootstrap(process.cwd(), async () => { if (!ctx) return
const sessions = await AppRuntime.runPromise( const store = yield* InstanceStore.Service
Session.Service.use((svc) => svc.list({ roots: true, limit: args.maxCount })), return yield* Effect.gen(function* () {
) const sessions = yield* Session.Service.use((svc) => svc.list({ roots: true, limit: args.maxCount }))
if (sessions.length === 0) { if (sessions.length === 0) return
return
}
let output: string const output = args.format === "json" ? formatSessionJSON(sessions) : formatSessionTable(sessions)
if (args.format === "json") {
output = formatSessionJSON(sessions)
} else {
output = formatSessionTable(sessions)
}
const shouldPaginate = process.stdout.isTTY && !args.maxCount && args.format === "table" const shouldPaginate = process.stdout.isTTY && !args.maxCount && args.format === "table"
if (shouldPaginate) { if (shouldPaginate) {
const proc = Process.spawn(pagerCmd(), { yield* Effect.promise(async () => {
stdin: "pipe", const proc = Process.spawn(pagerCmd(), {
stdout: "inherit", stdin: "pipe",
stderr: "inherit", stdout: "inherit",
stderr: "inherit",
})
if (!proc.stdin) {
console.log(output)
return
}
proc.stdin.write(output)
proc.stdin.end()
await proc.exited
}) })
if (!proc.stdin) {
console.log(output)
return
}
proc.stdin.write(output)
proc.stdin.end()
await proc.exited
} else { } else {
console.log(output) console.log(output)
} }
}) }).pipe(Effect.ensuring(store.dispose(ctx)))
}, }),
}) })
function formatSessionTable(sessions: Session.Info[]): string { function formatSessionTable(sessions: Session.Info[]): string {