Răsfoiți Sursa

chore: generate

opencode-agent[bot] 4 săptămâni în urmă
părinte
comite
51618e9cef

+ 172 - 178
packages/opencode/src/installation/index.ts

@@ -81,119 +81,115 @@ export namespace Installation {
 
 
   export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Installation") {}
   export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Installation") {}
 
 
-  export const layer: Layer.Layer<
-    Service,
-    never,
-    HttpClient.HttpClient | ChildProcessSpawner.ChildProcessSpawner
-  > = Layer.effect(
-    Service,
-    Effect.gen(function* () {
-      const http = yield* HttpClient.HttpClient
-      const httpOk = HttpClient.filterStatusOk(withTransientReadRetry(http))
-      const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
-
-      const text = Effect.fnUntraced(
-        function* (cmd: string[], opts?: { cwd?: string; env?: Record<string, string> }) {
-          const proc = ChildProcess.make(cmd[0], cmd.slice(1), {
-            cwd: opts?.cwd,
-            env: opts?.env,
-            extendEnv: true,
-          })
-          const handle = yield* spawner.spawn(proc)
-          const out = yield* Stream.mkString(Stream.decodeText(handle.stdout))
-          yield* handle.exitCode
-          return out
-        },
-        Effect.scoped,
-        Effect.catch(() => Effect.succeed("")),
-      )
-
-      const run = Effect.fnUntraced(
-        function* (cmd: string[], opts?: { cwd?: string; env?: Record<string, string> }) {
-          const proc = ChildProcess.make(cmd[0], cmd.slice(1), {
-            cwd: opts?.cwd,
-            env: opts?.env,
-            extendEnv: true,
-          })
-          const handle = yield* spawner.spawn(proc)
-          const [stdout, stderr] = yield* Effect.all(
-            [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
-            { concurrency: 2 },
-          )
-          const code = yield* handle.exitCode
-          return { code, stdout, stderr }
-        },
-        Effect.scoped,
-        Effect.catch(() => Effect.succeed({ code: ChildProcessSpawner.ExitCode(1), stdout: "", stderr: "" })),
-      )
-
-      const getBrewFormula = Effect.fnUntraced(function* () {
-        const tapFormula = yield* text(["brew", "list", "--formula", "anomalyco/tap/opencode"])
-        if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode"
-        const coreFormula = yield* text(["brew", "list", "--formula", "opencode"])
-        if (coreFormula.includes("opencode")) return "opencode"
-        return "opencode"
-      })
-
-      const upgradeCurl = Effect.fnUntraced(
-        function* (target: string) {
-          const response = yield* httpOk.execute(HttpClientRequest.get("https://opencode.ai/install"))
-          const body = yield* response.text
-          const bodyBytes = new TextEncoder().encode(body)
-          const proc = ChildProcess.make("bash", [], {
-            stdin: Stream.make(bodyBytes),
-            env: { VERSION: target },
-            extendEnv: true,
-          })
-          const handle = yield* spawner.spawn(proc)
-          const [stdout, stderr] = yield* Effect.all(
-            [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
-            { concurrency: 2 },
-          )
-          const code = yield* handle.exitCode
-          return { code, stdout, stderr }
-        },
-        Effect.scoped,
-        Effect.orDie,
-      )
-
-      const methodImpl = Effect.fn("Installation.method")(function* () {
-        if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl" as Method
-        if (process.execPath.includes(path.join(".local", "bin"))) return "curl" as Method
-        const exec = process.execPath.toLowerCase()
-
-        const checks: Array<{ name: Method; command: () => Effect.Effect<string> }> = [
-          { name: "npm", command: () => text(["npm", "list", "-g", "--depth=0"]) },
-          { name: "yarn", command: () => text(["yarn", "global", "list"]) },
-          { name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) },
-          { name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) },
-          { name: "brew", command: () => text(["brew", "list", "--formula", "opencode"]) },
-          { name: "scoop", command: () => text(["scoop", "list", "opencode"]) },
-          { name: "choco", command: () => text(["choco", "list", "--limit-output", "opencode"]) },
-        ]
-
-        checks.sort((a, b) => {
-          const aMatches = exec.includes(a.name)
-          const bMatches = exec.includes(b.name)
-          if (aMatches && !bMatches) return -1
-          if (!aMatches && bMatches) return 1
-          return 0
+  export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | ChildProcessSpawner.ChildProcessSpawner> =
+    Layer.effect(
+      Service,
+      Effect.gen(function* () {
+        const http = yield* HttpClient.HttpClient
+        const httpOk = HttpClient.filterStatusOk(withTransientReadRetry(http))
+        const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
+
+        const text = Effect.fnUntraced(
+          function* (cmd: string[], opts?: { cwd?: string; env?: Record<string, string> }) {
+            const proc = ChildProcess.make(cmd[0], cmd.slice(1), {
+              cwd: opts?.cwd,
+              env: opts?.env,
+              extendEnv: true,
+            })
+            const handle = yield* spawner.spawn(proc)
+            const out = yield* Stream.mkString(Stream.decodeText(handle.stdout))
+            yield* handle.exitCode
+            return out
+          },
+          Effect.scoped,
+          Effect.catch(() => Effect.succeed("")),
+        )
+
+        const run = Effect.fnUntraced(
+          function* (cmd: string[], opts?: { cwd?: string; env?: Record<string, string> }) {
+            const proc = ChildProcess.make(cmd[0], cmd.slice(1), {
+              cwd: opts?.cwd,
+              env: opts?.env,
+              extendEnv: true,
+            })
+            const handle = yield* spawner.spawn(proc)
+            const [stdout, stderr] = yield* Effect.all(
+              [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
+              { concurrency: 2 },
+            )
+            const code = yield* handle.exitCode
+            return { code, stdout, stderr }
+          },
+          Effect.scoped,
+          Effect.catch(() => Effect.succeed({ code: ChildProcessSpawner.ExitCode(1), stdout: "", stderr: "" })),
+        )
+
+        const getBrewFormula = Effect.fnUntraced(function* () {
+          const tapFormula = yield* text(["brew", "list", "--formula", "anomalyco/tap/opencode"])
+          if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode"
+          const coreFormula = yield* text(["brew", "list", "--formula", "opencode"])
+          if (coreFormula.includes("opencode")) return "opencode"
+          return "opencode"
         })
         })
 
 
-        for (const check of checks) {
-          const output = yield* check.command()
-          const installedName =
-            check.name === "brew" || check.name === "choco" || check.name === "scoop" ? "opencode" : "opencode-ai"
-          if (output.includes(installedName)) {
-            return check.name
+        const upgradeCurl = Effect.fnUntraced(
+          function* (target: string) {
+            const response = yield* httpOk.execute(HttpClientRequest.get("https://opencode.ai/install"))
+            const body = yield* response.text
+            const bodyBytes = new TextEncoder().encode(body)
+            const proc = ChildProcess.make("bash", [], {
+              stdin: Stream.make(bodyBytes),
+              env: { VERSION: target },
+              extendEnv: true,
+            })
+            const handle = yield* spawner.spawn(proc)
+            const [stdout, stderr] = yield* Effect.all(
+              [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
+              { concurrency: 2 },
+            )
+            const code = yield* handle.exitCode
+            return { code, stdout, stderr }
+          },
+          Effect.scoped,
+          Effect.orDie,
+        )
+
+        const methodImpl = Effect.fn("Installation.method")(function* () {
+          if (process.execPath.includes(path.join(".opencode", "bin"))) return "curl" as Method
+          if (process.execPath.includes(path.join(".local", "bin"))) return "curl" as Method
+          const exec = process.execPath.toLowerCase()
+
+          const checks: Array<{ name: Method; command: () => Effect.Effect<string> }> = [
+            { name: "npm", command: () => text(["npm", "list", "-g", "--depth=0"]) },
+            { name: "yarn", command: () => text(["yarn", "global", "list"]) },
+            { name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) },
+            { name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) },
+            { name: "brew", command: () => text(["brew", "list", "--formula", "opencode"]) },
+            { name: "scoop", command: () => text(["scoop", "list", "opencode"]) },
+            { name: "choco", command: () => text(["choco", "list", "--limit-output", "opencode"]) },
+          ]
+
+          checks.sort((a, b) => {
+            const aMatches = exec.includes(a.name)
+            const bMatches = exec.includes(b.name)
+            if (aMatches && !bMatches) return -1
+            if (!aMatches && bMatches) return 1
+            return 0
+          })
+
+          for (const check of checks) {
+            const output = yield* check.command()
+            const installedName =
+              check.name === "brew" || check.name === "choco" || check.name === "scoop" ? "opencode" : "opencode-ai"
+            if (output.includes(installedName)) {
+              return check.name
+            }
           }
           }
-        }
 
 
-        return "unknown" as Method
-      })
+          return "unknown" as Method
+        })
 
 
-      const latestImpl = Effect.fn("Installation.latest")(
-        function* (installMethod?: Method) {
+        const latestImpl = Effect.fn("Installation.latest")(function* (installMethod?: Method) {
           const detectedMethod = installMethod || (yield* methodImpl())
           const detectedMethod = installMethod || (yield* methodImpl())
 
 
           if (detectedMethod === "brew") {
           if (detectedMethod === "brew") {
@@ -251,82 +247,80 @@ export namespace Installation {
           )
           )
           const data = yield* HttpClientResponse.schemaBodyJson(GitHubRelease)(response)
           const data = yield* HttpClientResponse.schemaBodyJson(GitHubRelease)(response)
           return data.tag_name.replace(/^v/, "")
           return data.tag_name.replace(/^v/, "")
-        },
-        Effect.orDie,
-      )
-
-      const upgradeImpl = Effect.fn("Installation.upgrade")(function* (m: Method, target: string) {
-        let result: { code: ChildProcessSpawner.ExitCode; stdout: string; stderr: string } | undefined
-        switch (m) {
-          case "curl":
-            result = yield* upgradeCurl(target)
-            break
-          case "npm":
-            result = yield* run(["npm", "install", "-g", `opencode-ai@${target}`])
-            break
-          case "pnpm":
-            result = yield* run(["pnpm", "install", "-g", `opencode-ai@${target}`])
-            break
-          case "bun":
-            result = yield* run(["bun", "install", "-g", `opencode-ai@${target}`])
-            break
-          case "brew": {
-            const formula = yield* getBrewFormula()
-            const env = { HOMEBREW_NO_AUTO_UPDATE: "1" }
-            if (formula.includes("/")) {
-              const tap = yield* run(["brew", "tap", "anomalyco/tap"], { env })
-              if (tap.code !== 0) {
-                result = tap
-                break
-              }
-              const repo = yield* text(["brew", "--repo", "anomalyco/tap"])
-              const dir = repo.trim()
-              if (dir) {
-                const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env })
-                if (pull.code !== 0) {
-                  result = pull
+        }, Effect.orDie)
+
+        const upgradeImpl = Effect.fn("Installation.upgrade")(function* (m: Method, target: string) {
+          let result: { code: ChildProcessSpawner.ExitCode; stdout: string; stderr: string } | undefined
+          switch (m) {
+            case "curl":
+              result = yield* upgradeCurl(target)
+              break
+            case "npm":
+              result = yield* run(["npm", "install", "-g", `opencode-ai@${target}`])
+              break
+            case "pnpm":
+              result = yield* run(["pnpm", "install", "-g", `opencode-ai@${target}`])
+              break
+            case "bun":
+              result = yield* run(["bun", "install", "-g", `opencode-ai@${target}`])
+              break
+            case "brew": {
+              const formula = yield* getBrewFormula()
+              const env = { HOMEBREW_NO_AUTO_UPDATE: "1" }
+              if (formula.includes("/")) {
+                const tap = yield* run(["brew", "tap", "anomalyco/tap"], { env })
+                if (tap.code !== 0) {
+                  result = tap
                   break
                   break
                 }
                 }
+                const repo = yield* text(["brew", "--repo", "anomalyco/tap"])
+                const dir = repo.trim()
+                if (dir) {
+                  const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env })
+                  if (pull.code !== 0) {
+                    result = pull
+                    break
+                  }
+                }
               }
               }
+              result = yield* run(["brew", "upgrade", formula], { env })
+              break
             }
             }
-            result = yield* run(["brew", "upgrade", formula], { env })
-            break
+            case "choco":
+              result = yield* run(["choco", "upgrade", "opencode", `--version=${target}`, "-y"])
+              break
+            case "scoop":
+              result = yield* run(["scoop", "install", `opencode@${target}`])
+              break
+            default:
+              throw new Error(`Unknown method: ${m}`)
           }
           }
-          case "choco":
-            result = yield* run(["choco", "upgrade", "opencode", `--version=${target}`, "-y"])
-            break
-          case "scoop":
-            result = yield* run(["scoop", "install", `opencode@${target}`])
-            break
-          default:
-            throw new Error(`Unknown method: ${m}`)
-        }
-        if (!result || result.code !== 0) {
-          const stderr = m === "choco" ? "not running from an elevated command shell" : result?.stderr || ""
-          return yield* new UpgradeFailedError({ stderr })
-        }
-        log.info("upgraded", {
-          method: m,
-          target,
-          stdout: result.stdout,
-          stderr: result.stderr,
-        })
-        yield* text([process.execPath, "--version"])
-      })
-
-      return Service.of({
-        info: Effect.fn("Installation.info")(function* () {
-          return {
-            version: VERSION,
-            latest: yield* latestImpl(),
+          if (!result || result.code !== 0) {
+            const stderr = m === "choco" ? "not running from an elevated command shell" : result?.stderr || ""
+            return yield* new UpgradeFailedError({ stderr })
           }
           }
-        }),
-        method: methodImpl,
-        latest: latestImpl,
-        upgrade: upgradeImpl,
-      })
-    }),
-  )
+          log.info("upgraded", {
+            method: m,
+            target,
+            stdout: result.stdout,
+            stderr: result.stderr,
+          })
+          yield* text([process.execPath, "--version"])
+        })
+
+        return Service.of({
+          info: Effect.fn("Installation.info")(function* () {
+            return {
+              version: VERSION,
+              latest: yield* latestImpl(),
+            }
+          }),
+          method: methodImpl,
+          latest: latestImpl,
+          upgrade: upgradeImpl,
+        })
+      }),
+    )
 
 
   export const defaultLayer = layer.pipe(
   export const defaultLayer = layer.pipe(
     Layer.provide(FetchHttpClient.layer),
     Layer.provide(FetchHttpClient.layer),

+ 1 - 3
packages/opencode/test/account/service.test.ts

@@ -178,9 +178,7 @@ it.effect("config sends the selected org header", () =>
       }),
       }),
     )
     )
 
 
