refactor(opencode): remove AppRuntime from tui file io

This commit is contained in:
Kit Langton
2026-04-15 12:40:11 -04:00
parent d8070f7f23
commit 58a8d4dbf5
8 changed files with 39 additions and 144 deletions

View File

@@ -1,8 +1,5 @@
import path from "path" import path from "path"
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { onMount } from "solid-js" import { onMount } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { createSimpleContext } from "../../context/helper" import { createSimpleContext } from "../../context/helper"
@@ -21,22 +18,10 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont
name: "Frecency", name: "Frecency",
init: () => { init: () => {
const frecencyPath = path.join(Global.Path.state, "frecency.jsonl") const frecencyPath = path.join(Global.Path.state, "frecency.jsonl")
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readFileString(frecencyPath)
}),
)
const write = (content: string) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeWithDirs(frecencyPath, content)
}),
)
onMount(async () => { onMount(async () => {
const text = await read().catch(() => "") const text = await Bun.file(frecencyPath)
.text()
.catch(() => "")
const lines = text const lines = text
.split("\n") .split("\n")
.filter(Boolean) .filter(Boolean)
@@ -70,7 +55,7 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont
if (sorted.length > 0) { if (sorted.length > 0) {
const content = sorted.map((entry) => JSON.stringify(entry)).join("\n") + "\n" const content = sorted.map((entry) => JSON.stringify(entry)).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(frecencyPath, content)
} }
}) })
@@ -93,7 +78,7 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont
.slice(0, MAX_FRECENCY_ENTRIES) .slice(0, MAX_FRECENCY_ENTRIES)
setStore("data", Object.fromEntries(sorted)) setStore("data", Object.fromEntries(sorted))
const content = sorted.map(([path, entry]) => JSON.stringify({ path, ...entry })).join("\n") + "\n" const content = sorted.map(([path, entry]) => JSON.stringify({ path, ...entry })).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(frecencyPath, content)
} }
} }

View File

@@ -1,8 +1,5 @@
import path from "path" import path from "path"
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { onMount } from "solid-js" import { onMount } from "solid-js"
import { createStore, produce, unwrap } from "solid-js/store" import { createStore, produce, unwrap } from "solid-js/store"
import { createSimpleContext } from "../../context/helper" import { createSimpleContext } from "../../context/helper"
@@ -33,22 +30,10 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
name: "PromptHistory", name: "PromptHistory",
init: () => { init: () => {
const historyPath = path.join(Global.Path.state, "prompt-history.jsonl") const historyPath = path.join(Global.Path.state, "prompt-history.jsonl")
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readFileString(historyPath)
}),
)
const write = (content: string) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeWithDirs(historyPath, content)
}),
)
onMount(async () => { onMount(async () => {
const text = await read().catch(() => "") const text = await Bun.file(historyPath)
.text()
.catch(() => "")
const lines = text const lines = text
.split("\n") .split("\n")
.filter(Boolean) .filter(Boolean)
@@ -67,7 +52,7 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
// Rewrite file with only valid entries to self-heal corruption // Rewrite file with only valid entries to self-heal corruption
if (lines.length > 0) { if (lines.length > 0) {
const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n" const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(historyPath, content)
} }
}) })
@@ -113,7 +98,7 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
if (trimmed) { if (trimmed) {
const content = store.history.map((line) => JSON.stringify(line)).join("\n") + "\n" const content = store.history.map((line) => JSON.stringify(line)).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(historyPath, content)
return return
} }

View File

