Merge branch 'node-pty' into pr-18335
commit
282ab0f67d
16
bun.lock
16
bun.lock
|
|
@ -372,6 +372,7 @@
|
|||
"jsonc-parser": "3.3.1",
|
||||
"mime-types": "3.0.2",
|
||||
"minimatch": "10.0.3",
|
||||
"node-pty": "1.1.0",
|
||||
"open": "10.1.2",
|
||||
"opencode-gitlab-auth": "2.0.0",
|
||||
"opencode-poe-auth": "0.0.1",
|
||||
|
|
@ -586,8 +587,9 @@
|
|||
},
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"electron",
|
||||
"esbuild",
|
||||
"node-pty",
|
||||
"electron",
|
||||
"web-tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
],
|
||||
|
|
@ -2161,7 +2163,7 @@
|
|||
|
||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
||||
|
||||
"@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
|
||||
"@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=="],
|
||||
|
||||
|
|
@ -3747,6 +3749,8 @@
|
|||
|
||||
"node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
|
||||
|
||||
"node-pty": ["node-pty@1.1.0", "", { "dependencies": { "node-addon-api": "^7.1.0" } }, "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="],
|
||||
|
||||
"nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="],
|
||||
|
|
@ -5229,8 +5233,6 @@
|
|||
|
||||
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
|
||||
|
||||
"@openrouter/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
|
||||
|
||||
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
|
||||
|
|
@ -5325,6 +5327,8 @@
|
|||
|
||||
"@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
||||
|
||||
"@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="],
|
||||
|
||||
"@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||
|
||||
"@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
|
||||
|
|
@ -5659,6 +5663,8 @@
|
|||
|
||||
"storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||
|
||||
"storybook/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
|
||||
|
||||
"storybook-solidjs-vite/vite-plugin-solid": ["vite-plugin-solid@2.11.11", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-YMZCXsLw9kyuvQFEdwLP27fuTQJLmjNoHy90AOJnbRuJ6DwShUxKFo38gdFrWn9v11hnGicKCZEaeI/TFs6JKw=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
|
@ -6317,8 +6323,6 @@
|
|||
|
||||
"js-beautify/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"lazystream/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
|
||||
"dev:storybook": "bun --cwd packages/storybook storybook",
|
||||
"typecheck": "bun turbo typecheck",
|
||||
"postinstall": "bun run --cwd packages/opencode fix-node-pty",
|
||||
"prepare": "husky",
|
||||
"random": "echo 'Random script'",
|
||||
"hello": "echo 'Hello World!'",
|
||||
|
|
@ -100,6 +101,7 @@
|
|||
},
|
||||
"trustedDependencies": [
|
||||
"esbuild",
|
||||
"node-pty",
|
||||
"protobufjs",
|
||||
"tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"typecheck": "tsgo --noEmit",
|
||||
"test": "bun test --timeout 30000",
|
||||
"build": "bun run script/build.ts",
|
||||
"fix-node-pty": "bun run script/fix-node-pty.ts",
|
||||
"dev": "bun run --conditions=browser ./src/index.ts",
|
||||
"random": "echo 'Random script updated at $(date)' && echo 'Change queued successfully' && echo 'Another change made' && echo 'Yet another change' && echo 'One more change' && echo 'Final change' && echo 'Another final change' && echo 'Yet another final change'",
|
||||
"clean": "echo 'Cleaning up...' && rm -rf node_modules dist",
|
||||
|
|
@ -31,6 +32,11 @@
|
|||
"bun": "./src/storage/db.bun.ts",
|
||||
"node": "./src/storage/db.node.ts",
|
||||
"default": "./src/storage/db.bun.ts"
|
||||
},
|
||||
"#pty": {
|
||||
"bun": "./src/pty/pty.bun.ts",
|
||||
"node": "./src/pty/pty.node.ts",
|
||||
"default": "./src/pty/pty.bun.ts"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -135,6 +141,7 @@
|
|||
"jsonc-parser": "3.3.1",
|
||||
"mime-types": "3.0.2",
|
||||
"minimatch": "10.0.3",
|
||||
"node-pty": "1.1.0",
|
||||
"open": "10.1.2",
|
||||
"opencode-gitlab-auth": "2.0.0",
|
||||
"opencode-poe-auth": "0.0.1",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ await Bun.build({
|
|||
entrypoints: ["./src/node.ts"],
|
||||
outdir: "./dist",
|
||||
format: "esm",
|
||||
external: ["jsonc-parser"],
|
||||
external: ["jsonc-parser", "node-pty"],
|
||||
define: {
|
||||
OPENCODE_MIGRATIONS: JSON.stringify(migrations),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const dir = path.resolve(__dirname, "..")
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
const root = path.join(dir, "node_modules", "node-pty", "prebuilds")
|
||||
const dirs = await fs.readdir(root, { withFileTypes: true }).catch(() => [])
|
||||
const files = dirs.filter((x) => x.isDirectory()).map((x) => path.join(root, x.name, "spawn-helper"))
|
||||
const result = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
const stat = await fs.stat(file).catch(() => undefined)
|
||||
if (!stat) return
|
||||
if ((stat.mode & 0o111) === 0o111) return
|
||||
await fs.chmod(file, stat.mode | 0o755)
|
||||
return file
|
||||
}),
|
||||
)
|
||||
const fixed = result.filter(Boolean)
|
||||
if (fixed.length) {
|
||||
console.log(`fixed node-pty permissions for ${fixed.length} helper${fixed.length === 1 ? "" : "s"}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { Bus } from "@/bus"
|
|||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { makeRuntime } from "@/effect/run-service"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { type IPty } from "bun-pty"
|
||||
import type { Proc } from "#pty"
|
||||
import z from "zod"
|
||||
import { Log } from "../util/log"
|
||||
import { lazy } from "@opencode-ai/util/lazy"
|
||||
|
|
@ -30,7 +30,7 @@ export namespace Pty {
|
|||
|
||||
type Active = {
|
||||
info: Info
|
||||
process: IPty
|
||||
process: Proc
|
||||
buffer: string
|
||||
bufferCursor: number
|
||||
cursor: number
|
||||
|
|
@ -52,10 +52,7 @@ export namespace Pty {
|
|||
return out
|
||||
}
|
||||
|
||||
const pty = lazy(async () => {
|
||||
const { spawn } = await import("bun-pty")
|
||||
return spawn
|
||||
})
|
||||
const pty = lazy(() => import("#pty"))
|
||||
|
||||
export const Info = z
|
||||
.object({
|
||||
|
|
@ -199,7 +196,7 @@ export namespace Pty {
|
|||
}
|
||||
log.info("creating session", { id, cmd: command, args, cwd })
|
||||
|
||||
const spawn = await pty()
|
||||
const { spawn } = await pty()
|
||||
const proc = spawn(command, args, {
|
||||
name: "xterm-256color",
|
||||
cwd,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import { spawn as create } from "bun-pty"
|
||||
import type { Opts, Proc } from "./pty"
|
||||
|
||||
export type { Disp, Exit, Opts, Proc } from "./pty"
|
||||
|
||||
export function spawn(file: string, args: string[], opts: Opts): Proc {
|
||||
const pty = create(file, args, opts)
|
||||
return {
|
||||
pid: pty.pid,
|
||||
onData(listener) {
|
||||
return pty.onData(listener)
|
||||
},
|
||||
onExit(listener) {
|
||||
return pty.onExit(listener)
|
||||
},
|
||||
write(data) {
|
||||
pty.write(data)
|
||||
},
|
||||
resize(cols, rows) {
|
||||
pty.resize(cols, rows)
|
||||
},
|
||||
kill(signal) {
|
||||
pty.kill(signal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import * as pty from "node-pty"
|
||||
import type { Opts, Proc } from "./pty"
|
||||
|
||||
export type { Disp, Exit, Opts, Proc } from "./pty"
|
||||
|
||||
export function spawn(file: string, args: string[], opts: Opts): Proc {
|
||||
const proc = pty.spawn(file, args, opts)
|
||||
return {
|
||||
pid: proc.pid,
|
||||
onData(listener) {
|
||||
return proc.onData(listener)
|
||||
},
|
||||
onExit(listener) {
|
||||
return proc.onExit(listener)
|
||||
},
|
||||
write(data) {
|
||||
proc.write(data)
|
||||
},
|
||||
resize(cols, rows) {
|
||||
proc.resize(cols, rows)
|
||||
},
|
||||
kill(signal) {
|
||||
proc.kill(signal)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
export type Disp = {
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
export type Exit = {
|
||||
exitCode: number
|
||||
signal?: number | string
|
||||
}
|
||||
|
||||
export type Opts = {
|
||||
name: string
|
||||
cols?: number
|
||||
rows?: number
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
}
|
||||
|
||||
export type Proc = {
|
||||
pid: number
|
||||
onData(listener: (data: string) => void): Disp
|
||||
onExit(listener: (event: Exit) => void): Disp
|
||||
write(data: string): void
|
||||
resize(cols: number, rows: number): void
|
||||
kill(signal?: string): void
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import { ProviderID } from "../provider/schema"
|
|||
import { WorkspaceRouterMiddleware } from "../control-plane/workspace-router-middleware"
|
||||
import { ProjectRoutes } from "./routes/project"
|
||||
import { SessionRoutes } from "./routes/session"
|
||||
// import { PtyRoutes } from "./routes/pty"
|
||||
import { PtyRoutes } from "./routes/pty"
|
||||
import { McpRoutes } from "./routes/mcp"
|
||||
import { FileRoutes } from "./routes/file"
|
||||
import { ConfigRoutes } from "./routes/config"
|
||||
|
|
@ -524,6 +524,7 @@ export namespace Server {
|
|||
return c.json(await Format.status())
|
||||
},
|
||||
)
|
||||
.route("/pty", PtyRoutes(ws.upgradeWebSocket))
|
||||
.all("/*", async (c) => {
|
||||
const embeddedWebUI = await embeddedUIPromise
|
||||
const path = c.req.path
|
||||
|
|
|
|||
Loading…
Reference in New Issue