Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82b13ca184 |
@@ -29,14 +29,14 @@ export const PrCommand = effectCmd({
|
||||
UI.println(`Fetching and checking out PR #${prNumber}...`)
|
||||
|
||||
const checkout = yield* Effect.promise(() =>
|
||||
Process.run(["gh", "pr", "checkout", `${prNumber}`, "--branch", localBranchName, "--force"], { nothrow: true }),
|
||||
Process.runPromise(["gh", "pr", "checkout", `${prNumber}`, "--branch", localBranchName, "--force"], { nothrow: true }),
|
||||
)
|
||||
if (checkout.code !== 0) {
|
||||
return yield* fail(`Failed to checkout PR #${prNumber}. Make sure you have gh CLI installed and authenticated.`)
|
||||
}
|
||||
|
||||
const prInfoResult = yield* Effect.promise(() =>
|
||||
Process.text(
|
||||
Process.textPromise(
|
||||
[
|
||||
"gh",
|
||||
"pr",
|
||||
@@ -80,7 +80,7 @@ export const PrCommand = effectCmd({
|
||||
UI.println(`Importing session...`)
|
||||
|
||||
const importResult = yield* Effect.promise(() =>
|
||||
Process.text(["opencode", "import", sessionUrl], { nothrow: true }),
|
||||
Process.textPromise(["opencode", "import", sessionUrl], { nothrow: true }),
|
||||
)
|
||||
if (importResult.code === 0) {
|
||||
const sessionIdMatch = importResult.text.trim().match(/Imported session: ([a-zA-Z0-9_-]+)/)
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function read(): Promise<Content | undefined> {
|
||||
if (os === "darwin") {
|
||||
const tmpfile = path.join(tmpdir(), "opencode-clipboard.png")
|
||||
try {
|
||||
await Process.run(
|
||||
await Process.runPromise(
|
||||
[
|
||||
"osascript",
|
||||
"-e",
|
||||
@@ -79,7 +79,7 @@ export async function read(): Promise<Content | undefined> {
|
||||
if (os === "win32" || release().includes("WSL")) {
|
||||
const script =
|
||||
"Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [System.Convert]::ToBase64String($ms.ToArray()) }"
|
||||
const base64 = await Process.text(["powershell.exe", "-NonInteractive", "-NoProfile", "-command", script], {
|
||||
const base64 = await Process.textPromise(["powershell.exe", "-NonInteractive", "-NoProfile", "-command", script], {
|
||||
nothrow: true,
|
||||
})
|
||||
if (base64.text) {
|
||||
@@ -91,11 +91,11 @@ export async function read(): Promise<Content | undefined> {
|
||||
}
|
||||
|
||||
if (os === "linux") {
|
||||
const wayland = await Process.run(["wl-paste", "-t", "image/png"], { nothrow: true })
|
||||
const wayland = await Process.runPromise(["wl-paste", "-t", "image/png"], { nothrow: true })
|
||||
if (wayland.stdout.byteLength > 0) {
|
||||
return { data: Buffer.from(wayland.stdout).toString("base64"), mime: "image/png" }
|
||||
}
|
||||
const x11 = await Process.run(["xclip", "-selection", "clipboard", "-t", "image/png", "-o"], {
|
||||
const x11 = await Process.runPromise(["xclip", "-selection", "clipboard", "-t", "image/png", "-o"], {
|
||||
nothrow: true,
|
||||
})
|
||||
if (x11.stdout.byteLength > 0) {
|
||||
@@ -118,7 +118,7 @@ const getCopyMethod = lazy(async () => {
|
||||
console.log("clipboard: using osascript")
|
||||
return async (text: string) => {
|
||||
const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
||||
await Process.run(["osascript", "-e", `set the clipboard to "${escaped}"`], { nothrow: true })
|
||||
await Process.runPromise(["osascript", "-e", `set the clipboard to "${escaped}"`], { nothrow: true })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ async function executeUninstall(method: Installation.Method, targets: RemovalTar
|
||||
const cmd = cmds[method]
|
||||
if (cmd) {
|
||||
spinner.start(`Running ${cmd.join(" ")}...`)
|
||||
const result = await Process.run(method === "choco" ? ["choco", "uninstall", "opencode", "-y", "-r"] : cmd, {
|
||||
const result = await Process.runPromise(method === "choco" ? ["choco", "uninstall", "opencode", "-y", "-r"] : cmd, {
|
||||
nothrow: true,
|
||||
})
|
||||
if (result.code !== 0) {
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function readManagedPreferences() {
|
||||
for (const plist of paths) {
|
||||
if (!existsSync(plist)) continue
|
||||
log.info("reading macOS managed preferences", { path: plist })
|
||||
const result = await Process.run(["plutil", "-convert", "json", "-o", "-", plist], { nothrow: true })
|
||||
const result = await Process.runPromise(["plutil", "-convert", "json", "-o", "-", plist], { nothrow: true })
|
||||
if (result.code !== 0) {
|
||||
log.warn("failed to convert managed preferences plist", { path: plist })
|
||||
continue
|
||||
|
||||
@@ -221,7 +221,7 @@ export const rlang: Info = {
|
||||
const air = which("air")
|
||||
if (air == null) return false
|
||||
|
||||
const output = await Process.text([air, "--help"], { nothrow: true })
|
||||
const output = await Process.textPromise([air, "--help"], { nothrow: true })
|
||||
|
||||
// Check for "Air: An R language server and formatter"
|
||||
const firstLine = output.text.split("\n")[0]
|
||||
@@ -239,7 +239,7 @@ export const uvformat: Info = {
|
||||
if (await ruff.enabled(context)) return false
|
||||
const uv = which("uv")
|
||||
if (uv == null) return false
|
||||
const output = await Process.run([uv, "format", "--help"], { nothrow: true })
|
||||
const output = await Process.runPromise([uv, "format", "--help"], { nothrow: true })
|
||||
if (output.code === 0) return [uv, "format", "--", "$FILE"]
|
||||
return false
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ export async function install(ide: (typeof SUPPORTED_IDES)[number]["name"]) {
|
||||
const cmd = SUPPORTED_IDES.find((i) => i.name === ide)?.cmd
|
||||
if (!cmd) throw new Error(`Unknown IDE: ${ide}`)
|
||||
|
||||
const p = await Process.run([cmd, "--install-extension", "sst-dev.opencode"], {
|
||||
const p = await Process.runPromise([cmd, "--install-extension", "sst-dev.opencode"], {
|
||||
nothrow: true,
|
||||
})
|
||||
const stdout = p.stdout.toString()
|
||||
|
||||
@@ -5,6 +5,9 @@ import { Global } from "@opencode-ai/core/global"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { text } from "node:stream/consumers"
|
||||
import fs from "fs/promises"
|
||||
import { Effect, ManagedRuntime } from "effect"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { memoMap } from "@opencode-ai/core/effect/memo-map"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import type { InstanceContext } from "../project/instance"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
@@ -21,8 +24,17 @@ const pathExists = async (p: string) =>
|
||||
.stat(p)
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
const run = (cmd: string[], opts: Process.RunOptions = {}) => Process.run(cmd, { ...opts, nothrow: true })
|
||||
const output = (cmd: string[], opts: Process.RunOptions = {}) => Process.text(cmd, { ...opts, nothrow: true })
|
||||
|
||||
// Private runtime so the Effect-returning `Process.run` / `Process.text` can be
|
||||
// invoked from this file's promise-based spawn callbacks. The `LSP` service
|
||||
// layer in `lsp.ts` calls these spawn functions inside `Effect.promise(async
|
||||
// () => ...)`, so a re-entry point is needed. Sharing `memoMap` keeps a single
|
||||
// `ChildProcessSpawner` instance across the process.
|
||||
const processRuntime = ManagedRuntime.make(CrossSpawnSpawner.defaultLayer, { memoMap })
|
||||
const run = (cmd: string[], opts: Process.RunOptions = {}) =>
|
||||
processRuntime.runPromise(Process.run(cmd, { ...opts, nothrow: true }))
|
||||
const output = (cmd: string[], opts: Process.RunOptions = {}) =>
|
||||
processRuntime.runPromise(Process.text(cmd, { ...opts, nothrow: true }))
|
||||
|
||||
export interface Handle {
|
||||
process: ChildProcessWithoutNullStreams
|
||||
@@ -188,8 +200,12 @@ export const ESLint: Info = {
|
||||
await fs.rename(extractedPath, finalPath)
|
||||
|
||||
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm"
|
||||
await Process.run([npmCmd, "install"], { cwd: finalPath })
|
||||
await Process.run([npmCmd, "run", "compile"], { cwd: finalPath })
|
||||
await processRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
yield* Process.run([npmCmd, "install"], { cwd: finalPath })
|
||||
yield* Process.run([npmCmd, "run", "compile"], { cwd: finalPath })
|
||||
}),
|
||||
)
|
||||
|
||||
log.info("installed VS Code ESLint server", { serverPath })
|
||||
}
|
||||
@@ -570,9 +586,13 @@ export const ElixirLS: Info = {
|
||||
|
||||
const cwd = path.join(Global.Path.bin, "elixir-ls-master")
|
||||
const env = { MIX_ENV: "prod", ...process.env }
|
||||
await Process.run(["mix", "deps.get"], { cwd, env })
|
||||
await Process.run(["mix", "compile"], { cwd, env })
|
||||
await Process.run(["mix", "elixir_ls.release2", "-o", "release"], { cwd, env })
|
||||
await processRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
yield* Process.run(["mix", "deps.get"], { cwd, env })
|
||||
yield* Process.run(["mix", "compile"], { cwd, env })
|
||||
yield* Process.run(["mix", "elixir_ls.release2", "-o", "release"], { cwd, env })
|
||||
}),
|
||||
)
|
||||
|
||||
log.info(`installed elixir-ls`, {
|
||||
path: elixirLsPath,
|
||||
|
||||
@@ -1908,7 +1908,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
const sh = Shell.preferred(cfg.shell)
|
||||
const results = yield* Effect.promise(() =>
|
||||
Promise.all(
|
||||
shellMatches.map(async ([, cmd]) => (await Process.text([cmd], { shell: sh, nothrow: true })).text),
|
||||
shellMatches.map(async ([, cmd]) => (await Process.textPromise([cmd], { shell: sh, nothrow: true })).text),
|
||||
),
|
||||
)
|
||||
let index = 0
|
||||
|
||||
@@ -7,11 +7,11 @@ export async function extractZip(zipPath: string, destDir: string) {
|
||||
const winDestDir = path.resolve(destDir)
|
||||
// $global:ProgressPreference suppresses PowerShell's blue progress bar popup
|
||||
const cmd = `$global:ProgressPreference = 'SilentlyContinue'; Expand-Archive -Path '${winZipPath}' -DestinationPath '${winDestDir}' -Force`
|
||||
await Process.run(["powershell", "-NoProfile", "-NonInteractive", "-Command", cmd])
|
||||
await Process.runPromise(["powershell", "-NoProfile", "-NonInteractive", "-Command", cmd])
|
||||
return
|
||||
}
|
||||
|
||||
await Process.run(["unzip", "-o", "-q", zipPath, "-d", destDir])
|
||||
await Process.runPromise(["unzip", "-o", "-q", zipPath, "-d", destDir])
|
||||
}
|
||||
|
||||
export * as Archive from "./archive"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { type ChildProcess } from "child_process"
|
||||
import { type ChildProcess as NodeChildProcess } from "child_process"
|
||||
import launch from "cross-spawn"
|
||||
import { buffer } from "node:stream/consumers"
|
||||
import { Effect, Stream } from "effect"
|
||||
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
||||
import { errorMessage } from "./error"
|
||||
|
||||
export type Stdio = "inherit" | "pipe" | "ignore"
|
||||
@@ -53,7 +55,7 @@ export class RunFailedError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export type Child = ChildProcess & { exited: Promise<number> }
|
||||
export type Child = NodeChildProcess & { exited: Promise<number> }
|
||||
|
||||
export function spawn(cmd: string[], opts: Options = {}): Child {
|
||||
if (cmd.length === 0) throw new Error("Command is required")
|
||||
@@ -110,8 +112,104 @@ export function spawn(cmd: string[], opts: Options = {}): Child {
|
||||
return child
|
||||
}
|
||||
|
||||
export async function run(cmd: string[], opts: RunOptions = {}): Promise<Result> {
|
||||
const proc = spawn(cmd, {
|
||||
// Duplicated in `packages/sdk/js/src/process.ts` because the SDK cannot import
|
||||
// `opencode` without creating a cycle. Keep both copies in sync.
|
||||
export async function stop(proc: NodeChildProcess) {
|
||||
if (proc.exitCode !== null || proc.signalCode !== null) return
|
||||
|
||||
if (process.platform !== "win32" || !proc.pid) {
|
||||
proc.kill()
|
||||
return
|
||||
}
|
||||
|
||||
const out = await runPromise(["taskkill", "/pid", String(proc.pid), "/T", "/F"], {
|
||||
nothrow: true,
|
||||
})
|
||||
|
||||
if (out.code === 0) return
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
const mergeEnv = (env: NodeJS.ProcessEnv | null | undefined): { env: Record<string, string>; extendEnv: boolean } => {
|
||||
if (env === null) return { env: {}, extendEnv: false }
|
||||
if (env === undefined) return { env: {}, extendEnv: true }
|
||||
const out: Record<string, string> = {}
|
||||
for (const [k, v] of Object.entries(env)) {
|
||||
if (v !== undefined) out[k] = v
|
||||
}
|
||||
return { env: out, extendEnv: true }
|
||||
}
|
||||
|
||||
export const run = Effect.fn("Process.run")(function* (cmd: string[], opts: RunOptions = {}) {
|
||||
if (cmd.length === 0) return yield* Effect.die(new Error("Command is required"))
|
||||
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
||||
const { env, extendEnv } = mergeEnv(opts.env)
|
||||
|
||||
const result = yield* Effect.scoped(
|
||||
Effect.gen(function* () {
|
||||
const proc = ChildProcess.make(cmd[0], cmd.slice(1), {
|
||||
cwd: opts.cwd,
|
||||
env,
|
||||
extendEnv,
|
||||
shell: opts.shell,
|
||||
stdin: opts.stdin ?? "ignore",
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
})
|
||||
const handle = yield* spawner.spawn(proc)
|
||||
const [stdoutBytes, stderrBytes, exitCode] = yield* Effect.all(
|
||||
[Stream.mkUint8Array(handle.stdout), Stream.mkUint8Array(handle.stderr), handle.exitCode],
|
||||
{ concurrency: 3 },
|
||||
)
|
||||
return {
|
||||
code: exitCode as number,
|
||||
stdout: Buffer.from(stdoutBytes),
|
||||
stderr: Buffer.from(stderrBytes),
|
||||
} satisfies Result
|
||||
}),
|
||||
).pipe(
|
||||
Effect.catch((err) =>
|
||||
opts.nothrow
|
||||
? Effect.succeed({
|
||||
code: 1,
|
||||
stdout: Buffer.alloc(0),
|
||||
stderr: Buffer.from(errorMessage(err)),
|
||||
} satisfies Result)
|
||||
: Effect.die(err),
|
||||
),
|
||||
)
|
||||
|
||||
if (result.code === 0 || opts.nothrow) return result
|
||||
return yield* Effect.die(new RunFailedError(cmd, result.code, result.stdout, result.stderr))
|
||||
})
|
||||
|
||||
export const text = Effect.fn("Process.text")(function* (cmd: string[], opts: RunOptions = {}) {
|
||||
const out = yield* run(cmd, opts)
|
||||
return {
|
||||
...out,
|
||||
text: out.stdout.toString(),
|
||||
} satisfies TextResult
|
||||
})
|
||||
|
||||
export const lines = Effect.fn("Process.lines")(function* (cmd: string[], opts: RunOptions = {}) {
|
||||
const out = yield* text(cmd, opts)
|
||||
return out.text.split(/\r?\n/).filter(Boolean)
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Promise-returning facades for legacy non-Effect callers.
|
||||
//
|
||||
// The new `run` / `text` / `lines` exports above return Effects. These
|
||||
// wrappers preserve the original Promise-based shape (failing with
|
||||
// `RunFailedError` on non-zero exit, etc.) and the legacy AbortSignal /
|
||||
// timeout semantics by using `spawn(...)` directly.
|
||||
//
|
||||
// New code should yield the Effect versions. These wrappers exist only to
|
||||
// avoid touching the remaining non-Effect call sites in this PR.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function runPromise(cmd: string[], opts: RunOptions = {}): Promise<Result> {
|
||||
const spawnOpts = {
|
||||
cwd: opts.cwd,
|
||||
env: opts.env,
|
||||
stdin: opts.stdin,
|
||||
@@ -119,13 +217,16 @@ export async function run(cmd: string[], opts: RunOptions = {}): Promise<Result>
|
||||
abort: opts.abort,
|
||||
kill: opts.kill,
|
||||
timeout: opts.timeout,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
})
|
||||
stdout: "pipe" as const,
|
||||
stderr: "pipe" as const,
|
||||
}
|
||||
|
||||
if (!proc.stdout || !proc.stderr) throw new Error("Process output not available")
|
||||
// Preserve the legacy abort/timeout semantics by using `spawn(...)` directly
|
||||
// rather than the Effect path (which lacks AbortSignal hooks today).
|
||||
const proc = spawn(cmd, spawnOpts)
|
||||
if (!proc.stdout || !proc.stderr) return Promise.reject(new Error("Process output not available"))
|
||||
|
||||
const out = await Promise.all([proc.exited, buffer(proc.stdout), buffer(proc.stderr)])
|
||||
return Promise.all([proc.exited, buffer(proc.stdout), buffer(proc.stderr)])
|
||||
.then(([code, stdout, stderr]) => ({
|
||||
code,
|
||||
stdout,
|
||||
@@ -137,40 +238,24 @@ export async function run(cmd: string[], opts: RunOptions = {}): Promise<Result>
|
||||
code: 1,
|
||||
stdout: Buffer.alloc(0),
|
||||
stderr: Buffer.from(errorMessage(err)),
|
||||
}
|
||||
} satisfies Result
|
||||
})
|
||||
.then((out) => {
|
||||
if (out.code === 0 || opts.nothrow) return out
|
||||
throw new RunFailedError(cmd, out.code, out.stdout, out.stderr)
|
||||
})
|
||||
if (out.code === 0 || opts.nothrow) return out
|
||||
throw new RunFailedError(cmd, out.code, out.stdout, out.stderr)
|
||||
}
|
||||
|
||||
// Duplicated in `packages/sdk/js/src/process.ts` because the SDK cannot import
|
||||
// `opencode` without creating a cycle. Keep both copies in sync.
|
||||
export async function stop(proc: ChildProcess) {
|
||||
if (proc.exitCode !== null || proc.signalCode !== null) return
|
||||
|
||||
if (process.platform !== "win32" || !proc.pid) {
|
||||
proc.kill()
|
||||
return
|
||||
}
|
||||
|
||||
const out = await run(["taskkill", "/pid", String(proc.pid), "/T", "/F"], {
|
||||
nothrow: true,
|
||||
})
|
||||
|
||||
if (out.code === 0) return
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
export async function text(cmd: string[], opts: RunOptions = {}): Promise<TextResult> {
|
||||
const out = await run(cmd, opts)
|
||||
export async function textPromise(cmd: string[], opts: RunOptions = {}): Promise<TextResult> {
|
||||
const out = await runPromise(cmd, opts)
|
||||
return {
|
||||
...out,
|
||||
text: out.stdout.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
export async function lines(cmd: string[], opts: RunOptions = {}): Promise<string[]> {
|
||||
return (await text(cmd, opts)).text.split(/\r?\n/).filter(Boolean)
|
||||
export async function linesPromise(cmd: string[], opts: RunOptions = {}): Promise<string[]> {
|
||||
return (await textPromise(cmd, opts)).text.split(/\r?\n/).filter(Boolean)
|
||||
}
|
||||
|
||||
export * as Process from "./process"
|
||||
|
||||
@@ -17,7 +17,7 @@ type Msg = {
|
||||
}
|
||||
|
||||
function run(msg: Msg) {
|
||||
return Process.run([process.execPath, worker, JSON.stringify(msg)], {
|
||||
return Process.runPromise([process.execPath, worker, JSON.stringify(msg)], {
|
||||
cwd: root,
|
||||
nothrow: true,
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ const root = path.join(import.meta.dir, "../..")
|
||||
const worker = path.join(import.meta.dir, "../fixture/plugin-meta-worker.ts")
|
||||
|
||||
function run(input: { file: string; spec: string; target: string; id: string }) {
|
||||
return Process.run([process.execPath, worker, JSON.stringify(input)], {
|
||||
return Process.runPromise([process.execPath, worker, JSON.stringify(input)], {
|
||||
cwd: root,
|
||||
nothrow: true,
|
||||
})
|
||||
|
||||
@@ -224,7 +224,7 @@ describe("Truncate", () => {
|
||||
)
|
||||
|
||||
test("loads truncate effect in a fresh process", async () => {
|
||||
const out = await Process.run([process.execPath, "run", path.join(ROOT, "src", "tool", "truncate.ts")], {
|
||||
const out = await Process.runPromise([process.execPath, "run", path.join(ROOT, "src", "tool", "truncate.ts")], {
|
||||
cwd: ROOT,
|
||||
})
|
||||
|
||||
|
||||
@@ -10,19 +10,19 @@ function node(script: string) {
|
||||
|
||||
describe("util.process", () => {
|
||||
test("captures stdout and stderr", async () => {
|
||||
const out = await Process.run(node('process.stdout.write("out");process.stderr.write("err")'))
|
||||
const out = await Process.runPromise(node('process.stdout.write("out");process.stderr.write("err")'))
|
||||
expect(out.code).toBe(0)
|
||||
expect(out.stdout.toString()).toBe("out")
|
||||
expect(out.stderr.toString()).toBe("err")
|
||||
})
|
||||
|
||||
test("returns code when nothrow is enabled", async () => {
|
||||
const out = await Process.run(node("process.exit(7)"), { nothrow: true })
|
||||
const out = await Process.runPromise(node("process.exit(7)"), { nothrow: true })
|
||||
expect(out.code).toBe(7)
|
||||
})
|
||||
|
||||
test("throws RunFailedError on non-zero exit", async () => {
|
||||
const err = await Process.run(node('process.stderr.write("bad");process.exit(3)')).catch((error) => error)
|
||||
const err = await Process.runPromise(node('process.stderr.write("bad");process.exit(3)')).catch((error) => error)
|
||||
expect(err).toBeInstanceOf(Process.RunFailedError)
|
||||
if (!(err instanceof Process.RunFailedError)) throw err
|
||||
expect(err.code).toBe(3)
|
||||
@@ -34,7 +34,7 @@ describe("util.process", () => {
|
||||
const started = Date.now()
|
||||
setTimeout(() => abort.abort(), 25)
|
||||
|
||||
const out = await Process.run(node("setInterval(() => {}, 1000)"), {
|
||||
const out = await Process.runPromise(node("setInterval(() => {}, 1000)"), {
|
||||
abort: abort.signal,
|
||||
nothrow: true,
|
||||
})
|
||||
@@ -50,7 +50,7 @@ describe("util.process", () => {
|
||||
const started = Date.now()
|
||||
setTimeout(() => abort.abort(), 25)
|
||||
|
||||
const out = await Process.run(node('process.on("SIGTERM", () => {}); setInterval(() => {}, 1000)'), {
|
||||
const out = await Process.runPromise(node('process.on("SIGTERM", () => {}); setInterval(() => {}, 1000)'), {
|
||||
abort: abort.signal,
|
||||
nothrow: true,
|
||||
timeout: 25,
|
||||
@@ -62,14 +62,14 @@ describe("util.process", () => {
|
||||
|
||||
test("uses cwd when spawning commands", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
const out = await Process.run(node("process.stdout.write(process.cwd())"), {
|
||||
const out = await Process.runPromise(node("process.stdout.write(process.cwd())"), {
|
||||
cwd: tmp.path,
|
||||
})
|
||||
expect(out.stdout.toString()).toBe(tmp.path)
|
||||
})
|
||||
|
||||
test("merges environment overrides", async () => {
|
||||
const out = await Process.run(node('process.stdout.write(process.env.OPENCODE_TEST ?? "")'), {
|
||||
const out = await Process.runPromise(node('process.stdout.write(process.env.OPENCODE_TEST ?? "")'), {
|
||||
env: {
|
||||
OPENCODE_TEST: "set",
|
||||
},
|
||||
@@ -80,7 +80,7 @@ describe("util.process", () => {
|
||||
test("uses shell in run on Windows", async () => {
|
||||
if (process.platform !== "win32") return
|
||||
|
||||
const out = await Process.run(["set", "OPENCODE_TEST_SHELL"], {
|
||||
const out = await Process.runPromise(["set", "OPENCODE_TEST_SHELL"], {
|
||||
shell: true,
|
||||
env: {
|
||||
OPENCODE_TEST_SHELL: "ok",
|
||||
|
||||
Reference in New Issue
Block a user