@@ -1,8 +1,5 @@
import path from "path" import path from "path"
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { onMount } from "solid-js" import { onMount } from "solid-js"
import { createStore, produce, unwrap } from "solid-js/store" import { createStore, produce, unwrap } from "solid-js/store"
import { createSimpleContext } from "../../context/helper" import { createSimpleContext } from "../../context/helper"
@@ -21,22 +18,10 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp
name: "PromptStash", name: "PromptStash",
init: () => { init: () => {
const stashPath = path.join(Global.Path.state, "prompt-stash.jsonl") const stashPath = path.join(Global.Path.state, "prompt-stash.jsonl")
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readFileString(stashPath)
}),
)
const write = (content: string) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeWithDirs(stashPath, content)
}),
)
onMount(async () => { onMount(async () => {
const text = await read().catch(() => "") const text = await Bun.file(stashPath)
.text()
.catch(() => "")
const lines = text const lines = text
.split("\n") .split("\n")
.filter(Boolean) .filter(Boolean)
@@ -55,7 +40,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp
// Rewrite file with only valid entries to self-heal corruption // Rewrite file with only valid entries to self-heal corruption
if (lines.length > 0) { if (lines.length > 0) {
const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n" const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(stashPath, content)
} }
}) })
@@ -82,7 +67,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp
if (trimmed) { if (trimmed) {
const content = store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" const content = store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n"
write(content).catch(() => {}) void Bun.write(stashPath, content)
return return
} }
@@ -98,7 +83,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp
) )
const content = const content =
store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : "" store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : ""
write(content).catch(() => {}) void Bun.write(stashPath, content)
return entry return entry
}, },
remove(index: number) { remove(index: number) {
@@ -110,7 +95,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp
) )
const content = const content =
store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : "" store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : ""
write(content).catch(() => {}) void Bun.write(stashPath, content)
}, },
} }
}, },

View File

@@ -1,7 +1,4 @@
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { createSignal, type Setter } from "solid-js" import { createSignal, type Setter } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { createSimpleContext } from "./helper" import { createSimpleContext } from "./helper"
@@ -13,22 +10,9 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
const [ready, setReady] = createSignal(false) const [ready, setReady] = createSignal(false)
const [store, setStore] = createStore<Record<string, any>>() const [store, setStore] = createStore<Record<string, any>>()
const filePath = path.join(Global.Path.state, "kv.json") const filePath = path.join(Global.Path.state, "kv.json")
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readJson(filePath)
}),
)
const write = (data: unknown) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeJson(filePath, data)
}),
)
read() Bun.file(filePath)
.json()
.then((x) => { .then((x) => {
if (typeof x === "object" && x !== null) setStore(x as Record<string, any>) if (typeof x === "object" && x !== null) setStore(x as Record<string, any>)
}) })
@@ -60,7 +44,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
}, },
set(key: string, value: any) { set(key: string, value: any) {
setStore(key, value) setStore(key, value)
write(store) void Bun.write(filePath, JSON.stringify(store, null, 2))
}, },
} }
return result return result

View File

