This commit is contained in:
Dax Raad
2025-05-18 02:43:01 -04:00
parent a34d020bc6
commit d0d67029f4
11 changed files with 351 additions and 75 deletions

52
js/src/app/config.ts Normal file
View File

@@ -0,0 +1,52 @@
import path from "node:path";
import { Log } from "../util/log";
import { z } from "zod/v4";
export namespace Config {
const log = Log.create({ service: "config" });
export const Info = z
.object({
providers: z
.object({
anthropic: z
.object({
apiKey: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
baseURL: z.string().optional(),
})
.strict()
.optional(),
})
.strict()
.optional(),
})
.strict();
export type Info = z.output<typeof Info>;
export async function load(directory: string) {
let result: Info = {};
for (const file of ["opencode.jsonc", "opencode.json"]) {
const resolved = path.join(directory, file);
log.info("searching", { path: resolved });
try {
result = await import(path.join(directory, file)).then((mod) =>
Info.parse(mod.default),
);
log.info("found", { path: resolved });
break;
} catch (e) {
if (e instanceof z.ZodError) {
for (const issue of e.issues) {
log.info(issue.message);
}
throw e;
}
continue;
}
}
log.info("loaded", result);
return result;
}
}

View File

@@ -2,6 +2,7 @@ import fs from "fs/promises";
import { AppPath } from "./path";
import { Log } from "../util/log";
import { Context } from "../util/context";
import { Config } from "./config";
export namespace App {
const log = Log.create({ service: "app" });
@@ -13,29 +14,40 @@ export namespace App {
export async function create(input: { directory: string }) {
log.info("creating");
const config = await Config.load(input.directory);
const dataDir = AppPath.data(input.directory);
await fs.mkdir(dataDir, { recursive: true });
log.info("created", { path: dataDir });
const services = new Map<any, any>();
return {
const result = {
get services() {
return services;
},
get config() {
return config;
},
get root() {
return input.directory;
},
service<T extends () => any>(service: any, init: T) {
if (!services.has(service)) {
log.info("registering service", { name: service });
services.set(service, init());
}
return services.get(service) as ReturnType<T>;
},
service<T extends (app: any) => any>(service: any, init: T) {},
};
return result;
}
export function service<T extends () => any>(key: any, init: T) {
const app = ctx.use();
return app.service(key, init);
export function state<T extends (app: Info) => any>(key: any, init: T) {
return () => {
const app = ctx.use();
const services = app.services;
if (!services.has(key)) {
log.info("registering service", { name: key });
services.set(key, init(app));
}
return services.get(key) as ReturnType<T>;
};
}
export async function use() {