From 1311adfa3ffadfa50b08852b8f25dd4f47c1ecf6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 4 Jun 2026 00:43:49 -0400 Subject: [PATCH] feat(cli): wire preview npm publishing --- .github/workflows/publish.yml | 11 ++++ packages/cli/bin/lildax.cjs | 97 ++++++++++++++++++++++++++++++++++ packages/cli/package.json | 6 ++- packages/cli/script/build.ts | 27 +++++++--- packages/cli/script/publish.ts | 48 +++++++++++++++++ script/publish.ts | 3 ++ 6 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 packages/cli/bin/lildax.cjs create mode 100644 packages/cli/script/publish.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9887cbe4dc6..dc198f0d1f6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -90,6 +90,7 @@ jobs: id: build run: | ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} + ./packages/cli/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} env: OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_RELEASE: ${{ needs.version.outputs.release }} @@ -107,6 +108,11 @@ jobs: with: name: opencode-cli-windows path: packages/opencode/dist/opencode-windows* + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: opencode-preview-cli + path: packages/cli/dist/lildax-* outputs: version: ${{ needs.version.outputs.version }} @@ -446,6 +452,11 @@ jobs: name: opencode-cli-signed-windows path: packages/opencode/dist + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: opencode-preview-cli + path: packages/cli/dist + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 if: needs.version.outputs.release with: diff --git a/packages/cli/bin/lildax.cjs b/packages/cli/bin/lildax.cjs new file mode 100644 index 00000000000..24b6c36660c --- /dev/null +++ b/packages/cli/bin/lildax.cjs @@ -0,0 +1,97 @@ +#!/usr/bin/env node + +const childProcess = require("child_process") +const fs = require("fs") +const path = require("path") +const os = require("os") + +const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"] + +function run(target) { + const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" }) + child.on("error", (error) => { + console.error(error.message) + process.exit(1) + }) + const forwarders = {} + for (const signal of forwardedSignals) { + forwarders[signal] = () => { + try { + child.kill(signal) + } catch {} + } + process.on(signal, forwarders[signal]) + } + child.on("exit", (code, signal) => { + for (const forwardedSignal of forwardedSignals) process.removeListener(forwardedSignal, forwarders[forwardedSignal]) + if (signal) return process.kill(process.pid, signal) + process.exit(typeof code === "number" ? code : 0) + }) +} + +const envPath = process.env.OPENCODE_BIN_PATH +const scriptDir = path.dirname(fs.realpathSync(__filename)) +const cached = path.join(scriptDir, ".lildax") +const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] || os.platform() +const arch = { x64: "x64", arm64: "arm64", arm: "arm" }[os.arch()] || os.arch() +const base = "@opencode-ai/lildax-" + platform + "-" + arch +const binary = platform === "windows" ? "lildax.exe" : "lildax" + +function supportsAvx2() { + if (arch !== "x64") return false + if (platform === "linux") { + try { + return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8")) + } catch { + return false + } + } + if (platform === "darwin") { + try { + const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8", timeout: 1500 }) + return result.status === 0 && (result.stdout || "").trim() === "1" + } catch { + return false + } + } + return false +} + +const names = (() => { + const baseline = arch === "x64" && !supportsAvx2() + if (platform === "linux") { + const musl = (() => { + try { + if (fs.existsSync("/etc/alpine-release")) return true + const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" }) + return ((result.stdout || "") + (result.stderr || "")).toLowerCase().includes("musl") + } catch { + return false + } + })() + if (musl) return arch === "x64" ? (baseline ? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base] : [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]) : [`${base}-musl`, base] + return arch === "x64" ? (baseline ? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`] : [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]) : [base, `${base}-musl`] + } + return arch === "x64" ? (baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]) : [base] +})() + +function findBinary(startDir) { + let current = startDir + for (;;) { + const modules = path.join(current, "node_modules") + if (fs.existsSync(modules)) for (const name of names) { + const candidate = path.join(modules, name, "bin", binary) + if (fs.existsSync(candidate)) return candidate + } + const parent = path.dirname(current) + if (parent === current) return + current = parent + } +} + +const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir)) +if (!resolved) { + console.error("It seems that your package manager failed to install the right lildax CLI package. Try manually installing " + names.map((name) => `"${name}"`).join(" or ") + " package") + process.exit(1) +} +run(resolved) diff --git a/packages/cli/package.json b/packages/cli/package.json index bcf1418fb5d..6f6cf7f1cba 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -2,12 +2,14 @@ "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/cli", "version": "1.15.13", - "private": true, "type": "module", "license": "MIT", "bin": { - "lildax": "./src/index.ts" + "lildax": "./bin/lildax.cjs" }, + "files": [ + "bin" + ], "scripts": { "build": "bun run script/build.ts", "dev": "bun run src/index.ts", diff --git a/packages/cli/script/build.ts b/packages/cli/script/build.ts index 7b16318ef3f..f91653c2655 100644 --- a/packages/cli/script/build.ts +++ b/packages/cli/script/build.ts @@ -1,11 +1,9 @@ #!/usr/bin/env bun -import { $ } from "bun" import { rm } from "fs/promises" import path from "path" import { Script } from "@opencode-ai/script" import { modelsData } from "./generate" -import pkg from "../package.json" const dir = path.resolve(import.meta.dirname, "..") const binary = "lildax" @@ -15,7 +13,6 @@ await rm("dist", { recursive: true, force: true }) const singleFlag = process.argv.includes("--single") const baselineFlag = process.argv.includes("--baseline") -const skipInstall = process.argv.includes("--skip-install") const sourcemapsFlag = process.argv.includes("--sourcemaps") const allTargets: { @@ -46,8 +43,6 @@ const targets = singleFlag }) : allTargets -if (!skipInstall) await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}` - for (const item of targets) { const name = [ binary, @@ -86,7 +81,23 @@ for (const item of targets) { }, }) - if (result.success) continue - for (const log of result.logs) console.error(log) - process.exit(1) + if (!result.success) { + for (const log of result.logs) console.error(log) + process.exit(1) + } + + await Bun.write( + `./dist/${name}/package.json`, + JSON.stringify( + { + name: `@opencode-ai/${name}`, + version: Script.version, + license: "MIT", + os: [item.os], + cpu: [item.arch], + }, + null, + 2, + ), + ) } diff --git a/packages/cli/script/publish.ts b/packages/cli/script/publish.ts new file mode 100644 index 00000000000..ef9d65cd983 --- /dev/null +++ b/packages/cli/script/publish.ts @@ -0,0 +1,48 @@ +#!/usr/bin/env bun +import { $ } from "bun" +import pkg from "../package.json" +import { Script } from "@opencode-ai/script" +import { fileURLToPath } from "url" + +const dir = fileURLToPath(new URL("..", import.meta.url)) +process.chdir(dir) + +async function published(name: string, version: string) { + return (await $`npm view ${name}@${version} version`.nothrow()).exitCode === 0 +} + +async function publish(dir: string, name: string, version: string) { + if (process.platform !== "win32") await $`chmod -R 755 .`.cwd(dir) + if (await published(name, version)) return console.log(`already published ${name}@${version}`) + await $`npm pack`.cwd(dir) + await $`npm publish *.tgz --access public --tag ${Script.channel}`.cwd(dir) +} + +const binaries: Record = {} +for (const filepath of new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })) { + const item = await Bun.file(`./dist/${filepath}`).json() + binaries[item.name] = item.version +} +console.log("binaries", binaries) +const version = Object.values(binaries)[0] + +await $`mkdir -p ./dist/${pkg.name}/bin` +await $`cp ./bin/lildax.cjs ./dist/${pkg.name}/bin/lildax` +await Bun.file(`./dist/${pkg.name}/package.json`).write( + JSON.stringify( + { + name: pkg.name, + bin: { lildax: "./bin/lildax" }, + version, + license: pkg.license, + os: ["darwin", "linux", "win32"], + cpu: ["arm64", "x64"], + optionalDependencies: binaries, + }, + null, + 2, + ), +) + +await Promise.all(Object.entries(binaries).map(([name, version]) => publish(`./dist/${name.replace("@opencode-ai/", "")}`, name, version))) +await publish(`./dist/${pkg.name}`, pkg.name, version) diff --git a/script/publish.ts b/script/publish.ts index 7e91eef762d..3b01a9d9d78 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -45,6 +45,9 @@ await prepareReleaseFiles() console.log("\n=== cli ===\n") await $`bun ./packages/opencode/script/publish.ts` +console.log("\n=== preview cli ===\n") +await $`bun ./packages/cli/script/publish.ts` + console.log("\n=== sdk ===\n") await $`bun ./packages/sdk/js/script/publish.ts`