From 2d36a79d936b2eaddbfe492312182766e5bbfe90 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 11 Oct 2025 03:50:40 -0400 Subject: [PATCH] thread --- bun.lock | 16 ++++--- packages/opencode/package.json | 2 + packages/opencode/script/build.ts | 14 ++++++- .../opencode/src/cli/cmd/tui/tui-thread.ts | 42 +++++++++++++++++++ packages/opencode/src/cli/cmd/tui/tui.ts | 2 +- packages/opencode/src/cli/cmd/tui/worker.ts | 2 +- packages/opencode/src/index.ts | 2 + 7 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 packages/opencode/src/cli/cmd/tui/tui-thread.ts diff --git a/bun.lock b/bun.lock index 5b3a24313..fa5a523ce 100644 --- a/bun.lock +++ b/bun.lock @@ -205,10 +205,12 @@ "devDependencies": { "@ai-sdk/amazon-bedrock": "2.2.10", "@ai-sdk/google-vertex": "3.0.16", + "@babel/core": "7.28.4", "@octokit/webhooks-types": "7.6.1", "@parcel/watcher-win32-x64": "2.5.1", "@standard-schema/spec": "1.0.0", "@tsconfig/bun": "1.0.7", + "@types/babel__core": "7.20.5", "@types/bun": "catalog:", "@types/turndown": "5.0.5", "@types/yargs": "17.0.33", @@ -402,7 +404,7 @@ "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.782.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA=="], - "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], @@ -3248,8 +3250,6 @@ "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - "@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -3258,10 +3258,6 @@ "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - - "@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@cloudflare/kv-asset-handler/mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], "@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.21", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="], @@ -3404,8 +3400,12 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@tanstack/directive-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@tanstack/router-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@vercel/nft/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -3882,8 +3882,6 @@ "@opencode-ai/web/shiki/@shikijs/types": ["@shikijs/types@3.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="], - "@opentui/solid/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@solidjs/start/shiki/@shikijs/core": ["@shikijs/core@1.29.2", "", { "dependencies": { "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index bdd8511a3..2b66bb9ad 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -20,10 +20,12 @@ "devDependencies": { "@ai-sdk/amazon-bedrock": "2.2.10", "@ai-sdk/google-vertex": "3.0.16", + "@babel/core": "7.28.4", "@octokit/webhooks-types": "7.6.1", "@parcel/watcher-win32-x64": "2.5.1", "@standard-schema/spec": "1.0.0", "@tsconfig/bun": "1.0.7", + "@types/babel__core": "7.20.5", "@types/bun": "catalog:", "@types/turndown": "5.0.5", "@types/yargs": "17.0.33", diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 3f4409da3..a5edbeb3a 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -9,7 +9,9 @@ process.chdir(dir) import pkg from "../package.json" -const targets = [ +const singleFlag = process.argv.includes("--single") + +const allTargets = [ ["windows", "x64"], ["linux", "arm64"], ["linux", "x64"], @@ -19,6 +21,10 @@ const targets = [ ["darwin", "arm64"], ] +const targets = singleFlag + ? allTargets.filter(([os, arch]) => os === process.platform && arch === process.arch) + : allTargets + await $`rm -rf dist` const binaries: Record = {} @@ -48,7 +54,11 @@ for (const [os, arch] of targets) { execArgv: [`--user-agent=opencode/${version}`, `--env-file=""`, `--`], windows: {}, }, - entrypoints: ["./src/index.ts", path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js")], + entrypoints: [ + "./src/index.ts", + path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js"), + "./src/cli/cmd/tui/worker.ts", + ], define: { OPENCODE_VERSION: `'${version}'`, OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/../../node_modules/@opentui/core/parser.worker.js", diff --git a/packages/opencode/src/cli/cmd/tui/tui-thread.ts b/packages/opencode/src/cli/cmd/tui/tui-thread.ts new file mode 100644 index 000000000..24e9189c6 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/tui-thread.ts @@ -0,0 +1,42 @@ +import { cmd } from "@/cli/cmd/cmd" +import { tui } from "./app" + +export const TuiThreadCommand = cmd({ + command: "thread [project]", + describe: "start opencode tui threaded", + builder: (yargs) => + yargs + .positional("project", { + type: "string", + describe: "path to start opencode in", + }) + .option("port", { + type: "number", + describe: "port to listen on", + default: 0, + }) + .option("hostname", { + alias: ["h"], + type: "string", + describe: "hostname to listen on", + default: "127.0.0.1", + }), + handler: async () => { + const worker = new Worker("./src/cli/cmd/tui/worker.ts") + worker.onerror = console.error + const server = await new Promise((resolve) => { + worker.onmessage = async (evt) => { + resolve(JSON.parse(evt.data)) + } + }) + await tui({ + url: server.url, + onExit: async () => { + await new Promise((resolve) => { + worker.onmessage = resolve + worker.postMessage(JSON.stringify({ type: "shutdown" })) + }) + }, + }) + }, +}) diff --git a/packages/opencode/src/cli/cmd/tui/tui.ts b/packages/opencode/src/cli/cmd/tui/tui.ts index 7f8f24170..02a97c6cb 100644 --- a/packages/opencode/src/cli/cmd/tui/tui.ts +++ b/packages/opencode/src/cli/cmd/tui/tui.ts @@ -54,7 +54,7 @@ export const TuiCommand = cmd({ const code = proc.exitCode if (code === 0) break } - await server.stop(true) await Instance.disposeAll() + await server.stop(true) }, }) diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts index eb1bbb40a..36b2222d8 100644 --- a/packages/opencode/src/cli/cmd/tui/worker.ts +++ b/packages/opencode/src/cli/cmd/tui/worker.ts @@ -23,7 +23,7 @@ onmessage = async (evt) => { const parsed = JSON.parse(evt.data) if (parsed.type === "shutdown") { await Instance.disposeAll() - server.stop() + await server.stop(true) postMessage(JSON.stringify({ type: "shutdown.complete" })) } } diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 1444247ab..632f2e48e 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -19,6 +19,7 @@ import { GithubCommand } from "./cli/cmd/github" import { ExportCommand } from "./cli/cmd/export" import { TuiCommand } from "./cli/cmd/tui/tui" import { AttachCommand } from "./cli/cmd/tui/attach" +import { TuiThreadCommand } from "./cli/cmd/tui/tui-thread" const cancel = new AbortController() @@ -68,6 +69,7 @@ const cli = yargs(hideBin(process.argv)) }) .usage("\n" + UI.logo()) .command(McpCommand) + .command(TuiThreadCommand) .command(TuiCommand) .command(AttachCommand) .command(RunCommand)