cloudaxe-opencode/packages/opencode/src/app/app.ts

138 lines
3.2 KiB
TypeScript
Raw Normal View History

2025-05-31 18:41:00 +00:00
import { Log } from "../util/log"
import { Context } from "../util/context"
import { Filesystem } from "../util/filesystem"
import { Global } from "../global"
import path from "path"
2025-06-03 20:47:47 +00:00
import os from "os"
import { z } from "zod"
2025-05-18 01:31:42 +00:00
export namespace App {
2025-05-31 18:41:00 +00:00
const log = Log.create({ service: "app" })
2025-05-18 01:31:42 +00:00
export const Info = z
.object({
2025-06-03 20:47:47 +00:00
user: z.string(),
2025-06-03 17:18:42 +00:00
git: z.boolean(),
path: z.object({
2025-06-03 20:47:47 +00:00
config: z.string(),
data: z.string(),
root: z.string(),
cwd: z.string(),
}),
2025-06-03 18:24:45 +00:00
time: z.object({
initialized: z.number().optional(),
}),
})
.openapi({
ref: "App.Info",
})
export type Info = z.infer<typeof Info>
2025-05-18 01:31:42 +00:00
const ctx = Context.create<Awaited<ReturnType<typeof create>>>("app")
2025-05-18 01:31:42 +00:00
2025-06-03 18:28:50 +00:00
const APP_JSON = "app.json"
async function create(input: { cwd: string; version: string }) {
2025-06-09 19:00:48 +00:00
log.info("creating", {
cwd: input.cwd,
version: input.version,
})
2025-06-09 19:28:06 +00:00
const git = await Filesystem.findUp(".git", input.cwd).then(([x]) =>
x ? path.dirname(x) : undefined,
)
2025-06-09 19:00:48 +00:00
log.info("git", { git })
2025-05-21 14:30:39 +00:00
2025-06-05 15:50:54 +00:00
const data = path.join(
Global.Path.data,
"project",
git ? git.split(path.sep).join("-") : "global",
)
2025-06-03 18:28:50 +00:00
const stateFile = Bun.file(path.join(data, APP_JSON))
2025-06-03 18:24:45 +00:00
const state = (await stateFile.json().catch(() => ({}))) as {
initialized: number
version: string
}
state.version = input.version
await stateFile.write(JSON.stringify(state))
2025-05-18 01:31:42 +00:00
2025-05-26 18:52:38 +00:00
const services = new Map<
any,
{
2025-05-31 18:41:00 +00:00
state: any
shutdown?: (input: any) => Promise<void>
2025-05-26 18:52:38 +00:00
}
2025-05-31 18:41:00 +00:00
>()
2025-05-18 01:31:42 +00:00
const info: Info = {
2025-06-03 20:47:47 +00:00
user: os.userInfo().username,
time: {
initialized: state.initialized,
},
2025-06-03 17:18:42 +00:00
git: git !== undefined,
path: {
2025-06-03 20:47:47 +00:00
config: Global.Path.config,
data,
root: git ?? input.cwd,
cwd: input.cwd,
2025-05-18 01:31:42 +00:00
},
}
const result = {
2025-06-03 18:24:45 +00:00
version: input.version,
services,
info,
}
2025-05-18 06:43:01 +00:00
2025-05-31 18:41:00 +00:00
return result
2025-05-18 01:31:42 +00:00
}
2025-05-26 18:52:38 +00:00
export function state<State>(
key: any,
init: (app: Info) => State,
shutdown?: (state: Awaited<State>) => Promise<void>,
) {
2025-05-18 06:43:01 +00:00
return () => {
2025-05-31 18:41:00 +00:00
const app = ctx.use()
const services = app.services
2025-05-18 06:43:01 +00:00
if (!services.has(key)) {
2025-05-31 18:41:00 +00:00
log.info("registering service", { name: key })
2025-05-26 18:52:38 +00:00
services.set(key, {
state: init(app.info),
2025-05-26 18:52:38 +00:00
shutdown: shutdown,
2025-05-31 18:41:00 +00:00
})
2025-05-18 06:43:01 +00:00
}
2025-05-31 18:41:00 +00:00
return services.get(key)?.state as State
}
2025-05-18 01:31:42 +00:00
}
export function info() {
return ctx.use().info
2025-05-18 01:31:42 +00:00
}
2025-05-21 14:30:39 +00:00
export async function provide<T extends (app: Info) => any>(
input: { cwd: string; version: string },
2025-05-21 14:30:39 +00:00
cb: T,
) {
2025-05-31 18:41:00 +00:00
const app = await create(input)
2025-05-23 20:20:21 +00:00
return ctx.provide(app, async () => {
const result = await cb(app.info)
2025-05-26 18:52:38 +00:00
for (const [key, entry] of app.services.entries()) {
2025-05-31 18:41:00 +00:00
log.info("shutdown", { name: key })
await entry.shutdown?.(await entry.state)
2025-05-26 18:52:38 +00:00
}
2025-05-31 18:41:00 +00:00
return result
})
2025-05-21 14:30:39 +00:00
}
2025-06-03 18:24:45 +00:00
export async function initialize() {
const { info, version } = ctx.use()
info.time.initialized = Date.now()
await Bun.write(
2025-06-03 18:28:50 +00:00
path.join(info.path.data, APP_JSON),
2025-06-03 18:24:45 +00:00
JSON.stringify({
version,
initialized: Date.now(),
}),
)
}
2025-05-18 01:31:42 +00:00
}