The first public release of LLeMbas CLI: a terminal coding agent and project manager for any LLM API, with permission modes, git snapshots, memory and skills, knowledge bases, MCP, voice, and a link to a LLeMbas instance whose web UI can work its sessions too. Signed Linux binaries for x64 and arm64. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
355 files changed
+47028
No files matched your search
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bun
|
||||
// The files a Release carries: one binary per supported platform, gzipped (100 MB → 40), the
|
||||
// installer, get.sh, SHA256SUMS over them all, and SHA256SUMS.sig — the release key's signature,
|
||||
// which get.sh and the updater check. Run from a clean checkout at the release tag:
|
||||
// bun run dist -- --sign ~/.ssh/projecthor-release → dist/release/
|
||||
// (The path is where the release key is kept on the machine that signs; it is the key built in as
|
||||
// RELEASE_KEYS, and it signs in the namespace lembas-release.)
|
||||
// (--sign KEY with a key of your own for your own builds; then set update.public_key to its .pub.)
|
||||
// The arm64 build needs OpenTUI's arm64 library: bun install --frozen-lockfile --os=linux --cpu=arm64
|
||||
import { createHash } from "node:crypto"
|
||||
import { copyFileSync, mkdirSync, readFileSync, rmSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import pkg from "../package.json"
|
||||
import { RELEASE_KEYS, SIG_NAMESPACE } from "../src/update/index.ts"
|
||||
import { verifySshSig } from "../src/update/sshsig.ts"
|
||||
|
||||
export const TARGETS = ["linux-x64", "linux-x64-baseline", "linux-arm64"] as const
|
||||
const root = join(import.meta.dir, "..")
|
||||
const out = join(root, "dist", "release")
|
||||
|
||||
const dirty = Bun.spawnSync(["git", "status", "--porcelain", "--untracked-files=no"], { cwd: root, stdout: "pipe" }).stdout.toString().trim()
|
||||
if (dirty && !process.argv.includes("--dirty")) {
|
||||
console.error(`dist: the checkout has uncommitted changes — release files come from a commit (--dirty to build anyway)\n${dirty}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
rmSync(out, { recursive: true, force: true })
|
||||
mkdirSync(out, { recursive: true })
|
||||
const files: string[] = []
|
||||
for (const t of TARGETS) {
|
||||
const name = `lembas-${t}`
|
||||
const r = Bun.spawnSync(["bun", "run", "scripts/build.ts", join(out, name), "--target", t], { cwd: root, stdout: "inherit", stderr: "inherit" })
|
||||
if (r.exitCode !== 0) process.exit(r.exitCode ?? 1)
|
||||
await Bun.write(join(out, `${name}.gz`), Bun.gzipSync(readFileSync(join(out, name)), { level: 9 }))
|
||||
files.push(`${name}.gz`)
|
||||
}
|
||||
for (const f of ["install.sh", "get.sh", "LICENSES.txt"]) {
|
||||
copyFileSync(join(root, f), join(out, f))
|
||||
files.push(f)
|
||||
}
|
||||
// The first line names the version: it is signed with the rest, so an old release's files cannot
|
||||
// be passed off under a newer tag. (sha256sum -c skips it as a comment.)
|
||||
const sums = `# lembas ${pkg.version}\n` + files.map((f) => `${createHash("sha256").update(readFileSync(join(out, f))).digest("hex")} ${f}`).join("\n") + "\n"
|
||||
await Bun.write(join(out, "SHA256SUMS"), sums)
|
||||
|
||||
// The signature: made with ssh-keygen, and checked here the way the updater will check it.
|
||||
const signIdx = process.argv.indexOf("--sign")
|
||||
const key = signIdx > 0 ? process.argv[signIdx + 1] : undefined
|
||||
if (key) {
|
||||
const r = Bun.spawnSync(["ssh-keygen", "-q", "-Y", "sign", "-f", key.replace(/^~(?=\/)/, process.env.HOME ?? "~"), "-n", SIG_NAMESPACE, join(out, "SHA256SUMS")], { stdout: "inherit", stderr: "inherit" })
|
||||
if (r.exitCode !== 0) process.exit(1)
|
||||
const pub = readFileSync(`${key.replace(/^~(?=\/)/, process.env.HOME ?? "~")}.pub`, "utf8")
|
||||
verifySshSig(readFileSync(join(out, "SHA256SUMS")), readFileSync(join(out, "SHA256SUMS.sig"), "utf8"), SIG_NAMESPACE, [pub])
|
||||
const ours = RELEASE_KEYS.some((k) => k.split(/\s+/)[1] === pub.split(/\s+/)[1])
|
||||
console.log(`SHA256SUMS.sig — signed by ${pub.trim().split(/\s+/).slice(2).join(" ") || "the key"}${ours ? " (the release key built into LLeMbas CLI)" : " (NOT the built-in release key: users need update.public_key)"}`)
|
||||
} else console.warn("dist: no --sign KEY — this release is unsigned, and neither get.sh nor the updater will install it")
|
||||
|
||||
// What runs here is checked to run: the x64 builds on an x64 machine.
|
||||
for (const t of TARGETS) {
|
||||
if (!t.startsWith(`linux-${process.arch}`)) continue
|
||||
const v = Bun.spawnSync([join(out, `lembas-${t}`), "--version"], { stdout: "pipe" })
|
||||
if (v.exitCode !== 0) {
|
||||
console.error(`dist: lembas-${t} does not run here`)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`lembas-${t} --version → ${v.stdout.toString().trim()}`)
|
||||
}
|
||||
// The plain binaries stay beside them for a look; only what SHA256SUMS lists is published.
|
||||
console.log(`\n${out} — publish these (and SHA256SUMS.sig):\n${sums}`)
|
||||
Reference in new issue
Block a user