mirror of
https://github.com/anomalyco/opencode.git
synced 2026-03-11 09:04:26 +00:00
Compare commits
1 Commits
fix/interr
...
node-build
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c80e2662c |
18
bun.lock
18
bun.lock
@@ -324,6 +324,8 @@
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@gitlab/gitlab-ai-provider": "3.6.0",
|
||||
"@gitlab/opencode-gitlab-auth": "1.3.3",
|
||||
"@hono/node-server": "1.19.11",
|
||||
"@hono/node-ws": "1.3.0",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "1.25.2",
|
||||
@@ -366,6 +368,7 @@
|
||||
"opentui-spinner": "0.0.6",
|
||||
"partial-json": "0.1.7",
|
||||
"remeda": "catalog:",
|
||||
"semver": "^7.6.3",
|
||||
"solid-js": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
"tree-sitter-bash": "0.25.0",
|
||||
@@ -395,6 +398,7 @@
|
||||
"@types/babel__core": "7.20.5",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/turndown": "5.0.5",
|
||||
"@types/which": "3.0.4",
|
||||
"@types/yargs": "17.0.33",
|
||||
@@ -423,8 +427,12 @@
|
||||
},
|
||||
"packages/script": {
|
||||
"name": "@opencode-ai/script",
|
||||
"dependencies": {
|
||||
"semver": "^7.6.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:",
|
||||
"@types/semver": "^7.5.8",
|
||||
},
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
@@ -1104,7 +1112,9 @@
|
||||
|
||||
"@hey-api/types": ["@hey-api/types@0.1.2", "", {}, "sha512-uNNtiVAWL7XNrV/tFXx7GLY9lwaaDazx1173cGW3+UEaw4RUPsHEmiB4DSpcjNxMIcrctfz2sGKLnVx5PBG2RA=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
|
||||
"@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
|
||||
|
||||
"@hono/node-ws": ["@hono/node-ws@1.3.0", "", { "dependencies": { "ws": "^8.17.0" }, "peerDependencies": { "@hono/node-server": "^1.19.2", "hono": "^4.6.0" } }, "sha512-ju25YbbvLuXdqBCmLZLqnNYu1nbHIQjoyUqA8ApZOeL1k4skuiTcw5SW77/5SUYo2Xi2NVBJoVlfQurnKEp03Q=="],
|
||||
|
||||
"@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
|
||||
|
||||
@@ -2110,6 +2120,8 @@
|
||||
|
||||
"@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="],
|
||||
|
||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
||||
|
||||
"@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="],
|
||||
|
||||
"@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="],
|
||||
@@ -5028,6 +5040,8 @@
|
||||
|
||||
"@hey-api/openapi-ts/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
|
||||
"@hono/node-ws/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
||||
|
||||
"@hono/zod-validator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"@jimp/plugin-blit/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
@@ -5076,6 +5090,8 @@
|
||||
|
||||
"@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
# Bun shell migration plan
|
||||
|
||||
Practical phased replacement of Bun `$` calls.
|
||||
|
||||
## Goal
|
||||
|
||||
Replace runtime Bun shell template-tag usage in `packages/opencode/src` with a unified `Process` API in `util/process.ts`.
|
||||
|
||||
Keep behavior stable while improving safety, testability, and observability.
|
||||
|
||||
Current baseline from audit:
|
||||
|
||||
- 143 runtime command invocations across 17 files
|
||||
- 84 are git commands
|
||||
- Largest hotspots:
|
||||
- `src/cli/cmd/github.ts` (33)
|
||||
- `src/worktree/index.ts` (22)
|
||||
- `src/lsp/server.ts` (21)
|
||||
- `src/installation/index.ts` (20)
|
||||
- `src/snapshot/index.ts` (18)
|
||||
|
||||
## Decisions
|
||||
|
||||
- Extend `src/util/process.ts` (do not create a separate exec module).
|
||||
- Proceed with phased migration for both git and non-git paths.
|
||||
- Keep plugin `$` compatibility in 1.x and remove in 2.0.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Do not remove plugin `$` compatibility in this effort.
|
||||
- Do not redesign command semantics beyond what is needed to preserve behavior.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Keep migration phased, not big-bang.
|
||||
- Minimize behavioral drift.
|
||||
- Keep these explicit shell-only exceptions:
|
||||
- `src/session/prompt.ts` raw command execution
|
||||
- worktree start scripts in `src/worktree/index.ts`
|
||||
|
||||
## Process API proposal (`src/util/process.ts`)
|
||||
|
||||
Add higher-level wrappers on top of current spawn support.
|
||||
|
||||
Core methods:
|
||||
|
||||
- `Process.run(cmd, opts)`
|
||||
- `Process.text(cmd, opts)`
|
||||
- `Process.lines(cmd, opts)`
|
||||
- `Process.status(cmd, opts)`
|
||||
- `Process.shell(command, opts)` for intentional shell execution
|
||||
|
||||
Git helpers:
|
||||
|
||||
- `Process.git(args, opts)`
|
||||
- `Process.gitText(args, opts)`
|
||||
|
||||
Shared options:
|
||||
|
||||
- `cwd`, `env`, `stdin`, `stdout`, `stderr`, `abort`, `timeout`, `kill`
|
||||
- `allowFailure` / non-throw mode
|
||||
- optional redaction + trace metadata
|
||||
|
||||
Standard result shape:
|
||||
|
||||
- `code`, `stdout`, `stderr`, `duration_ms`, `cmd`
|
||||
- helpers like `text()` and `arrayBuffer()` where useful
|
||||
|
||||
## Phased rollout
|
||||
|
||||
### Phase 0: Foundation
|
||||
|
||||
- Implement Process wrappers in `src/util/process.ts`.
|
||||
- Refactor `src/util/git.ts` to use Process only.
|
||||
- Add tests for exit handling, timeout, abort, and output capture.
|
||||
|
||||
### Phase 1: High-impact hotspots
|
||||
|
||||
Migrate these first:
|
||||
|
||||
- `src/cli/cmd/github.ts`
|
||||
- `src/worktree/index.ts`
|
||||
- `src/lsp/server.ts`
|
||||
- `src/installation/index.ts`
|
||||
- `src/snapshot/index.ts`
|
||||
|
||||
Within each file, migrate git paths first where applicable.
|
||||
|
||||
### Phase 2: Remaining git-heavy files
|
||||
|
||||
Migrate git-centric call sites to `Process.git*` helpers:
|
||||
|
||||
- `src/file/index.ts`
|
||||
- `src/project/vcs.ts`
|
||||
- `src/file/watcher.ts`
|
||||
- `src/storage/storage.ts`
|
||||
- `src/cli/cmd/pr.ts`
|
||||
|
||||
### Phase 3: Remaining non-git files
|
||||
|
||||
Migrate residual non-git usages:
|
||||
|
||||
- `src/cli/cmd/tui/util/clipboard.ts`
|
||||
- `src/util/archive.ts`
|
||||
- `src/file/ripgrep.ts`
|
||||
- `src/tool/bash.ts`
|
||||
- `src/cli/cmd/uninstall.ts`
|
||||
|
||||
### Phase 4: Stabilize
|
||||
|
||||
- Remove dead wrappers and one-off patterns.
|
||||
- Keep plugin `$` compatibility isolated and documented as temporary.
|
||||
- Create linked 2.0 task for plugin `$` removal.
|
||||
|
||||
## Validation strategy
|
||||
|
||||
- Unit tests for new `Process` methods and options.
|
||||
- Integration tests on hotspot modules.
|
||||
- Smoke tests for install, snapshot, worktree, and GitHub flows.
|
||||
- Regression checks for output parsing behavior.
|
||||
|
||||
## Risk mitigation
|
||||
|
||||
- File-by-file PRs with small diffs.
|
||||
- Preserve behavior first, simplify second.
|
||||
- Keep shell-only exceptions explicit and documented.
|
||||
- Add consistent error shaping and logging at Process layer.
|
||||
|
||||
## Definition of done
|
||||
|
||||
- Runtime Bun `$` usage in `packages/opencode/src` is removed except:
|
||||
- approved shell-only exceptions
|
||||
- temporary plugin compatibility path (1.x)
|
||||
- Git paths use `Process.git*` consistently.
|
||||
- CI and targeted smoke tests pass.
|
||||
- 2.0 issue exists for plugin `$` removal.
|
||||
@@ -81,6 +81,8 @@
|
||||
"@gitlab/gitlab-ai-provider": "3.6.0",
|
||||
"@gitlab/opencode-gitlab-auth": "1.3.3",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
"@hono/node-server": "1.19.11",
|
||||
"@hono/node-ws": "1.3.0",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "1.25.2",
|
||||
"@octokit/graphql": "9.0.2",
|
||||
|
||||
8
packages/opencode/script/build-node.ts
Executable file
8
packages/opencode/script/build-node.ts
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
Bun.build({
|
||||
entrypoints: ["./src/node.ts"],
|
||||
target: "node",
|
||||
outdir: "./dist",
|
||||
format: "esm",
|
||||
})
|
||||
1
packages/opencode/src/node.ts
Normal file
1
packages/opencode/src/node.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { Server } from "./server/server"
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Hono } from "hono"
|
||||
import { describeRoute, validator, resolver } from "hono-openapi"
|
||||
import { upgradeWebSocket } from "hono/bun"
|
||||
import z from "zod"
|
||||
import { Pty } from "@/pty"
|
||||
import { NotFoundError } from "../../storage/db"
|
||||
import { errors } from "../error"
|
||||
import { lazy } from "../../util/lazy"
|
||||
import { Server } from "../server"
|
||||
|
||||
export const PtyRoutes = lazy(() =>
|
||||
new Hono()
|
||||
@@ -149,7 +149,7 @@ export const PtyRoutes = lazy(() =>
|
||||
},
|
||||
}),
|
||||
validator("param", z.object({ ptyID: z.string() })),
|
||||
upgradeWebSocket((c) => {
|
||||
Server.upgradeWebSocket((c) => {
|
||||
const id = c.req.param("ptyID")
|
||||
const cursor = (() => {
|
||||
const value = c.req.query("cursor")
|
||||
|
||||
@@ -35,7 +35,8 @@ import { lazy } from "../util/lazy"
|
||||
import { InstanceBootstrap } from "../project/bootstrap"
|
||||
import { NotFoundError } from "../storage/db"
|
||||
import type { ContentfulStatusCode } from "hono/utils/http-status"
|
||||
import { websocket } from "hono/bun"
|
||||
import { createAdaptorServer } from "@hono/node-server"
|
||||
import { createNodeWebSocket } from "@hono/node-ws"
|
||||
import { HTTPException } from "hono/http-exception"
|
||||
import { errors } from "./error"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
@@ -58,6 +59,8 @@ export namespace Server {
|
||||
}
|
||||
|
||||
const app = new Hono()
|
||||
const ws = createNodeWebSocket({ app })
|
||||
export const upgradeWebSocket = ws.upgradeWebSocket
|
||||
export const App: () => Hono = lazy(
|
||||
() =>
|
||||
// TODO: Break server.ts into smaller route files to fix type inference
|
||||
@@ -246,7 +249,7 @@ export namespace Server {
|
||||
),
|
||||
)
|
||||
.route("/project", ProjectRoutes())
|
||||
.route("/pty", PtyRoutes())
|
||||
// .route("/pty", PtyRoutes())
|
||||
.route("/config", ConfigRoutes())
|
||||
.route("/experimental", ExperimentalRoutes())
|
||||
.route("/session", SessionRoutes())
|
||||
@@ -594,7 +597,7 @@ export namespace Server {
|
||||
return result
|
||||
}
|
||||
|
||||
export function listen(opts: {
|
||||
export async function listen(opts: {
|
||||
port: number
|
||||
hostname: string
|
||||
mdns?: boolean
|
||||
@@ -603,42 +606,83 @@ export namespace Server {
|
||||
}) {
|
||||
_corsWhitelist = opts.cors ?? []
|
||||
|
||||
const args = {
|
||||
hostname: opts.hostname,
|
||||
idleTimeout: 0,
|
||||
fetch: App().fetch,
|
||||
websocket: websocket,
|
||||
} as const
|
||||
const tryServe = (port: number) => {
|
||||
try {
|
||||
return Bun.serve({ ...args, port })
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
const start = async (port: number) => {
|
||||
const server = createAdaptorServer(App())
|
||||
ws.injectWebSocket(server)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const fail = (err: Error) => {
|
||||
cleanup()
|
||||
reject(err)
|
||||
}
|
||||
const ready = () => {
|
||||
cleanup()
|
||||
resolve()
|
||||
}
|
||||
const cleanup = () => {
|
||||
server.off("error", fail)
|
||||
server.off("listening", ready)
|
||||
}
|
||||
server.once("error", fail)
|
||||
server.once("listening", ready)
|
||||
server.listen(port, opts.hostname)
|
||||
})
|
||||
return server
|
||||
}
|
||||
const server = opts.port === 0 ? (tryServe(4096) ?? tryServe(0)) : tryServe(opts.port)
|
||||
if (!server) throw new Error(`Failed to start server on port ${opts.port}`)
|
||||
|
||||
_url = server.url
|
||||
const server = await (async () => {
|
||||
if (opts.port !== 0) return start(opts.port)
|
||||
try {
|
||||
return await start(4096)
|
||||
} catch {
|
||||
return start(0)
|
||||
}
|
||||
})()
|
||||
|
||||
const addr = server.address()
|
||||
if (!addr || typeof addr === "string") {
|
||||
throw new Error(`Failed to resolve server address for port ${opts.port}`)
|
||||
}
|
||||
|
||||
const url = new URL("http://localhost")
|
||||
url.hostname = opts.hostname
|
||||
url.port = String(addr.port)
|
||||
_url = url
|
||||
|
||||
const shouldPublishMDNS =
|
||||
opts.mdns &&
|
||||
server.port &&
|
||||
addr.port &&
|
||||
opts.hostname !== "127.0.0.1" &&
|
||||
opts.hostname !== "localhost" &&
|
||||
opts.hostname !== "::1"
|
||||
if (shouldPublishMDNS) {
|
||||
MDNS.publish(server.port!, opts.mdnsDomain)
|
||||
MDNS.publish(addr.port, opts.mdnsDomain)
|
||||
} else if (opts.mdns) {
|
||||
log.warn("mDNS enabled but hostname is loopback; skipping mDNS publish")
|
||||
}
|
||||
|
||||
const originalStop = server.stop.bind(server)
|
||||
server.stop = async (closeActiveConnections?: boolean) => {
|
||||
if (shouldPublishMDNS) MDNS.unpublish()
|
||||
return originalStop(closeActiveConnections)
|
||||
let closing: Promise<void> | undefined
|
||||
return {
|
||||
hostname: opts.hostname,
|
||||
port: addr.port,
|
||||
url,
|
||||
stop(close?: boolean) {
|
||||
closing ??= new Promise<void>((resolve, reject) => {
|
||||
if (shouldPublishMDNS) MDNS.unpublish()
|
||||
server.close((err?: Error) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
if (close) {
|
||||
const node = server as { closeAllConnections?: () => void; closeIdleConnections?: () => void }
|
||||
node.closeAllConnections?.()
|
||||
node.closeIdleConnections?.()
|
||||
}
|
||||
})
|
||||
return closing
|
||||
},
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,8 @@ import { Snapshot } from "@/snapshot"
|
||||
import { fn } from "@/util/fn"
|
||||
import { Database, eq, desc, inArray } from "@/storage/db"
|
||||
import { MessageTable, PartTable } from "./session.sql"
|
||||
import { ProviderTransform } from "@/provider/transform"
|
||||
import { STATUS_CODES } from "http"
|
||||
import { Storage } from "@/storage/storage"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
import { iife } from "@/util/iife"
|
||||
import { type SystemError } from "bun"
|
||||
import type { Provider } from "@/provider/provider"
|
||||
|
||||
export namespace MessageV2 {
|
||||
@@ -843,15 +839,15 @@ export namespace MessageV2 {
|
||||
},
|
||||
{ cause: e },
|
||||
).toObject()
|
||||
case (e as SystemError)?.code === "ECONNRESET":
|
||||
case (e as any)?.code === "ECONNRESET":
|
||||
return new MessageV2.APIError(
|
||||
{
|
||||
message: "Connection reset by server",
|
||||
isRetryable: true,
|
||||
metadata: {
|
||||
code: (e as SystemError).code ?? "",
|
||||
syscall: (e as SystemError).syscall ?? "",
|
||||
message: (e as SystemError).message ?? "",
|
||||
code: (e as any).code ?? "",
|
||||
syscall: (e as any).syscall ?? "",
|
||||
message: (e as any).message ?? "",
|
||||
},
|
||||
},
|
||||
{ cause: e },
|
||||
|
||||
@@ -29,9 +29,11 @@ import { ReadTool } from "../tool/read"
|
||||
import { FileTime } from "../file/time"
|
||||
import { Flag } from "../flag/flag"
|
||||
import { ulid } from "ulid"
|
||||
import { Process } from "../util/process"
|
||||
import { spawn } from "child_process"
|
||||
|
||||
import { Command } from "../command"
|
||||
import { $ } from "bun"
|
||||
|
||||
import { pathToFileURL, fileURLToPath } from "url"
|
||||
import { ConfigMarkdown } from "../config/markdown"
|
||||
import { SessionSummary } from "./summary"
|
||||
@@ -1778,15 +1780,13 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||
template = template + "\n\n" + input.arguments
|
||||
}
|
||||
|
||||
const shell = ConfigMarkdown.shell(template)
|
||||
if (shell.length > 0) {
|
||||
const shellMatches = ConfigMarkdown.shell(template)
|
||||
if (shellMatches.length > 0) {
|
||||
const sh = Shell.preferred()
|
||||
const results = await Promise.all(
|
||||
shell.map(async ([, cmd]) => {
|
||||
try {
|
||||
return await $`${{ raw: cmd }}`.quiet().nothrow().text()
|
||||
} catch (error) {
|
||||
return `Error executing command: ${error instanceof Error ? error.message : String(error)}`
|
||||
}
|
||||
shellMatches.map(async ([, cmd]) => {
|
||||
const out = await Process.text([cmd], { shell: sh, nothrow: true })
|
||||
return out.text
|
||||
}),
|
||||
)
|
||||
let index = 0
|
||||
|
||||
@@ -13,6 +13,7 @@ export namespace Process {
|
||||
abort?: AbortSignal
|
||||
kill?: NodeJS.Signals | number
|
||||
timeout?: number
|
||||
shell?: string | boolean
|
||||
}
|
||||
|
||||
export interface RunOptions extends Omit<Options, "stdout" | "stderr"> {
|
||||
@@ -59,6 +60,7 @@ export namespace Process {
|
||||
const proc = launch(cmd[0], cmd.slice(1), {
|
||||
cwd: opts.cwd,
|
||||
env: opts.env === null ? {} : opts.env ? { ...process.env, ...opts.env } : undefined,
|
||||
shell: opts.shell,
|
||||
stdio: [opts.stdin ?? "ignore", opts.stdout ?? "ignore", opts.stderr ?? "ignore"],
|
||||
})
|
||||
|
||||
@@ -108,6 +110,7 @@ export namespace Process {
|
||||
const proc = spawn(cmd, {
|
||||
cwd: opts.cwd,
|
||||
env: opts.env,
|
||||
shell: opts.shell,
|
||||
stdin: opts.stdin,
|
||||
abort: opts.abort,
|
||||
kill: opts.kill,
|
||||
|
||||
Reference in New Issue
Block a user