mirror of
https://github.com/sst/opencode.git
synced 2025-08-04 05:28:16 +00:00
Refactor application path handling and data storage architecture
Replace simple directory-based path system with git-aware data management that uses global data directories and proper workspace detection.
🤖 Generated with opencode
Co-Authored-By: opencode <noreply@opencode.ai>
This commit is contained in:
parent
4be9f7ab9c
commit
526a8ea19a
17 changed files with 69 additions and 42 deletions
|
@ -1,7 +1,9 @@
|
|||
import fs from "fs/promises"
|
||||
import { AppPath } from "./path"
|
||||
import { Log } from "../util/log"
|
||||
import { Context } from "../util/context"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
import { Global } from "../global"
|
||||
import path from "path"
|
||||
|
||||
export namespace App {
|
||||
const log = Log.create({ service: "app" })
|
||||
|
@ -10,12 +12,13 @@ export namespace App {
|
|||
|
||||
const ctx = Context.create<Info>("app")
|
||||
|
||||
async function create(input: { directory: string }) {
|
||||
const dataDir = AppPath.data(input.directory)
|
||||
await fs.mkdir(dataDir, { recursive: true })
|
||||
await Log.file(input.directory)
|
||||
async function create(input: { cwd: string; version: string }) {
|
||||
let root = await Filesystem.findUp(".git", input.cwd).then((x) =>
|
||||
x ? path.dirname(x) : input.cwd,
|
||||
)
|
||||
|
||||
log.info("created", { path: dataDir })
|
||||
const data = path.join(Global.data(), root)
|
||||
await Bun.write(path.join(data, "version"), input.version)
|
||||
|
||||
const services = new Map<
|
||||
any,
|
||||
|
@ -25,14 +28,16 @@ export namespace App {
|
|||
}
|
||||
>()
|
||||
|
||||
const result = {
|
||||
get services() {
|
||||
return services
|
||||
await Log.file(path.join(data, "log"))
|
||||
|
||||
const result = Object.freeze({
|
||||
services,
|
||||
path: {
|
||||
data,
|
||||
root,
|
||||
cwd: input.cwd,
|
||||
},
|
||||
get root() {
|
||||
return input.directory
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -61,7 +66,7 @@ export namespace App {
|
|||
}
|
||||
|
||||
export async function provide<T extends (app: Info) => any>(
|
||||
input: { directory: string },
|
||||
input: { cwd: string; version: string },
|
||||
cb: T,
|
||||
) {
|
||||
const app = await create(input)
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import path from "path"
|
||||
|
||||
export namespace AppPath {
|
||||
export function data(input: string) {
|
||||
return path.join(input, ".opencode")
|
||||
}
|
||||
|
||||
export function storage(input: string) {
|
||||
return path.join(data(input), "storage")
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ export namespace Config {
|
|||
const log = Log.create({ service: "config" })
|
||||
|
||||
export const state = App.state("config", async (app) => {
|
||||
const result = await load(app.root)
|
||||
const result = await load(app.path.root)
|
||||
return result
|
||||
})
|
||||
|
||||
|
|
|
@ -17,4 +17,8 @@ export namespace Global {
|
|||
export function cache() {
|
||||
return paths.cache
|
||||
}
|
||||
|
||||
export function data() {
|
||||
return paths.data
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ export namespace Identifier {
|
|||
const prefixes = {
|
||||
session: "ses",
|
||||
message: "msg",
|
||||
user: "usr",
|
||||
} as const
|
||||
|
||||
export function schema(prefix: keyof typeof prefixes) {
|
||||
|
|
|
@ -16,9 +16,10 @@ declare global {
|
|||
}
|
||||
|
||||
const cli = cac("opencode")
|
||||
const version = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "dev"
|
||||
|
||||
cli.command("", "Start the opencode in interactive mode").action(async () => {
|
||||
await App.provide({ directory: process.cwd() }, async () => {
|
||||
await App.provide({ cwd: process.cwd(), version }, async () => {
|
||||
await Share.init()
|
||||
const server = Server.listen()
|
||||
|
||||
|
@ -66,14 +67,14 @@ cli
|
|||
.command("run [...message]", "Run a chat message")
|
||||
.option("--session <id>", "Session ID")
|
||||
.action(async (message: string[], options) => {
|
||||
await App.provide({ directory: process.cwd() }, async () => {
|
||||
await App.provide({ cwd: process.cwd(), version }, async () => {
|
||||
await Share.init()
|
||||
const session = options.session
|
||||
? await Session.get(options.session)
|
||||
: await Session.create()
|
||||
console.log("Session:", session.id)
|
||||
|
||||
Bus.subscribe(Message.Event.Updated, async (message) => {
|
||||
Bus.subscribe(Message.Event.Updated, async () => {
|
||||
console.log("Thinking...")
|
||||
})
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export namespace LSPClient {
|
|||
const [command, ...args] = input.cmd
|
||||
const server = spawn(command, args, {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
cwd: app.root,
|
||||
cwd: app.path.cwd,
|
||||
})
|
||||
|
||||
const connection = createMessageConnection(
|
||||
|
@ -64,7 +64,7 @@ export namespace LSPClient {
|
|||
workspaceFolders: [
|
||||
{
|
||||
name: "workspace",
|
||||
uri: "file://" + app.root,
|
||||
uri: "file://" + app.path.cwd,
|
||||
},
|
||||
],
|
||||
tsserver: {
|
||||
|
|
|
@ -220,7 +220,7 @@ export namespace Session {
|
|||
tool: {},
|
||||
},
|
||||
}
|
||||
const contextFile = Bun.file(path.join(app.root, "CONTEXT.md"))
|
||||
const contextFile = Bun.file(path.join(app.path.root, "CONTEXT.md"))
|
||||
if (await contextFile.exists()) {
|
||||
const context = await contextFile.text()
|
||||
system.parts.push({
|
||||
|
|
|
@ -3,8 +3,8 @@ import { LocalStorageAdapter } from "@flystorage/local-fs"
|
|||
import fs from "fs/promises"
|
||||
import { Log } from "../util/log"
|
||||
import { App } from "../app/app"
|
||||
import { AppPath } from "../app/path"
|
||||
import { Bus } from "../bus"
|
||||
import path from "path"
|
||||
import z from "zod"
|
||||
|
||||
export namespace Storage {
|
||||
|
@ -19,7 +19,7 @@ export namespace Storage {
|
|||
|
||||
const state = App.state("storage", async () => {
|
||||
const app = await App.use()
|
||||
const storageDir = AppPath.storage(app.root)
|
||||
const storageDir = path.join(app.path.data, "storage")
|
||||
await fs.mkdir(storageDir, { recursive: true })
|
||||
const storage = new FileStorage(new LocalStorageAdapter(storageDir))
|
||||
log.info("created", { path: storageDir })
|
||||
|
|
|
@ -51,7 +51,7 @@ export const GlobTool = Tool.define({
|
|||
}),
|
||||
async execute(params) {
|
||||
const app = await App.use()
|
||||
const search = params.path || app.root
|
||||
const search = params.path || app.path.cwd
|
||||
const limit = 100
|
||||
const glob = new Bun.Glob(params.pattern)
|
||||
const files = []
|
||||
|
|
|
@ -287,7 +287,7 @@ export const GrepTool = Tool.define({
|
|||
}
|
||||
|
||||
const app = await App.use()
|
||||
const searchPath = params.path || app.root
|
||||
const searchPath = params.path || app.path.cwd
|
||||
|
||||
// If literalText is true, escape the pattern
|
||||
const searchPattern = params.literalText
|
||||
|
|
|
@ -26,7 +26,7 @@ export const ListTool = Tool.define({
|
|||
}),
|
||||
async execute(params) {
|
||||
const app = await App.use()
|
||||
const searchPath = path.resolve(app.root, params.path || ".")
|
||||
const searchPath = path.resolve(app.path.cwd, params.path || ".")
|
||||
|
||||
const glob = new Bun.Glob("**/*")
|
||||
const files = []
|
||||
|
|
|
@ -37,7 +37,7 @@ TIPS:
|
|||
const app = await App.use()
|
||||
const normalized = path.isAbsolute(args.path)
|
||||
? args.path
|
||||
: path.join(app.root, args.path)
|
||||
: path.join(app.path.cwd, args.path)
|
||||
await LSP.file(normalized)
|
||||
const diagnostics = await LSP.diagnostics()
|
||||
const file = diagnostics[normalized]
|
||||
|
|
|
@ -21,7 +21,7 @@ export const LspHoverTool = Tool.define({
|
|||
const app = await App.use()
|
||||
const file = path.isAbsolute(args.file)
|
||||
? args.file
|
||||
: path.join(app.root, args.file)
|
||||
: path.join(app.path.cwd, args.file)
|
||||
await LSP.file(file)
|
||||
const result = await LSP.hover({
|
||||
...args,
|
||||
|
|
18
packages/opencode/src/util/filesystem.ts
Normal file
18
packages/opencode/src/util/filesystem.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { exists } from "fs/promises"
|
||||
import { dirname, join } from "path"
|
||||
|
||||
export namespace Filesystem {
|
||||
export async function findUp(target: string, start: string) {
|
||||
let currentDir = start
|
||||
while (true) {
|
||||
const targetPath = join(currentDir, target)
|
||||
if (await exists(targetPath)) return targetPath
|
||||
const parentDir = dirname(currentDir)
|
||||
if (parentDir === currentDir) {
|
||||
return
|
||||
}
|
||||
currentDir = parentDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import path from "path"
|
||||
import { AppPath } from "../app/path"
|
||||
import fs from "fs/promises"
|
||||
export namespace Log {
|
||||
const write = {
|
||||
|
@ -12,8 +11,9 @@ export namespace Log {
|
|||
}
|
||||
|
||||
export async function file(directory: string) {
|
||||
const outPath = path.join(AppPath.data(directory), "opencode.out.log")
|
||||
const errPath = path.join(AppPath.data(directory), "opencode.err.log")
|
||||
await fs.mkdir(directory, { recursive: true })
|
||||
const outPath = path.join(directory, "opencode.out.log")
|
||||
const errPath = path.join(directory, "opencode.err.log")
|
||||
await fs.truncate(outPath).catch(() => {})
|
||||
await fs.truncate(errPath).catch(() => {})
|
||||
const out = Bun.file(outPath)
|
||||
|
|
9
packages/opencode/sst-env.d.ts
vendored
Normal file
9
packages/opencode/sst-env.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* This file is auto-generated by SST. Do not edit. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* deno-fmt-ignore-file */
|
||||
|
||||
/// <reference path="../../sst-env.d.ts" />
|
||||
|
||||
import "sst"
|
||||
export {}
|
Loading…
Add table
Add a link
Reference in a new issue