@@ -1,14 +1,11 @@
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { batch, createEffect, createMemo } from "solid-js" import { batch, createEffect, createMemo } from "solid-js"
import { Effect } from "effect"
import { useSync } from "@tui/context/sync" import { useSync } from "@tui/context/sync"
import { useTheme } from "@tui/context/theme" import { useTheme } from "@tui/context/theme"
import { uniqueBy } from "remeda" import { uniqueBy } from "remeda"
import path from "path" import path from "path"
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { iife } from "@/util/iife" import { iife } from "@/util/iife"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { createSimpleContext } from "./helper" import { createSimpleContext } from "./helper"
import { useToast } from "../ui/toast" import { useToast } from "../ui/toast"
import { Provider } from "@/provider/provider" import { Provider } from "@/provider/provider"
@@ -126,20 +123,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const state = { const state = {
pending: false, pending: false,
} }
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readJson(filePath)
}),
)
const write = (data: unknown) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeJson(filePath, data)
}),
)
function save() { function save() {
if (!modelStore.ready) { if (!modelStore.ready) {
@@ -147,14 +130,22 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
return return
} }
state.pending = false state.pending = false
write({ void Bun.write(
recent: modelStore.recent, filePath,
favorite: modelStore.favorite, JSON.stringify(
variant: modelStore.variant, {
}) recent: modelStore.recent,
favorite: modelStore.favorite,
variant: modelStore.variant,
},
null,
2,
),
)
} }
read() Bun.file(filePath)
.json()
.then((x: any) => { .then((x: any) => {
if (Array.isArray(x.recent)) setModelStore("recent", x.recent) if (Array.isArray(x.recent)) setModelStore("recent", x.recent)
if (Array.isArray(x.favorite)) setModelStore("favorite", x.favorite) if (Array.isArray(x.favorite)) setModelStore("favorite", x.favorite)

View File

@@ -75,7 +75,6 @@ import stripAnsi from "strip-ansi"
import { usePromptRef } from "../../context/prompt" import { usePromptRef } from "../../context/prompt"
import { useExit } from "../../context/exit" import { useExit } from "../../context/exit"
import { Global } from "@/global" import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem" import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { PermissionPrompt } from "./permission" import { PermissionPrompt } from "./permission"
import { QuestionPrompt } from "./question" import { QuestionPrompt } from "./question"
@@ -88,7 +87,6 @@ import { getScrollAcceleration } from "../../util/scroll"
import { TuiPluginRuntime } from "../../plugin" import { TuiPluginRuntime } from "../../plugin"
import { DialogGoUpsell } from "../../component/dialog-go-upsell" import { DialogGoUpsell } from "../../component/dialog-go-upsell"
import { SessionRetry } from "@/session/retry" import { SessionRetry } from "@/session/retry"
import { Effect } from "effect"
addDefaultParsers(parsers.parsers) addDefaultParsers(parsers.parsers)
@@ -917,20 +915,13 @@ export function Session() {
const exportDir = process.cwd() const exportDir = process.cwd()
const filename = options.filename.trim() const filename = options.filename.trim()
const filepath = path.join(exportDir, filename) const filepath = path.join(exportDir, filename)
const write = (content: string) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeWithDirs(filepath, content)
}),
)
await write(transcript) await Bun.write(filepath, transcript)
// Open with EDITOR if available // Open with EDITOR if available
const result = await Editor.open({ value: transcript, renderer }) const result = await Editor.open({ value: transcript, renderer })
if (result !== undefined) { if (result !== undefined) {
await write(result) await Bun.write(filepath, result)
} }
toast.show({ message: `Session exported to ${filename}`, variant: "success" }) toast.show({ message: `Session exported to ${filename}`, variant: "success" })

View File

@@ -1,8 +1,5 @@
import { platform, release } from "os" import { platform, release } from "os"
import clipboardy from "clipboardy" import clipboardy from "clipboardy"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { lazy } from "../../../../util/lazy.js" import { lazy } from "../../../../util/lazy.js"
import { tmpdir } from "os" import { tmpdir } from "os"
import path from "path" import path from "path"
@@ -60,13 +57,7 @@ export namespace Clipboard {
], ],
{ nothrow: true }, { nothrow: true },
) )
const buffer = await AppRuntime.runPromise( return { data: Buffer.from(await Bun.file(tmpfile).arrayBuffer()).toString("base64"), mime: "image/png" }
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readFile(tmpfile)
}),
)
return { data: Buffer.from(buffer).toString("base64"), mime: "image/png" }
} catch { } catch {
} finally { } finally {
await fs.rm(tmpfile, { force: true }).catch(() => {}) await fs.rm(tmpfile, { force: true }).catch(() => {})

View File

@@ -1,7 +1,4 @@
import { defer } from "@/util/defer" import { defer } from "@/util/defer"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Effect } from "effect"
import { rm } from "node:fs/promises" import { rm } from "node:fs/promises"
import { tmpdir } from "node:os" import { tmpdir } from "node:os"
import { join } from "node:path" import { join } from "node:path"
@@ -14,23 +11,9 @@ export namespace Editor {
if (!editor) return if (!editor) return
const filepath = join(tmpdir(), `${Date.now()}.md`) const filepath = join(tmpdir(), `${Date.now()}.md`)
const write = (content: string) =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
yield* fs.writeWithDirs(filepath, content)
}),
)
const read = () =>
AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fs.readFileString(filepath)
}),
)
await using _ = defer(async () => rm(filepath, { force: true })) await using _ = defer(async () => rm(filepath, { force: true }))
await write(opts.value) await Bun.write(filepath, opts.value)
opts.renderer.suspend() opts.renderer.suspend()
opts.renderer.currentRenderBuffer.clear() opts.renderer.currentRenderBuffer.clear()
try { try {
@@ -42,7 +25,7 @@ export namespace Editor {
shell: process.platform === "win32", shell: process.platform === "win32",
}) })
await proc.exited await proc.exited
const content = await read() const content = await Bun.file(filepath).text()
return content || undefined return content || undefined
} finally { } finally {
opts.renderer.currentRenderBuffer.clear() opts.renderer.currentRenderBuffer.clear()