fix(headless): align openwrk TUI compile path with OpenTUI + branch CI (#536)

* fix(headless): align openwrk compile path with OpenTUI Solid

Use the upstream @opentui/solid build plugin and preload/tsconfig wiring so compiled openwrk binaries keep TUI runtime compatibility instead of emitting broken React runtime behavior.

* ci: run openwrk checks on feature branch pushes

Trigger CI for fix/openwrk-react-shim and add an openwrk binary build job so bundling regressions are caught before merging to dev.

* ci: relax binary validation to runtime smoke

Use openwrk --version/--help checks instead of grepping embedded strings so the branch CI validates bundling without false failures from defensive error text.

* fix(desktop): build openwrk sidecar via headless build script

Route desktop sidecar preparation through packages/headless/script/build.ts so OpenTUI plugin settings are applied consistently and Linux CI no longer compiles openwrk with incompatible JSX defaults.
This commit is contained in:
ben
2026-02-10 23:39:38 -08:00
committed by GitHub
parent e9bda43b0a
commit 86938ece00
9 changed files with 79 additions and 171 deletions

View File

@@ -2,9 +2,12 @@ name: OpenWork Tests
on:
pull_request:
branches:
- dev
push:
branches:
- dev
- fix/openwrk-react-shim
permissions:
contents: read

View File

@@ -4,6 +4,10 @@ on:
push:
branches:
- dev
- fix/openwrk-react-shim
pull_request:
branches:
- dev
permissions:
contents: read
@@ -32,3 +36,40 @@ jobs:
- name: Build web
run: pnpm --filter @different-ai/openwork build:web
build-openwrk-binary:
name: Build openwrk binary
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.27.0
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.3.9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck openwrk
run: pnpm --filter openwrk typecheck
- name: Build openwrk binary
run: pnpm --filter openwrk build:bin
- name: Validate compiled binary
run: |
./packages/headless/dist/bin/openwrk --version
./packages/headless/dist/bin/openwrk --help >/dev/null

View File

@@ -567,28 +567,17 @@ if (shouldBuildOpenwrk) {
// ignore
}
}
// openwrk uses bun build --compile directly
const openwrkCliPath = resolve(openwrkDir, "src", "cli.ts");
if (!existsSync(openwrkCliPath)) {
console.error(`Openwrk CLI source not found at ${openwrkCliPath}`);
const openwrkBuildScript = resolveBuildScript(openwrkDir);
if (!existsSync(openwrkBuildScript)) {
console.error(`Openwrk build script not found at ${openwrkBuildScript}`);
process.exit(1);
}
const openwrkVersionForDefine = (() => {
try {
const raw = readFileSync(resolve(openwrkDir, "package.json"), "utf8");
return String(JSON.parse(raw).version ?? "").trim();
} catch {
return "";
}
})();
const openwrkArgs = [
"build",
"--compile",
openwrkCliPath,
"--define",
`__OPENWRK_VERSION__=\"${openwrkVersionForDefine}\"`,
"--outfile",
openwrkBuildPath,
openwrkBuildScript,
"--outdir",
sidecarDir,
"--filename",
"openwrk",
];
if (bunTarget) {
openwrkArgs.push("--target", bunTarget);

View File

@@ -9,7 +9,7 @@
"build": "tsc -p tsconfig.json",
"build:bin": "node scripts/clean-dist.mjs && bun ./script/build.ts --outdir dist/bin --filename openwrk",
"build:bin:all": "node scripts/clean-dist.mjs && bun ./script/build.ts --outdir dist/bin --filename openwrk --target bun-darwin-arm64 --target bun-darwin-x64 --target bun-linux-x64 --target bun-linux-arm64 --target bun-windows-x64",
"build:bin:bundled": "node scripts/clean-dist.mjs && node ../desktop/scripts/prepare-sidecar.mjs --outdir dist && bun build --compile src/cli.ts --plugin ./script/opentui-solid-plugin.mjs --define __OPENWRK_VERSION__=\\\"$npm_package_version\\\" --outfile dist/openwrk && bun scripts/build-bin.ts",
"build:bin:bundled": "node scripts/clean-dist.mjs && node ../desktop/scripts/prepare-sidecar.mjs --outdir dist && bun ./script/build.ts --outdir dist --filename openwrk && bun scripts/build-bin.ts",
"build:sidecars": "node scripts/build-sidecars.mjs",
"typecheck": "tsc -p tsconfig.typecheck.json",
"test:router": "pnpm build && node scripts/router.mjs",
@@ -51,15 +51,12 @@
"solid-js": "1.9.9"
},
"devDependencies": {
"@babel/core": "7.28.6",
"@babel/preset-typescript": "7.28.5",
"@opentui/core-darwin-arm64": "0.1.77",
"@opentui/core-darwin-x64": "0.1.77",
"@opentui/core-linux-arm64": "0.1.77",
"@opentui/core-linux-x64": "0.1.77",
"@opentui/core-win32-x64": "0.1.77",
"@types/node": "^22.10.2",
"babel-preset-solid": "1.9.9",
"bun-types": "^1.3.6",
"typescript": "^5.6.3"
},

View File

@@ -1,6 +1,6 @@
import { spawnSync } from "node:child_process";
import { mkdirSync, readFileSync } from "node:fs";
import { join, resolve } from "node:path";
import solidPlugin from "../node_modules/@opentui/solid/scripts/solid-plugin";
const bunRuntime = (globalThis as typeof globalThis & {
Bun?: {
@@ -88,29 +88,41 @@ function outputName(filename: string, target?: string) {
return `${filename}${suffix}${ext}`;
}
function defaultTarget(): string {
const os = process.platform === "win32" ? "windows" : process.platform;
return `bun-${os}-${process.arch}`;
}
async function buildOnce(entrypoint: string, outdir: string, filename: string, target?: string) {
mkdirSync(outdir, { recursive: true });
const outfile = join(outdir, outputName(filename, target));
const solidPluginPath = resolve("script", "opentui-solid-plugin.mjs");
const args = ["build", entrypoint, "--compile", "--plugin", solidPluginPath, "--outfile", outfile];
const define: Record<string, string> = {};
const pkgPath = resolve("package.json");
try {
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { version?: string };
if (typeof pkg.version === "string" && pkg.version.trim()) {
args.push("--define", `__OPENWRK_VERSION__=\"${pkg.version.trim()}\"`);
define.__OPENWRK_VERSION__ = `\"${pkg.version.trim()}\"`;
}
} catch {
// ignore
}
if (target) {
args.push("--target", target);
}
const result = spawnSync("bun", args, { stdio: "inherit" });
if (result.status !== 0) {
process.exit(result.status ?? 1);
const resolvedTarget = target ?? defaultTarget();
const result = await bun.build({
tsconfig: "./tsconfig.json",
plugins: [solidPlugin],
entrypoints: [entrypoint],
define,
compile: {
target: resolvedTarget,
outfile,
},
});
if (!result.success) {
for (const log of result.logs) {
console.error(log);
}
process.exit(1);
}
}

View File

@@ -1,46 +0,0 @@
import { transformAsync } from "@babel/core";
import ts from "@babel/preset-typescript";
import solid from "babel-preset-solid";
const openTuiSolidPlugin = {
name: "openwrk-opentui-solid",
setup(build) {
build.onLoad({ filter: /\/node_modules\/solid-js\/dist\/server\.js$/ }, async (args) => {
const path = args.path.replace("server.js", "solid.js");
const file = Bun.file(path);
const code = await file.text();
return { contents: code, loader: "js" };
});
build.onLoad({ filter: /\/node_modules\/solid-js\/store\/dist\/server\.js$/ }, async (args) => {
const path = args.path.replace("server.js", "store.js");
const file = Bun.file(path);
const code = await file.text();
return { contents: code, loader: "js" };
});
build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
const file = Bun.file(args.path);
const code = await file.text();
const transformed = await transformAsync(code, {
filename: args.path,
presets: [
[
solid,
{
moduleName: "@opentui/solid",
generate: "universal",
},
],
[ts],
],
});
return {
contents: transformed?.code ?? "",
loader: "js",
};
});
},
};
export default openTuiSolidPlugin;

View File

@@ -1,17 +1,8 @@
import { RGBA, TextAttributes, type InputRenderable, type KeyEvent, type TabSelectRenderable } from "@opentui/core";
import {
createElement as openTuiCreateElement,
render,
useKeyboard,
useRenderer,
useSelectionHandler,
useTerminalDimensions,
} from "@opentui/solid";
import { render, useKeyboard, useRenderer, useSelectionHandler, useTerminalDimensions } from "@opentui/solid";
import { For, Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js";
import { createStore } from "solid-js/store";
const React = { createElement: openTuiCreateElement };
export type TuiLogLevel = "debug" | "info" | "warn" | "error";
export type TuiServiceStatus = "starting" | "running" | "healthy" | "stopped" | "disabled" | "error";

View File

@@ -4,7 +4,7 @@
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "preserve",
"jsxImportSource": "solid-js",
"jsxImportSource": "@opentui/solid",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"outDir": "dist",
"rootDir": "src",

79
pnpm-lock.yaml generated
View File

@@ -119,12 +119,6 @@ importers:
specifier: 1.9.9
version: 1.9.9
devDependencies:
'@babel/core':
specifier: 7.28.6
version: 7.28.6
'@babel/preset-typescript':
specifier: 7.28.5
version: 7.28.5(@babel/core@7.28.6)
'@opentui/core-darwin-arm64':
specifier: 0.1.77
version: 0.1.77
@@ -143,9 +137,6 @@ importers:
'@types/node':
specifier: ^22.10.2
version: 22.19.7
babel-preset-solid:
specifier: 1.9.9
version: 1.9.9(@babel/core@7.28.6)(solid-js@1.9.9)
bun-types:
specifier: ^1.3.6
version: 1.3.6
@@ -381,12 +372,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/preset-typescript@7.28.5':
resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/template@7.28.6':
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
engines: {node: '>=6.9.0'}
@@ -2636,19 +2621,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-annotate-as-pure': 7.27.3
'@babel/helper-member-expression-to-functions': 7.28.5
'@babel/helper-optimise-call-expression': 7.27.1
'@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.6)
'@babel/helper-skip-transparent-expression-wrappers': 7.27.1
'@babel/traverse': 7.28.6
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/helper-globals@7.28.0': {}
'@babel/helper-member-expression-to-functions@7.28.5':
@@ -2702,15 +2674,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/helper-replace-supers@7.28.6(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-member-expression-to-functions': 7.28.5
'@babel/helper-optimise-call-expression': 7.27.1
'@babel/traverse': 7.28.6
transitivePeerDependencies:
- supports-color
'@babel/helper-skip-transparent-expression-wrappers@7.27.1':
dependencies:
'@babel/traverse': 7.28.6
@@ -2748,11 +2711,6 @@ snapshots:
'@babel/core': 7.28.0
'@babel/helper-plugin-utils': 7.28.6
'@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-plugin-utils': 7.28.6
'@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
@@ -2761,14 +2719,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6)
'@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
'@babel/plugin-transform-typescript@7.28.6(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
@@ -2780,17 +2730,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/plugin-transform-typescript@7.28.6(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-annotate-as-pure': 7.27.3
'@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.6)
'@babel/helper-plugin-utils': 7.28.6
'@babel/helper-skip-transparent-expression-wrappers': 7.27.1
'@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.6)
transitivePeerDependencies:
- supports-color
'@babel/preset-typescript@7.27.1(@babel/core@7.28.0)':
dependencies:
'@babel/core': 7.28.0
@@ -2802,17 +2741,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/preset-typescript@7.28.5(@babel/core@7.28.6)':
dependencies:
'@babel/core': 7.28.6
'@babel/helper-plugin-utils': 7.28.6
'@babel/helper-validator-option': 7.27.1
'@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6)
'@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.6)
'@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.28.6)
transitivePeerDependencies:
- supports-color
'@babel/template@7.28.6':
dependencies:
'@babel/code-frame': 7.28.6
@@ -3718,13 +3646,6 @@ snapshots:
optionalDependencies:
solid-js: 1.9.10
babel-preset-solid@1.9.9(@babel/core@7.28.6)(solid-js@1.9.9):
dependencies:
'@babel/core': 7.28.6
babel-plugin-jsx-dom-expressions: 0.40.3(@babel/core@7.28.6)
optionalDependencies:
solid-js: 1.9.9
balanced-match@1.0.2: {}
base64-js@1.5.1: {}