global.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import path from "path"
  2. import fs from "fs/promises"
  3. import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
  4. import os from "os"
  5. import { Context, Effect, Layer } from "effect"
  6. import { Flock } from "./flock.js"
  7. import { makeGlobalNode } from "./effect/app-node.js"
  8. const app = "opencode"
  9. const data = path.join(xdgData!, app)
  10. const cache = path.join(xdgCache!, app)
  11. const config = path.join(xdgConfig!, app)
  12. const state = path.join(xdgState!, app)
  13. const tmp = path.join(os.tmpdir(), app)
  14. const paths = {
  15. get home() {
  16. return process.env.OPENCODE_TEST_HOME ?? os.homedir()
  17. },
  18. data,
  19. bin: path.join(cache, "bin"),
  20. log: path.join(data, "log"),
  21. repos: path.join(data, "repos"),
  22. cache,
  23. config,
  24. state,
  25. tmp,
  26. }
  27. export const Path = paths
  28. Flock.setGlobal({ state })
  29. await Promise.all([
  30. fs.mkdir(Path.data, { recursive: true }),
  31. fs.mkdir(Path.config, { recursive: true }),
  32. fs.mkdir(Path.state, { recursive: true }),
  33. fs.mkdir(Path.tmp, { recursive: true }),
  34. fs.mkdir(Path.log, { recursive: true }),
  35. fs.mkdir(Path.bin, { recursive: true }),
  36. fs.mkdir(Path.repos, { recursive: true }),
  37. ])
  38. export class Service extends Context.Service<Service, Interface>()("@opencode/Global") {}
  39. export interface Interface {
  40. readonly home: string
  41. readonly data: string
  42. readonly cache: string
  43. readonly config: string
  44. readonly state: string
  45. readonly tmp: string
  46. readonly bin: string
  47. readonly log: string
  48. readonly repos: string
  49. }
  50. export function make(input: Partial<Interface> = {}): Interface {
  51. return {
  52. home: Path.home,
  53. data: Path.data,
  54. cache: Path.cache,
  55. config: Path.config,
  56. state: Path.state,
  57. tmp: Path.tmp,
  58. bin: Path.bin,
  59. log: Path.log,
  60. repos: Path.repos,
  61. ...input,
  62. }
  63. }
  64. const layer = Layer.effect(
  65. Service,
  66. Effect.sync(() => Service.of(make({ config: process.env.OPENCODE_CONFIG_DIR ?? Path.config }))),
  67. )
  68. export const node = makeGlobalNode({ service: Service, layer: layer, deps: [] })
  69. export const layerWith = (input: Partial<Interface>) =>
  70. Layer.effect(
  71. Service,
  72. Effect.sync(() => Service.of(make(input))),
  73. )
  74. export * as Global from "./global.js"