-    const cfg = yield* Account.Service.use((s) => s.config(id, OrgID.make("org-9"))).pipe(
-      Effect.provide(live(client)),
-    )
+    const cfg = yield* Account.Service.use((s) => s.config(id, OrgID.make("org-9"))).pipe(Effect.provide(live(client)))
 
 
     expect(Option.getOrThrow(cfg)).toEqual({ theme: "light", seats: 5 })
     expect(Option.getOrThrow(cfg)).toEqual({ theme: "light", seats: 5 })
     expect(seen).toEqual({
     expect(seen).toEqual({

+ 2 - 6
packages/opencode/test/installation/installation.test.ts

@@ -44,10 +44,7 @@ function testLayer(
   httpHandler: (request: HttpClientRequest.HttpClientRequest) => Response,
   httpHandler: (request: HttpClientRequest.HttpClientRequest) => Response,
   spawnHandler?: (cmd: string, args: readonly string[]) => string,
   spawnHandler?: (cmd: string, args: readonly string[]) => string,
 ) {
 ) {
-  return Installation.layer.pipe(
-    Layer.provide(mockHttpClient(httpHandler)),
-    Layer.provide(mockSpawner(spawnHandler)),
-  )
+  return Installation.layer.pipe(Layer.provide(mockHttpClient(httpHandler)), Layer.provide(mockSpawner(spawnHandler)))
 }
 }
 
 
 describe("installation", () => {
 describe("installation", () => {
@@ -139,8 +136,7 @@ describe("installation", () => {
       const layer = testLayer(
       const layer = testLayer(
         () => jsonResponse({}), // HTTP not used for tap formula
         () => jsonResponse({}), // HTTP not used for tap formula
         (cmd, args) => {
         (cmd, args) => {
-          if (cmd === "brew" && args.includes("anomalyco/tap/opencode") && args.includes("--formula"))
-            return "opencode"
+          if (cmd === "brew" && args.includes("anomalyco/tap/opencode") && args.includes("--formula")) return "opencode"
           if (cmd === "brew" && args.includes("--json=v2")) return brewInfoJson
           if (cmd === "brew" && args.includes("--json=v2")) return brewInfoJson
           return ""
           return ""
         },
         },