#!/usr/bin/env bun // LICENSES.txt: every licence a release binary carries — LLeMbas CLI's, each npm package the bundle // actually contains (found by building it, not by reading the lockfile), OpenTUI's native // libraries, and the Bun runtime the binary is compiled with (third_party/bun--LICENSE.md, // copied from Bun's repository at that tag). // bun run licenses → writes LICENSES.txt // bun run licenses --check → fails if LICENSES.txt is not what it would write (CI) import solidPlugin from "@opentui/solid/bun-plugin" import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { join } from "node:path" const root = join(import.meta.dir, "..") const BUN = "1.4.2" const out = mkdtempSync(join(tmpdir(), "lembas-licenses-")) const r = await Bun.build({ entrypoints: [join(root, "src/cli.ts")], tsconfig: join(root, "tsconfig.json"), target: "bun", outdir: out, metafile: true, // Every native package but x64 glibc's left out of the build: they need not be installed (a // runner has only its own), and the arm64 one carries the same notices. plugins: [solidPlugin, { name: "other-platforms", setup: (b) => void b.onResolve({ filter: /^@opentui\/core-(?!linux-x64$)/ }, (a) => ({ path: a.path, external: true })) }], } as Parameters[0]) rmSync(out, { recursive: true, force: true }) const inputs = Object.keys((r as unknown as { metafile: { inputs: Record } }).metafile.inputs) // Only the x64/arm64 glibc builds are released; their native packages are the same but for the library. const pkgs = [...new Set(inputs.map((f) => /node_modules\/((?:@[^/]+\/)?[^/]+)/.exec(f)?.[1]).filter((p): p is string => !!p))].sort() const licenceFiles = (dir: string) => readdirSync(dir).filter((f) => /^(licen[cs]e|copying|notice|patents|authors)/i.test(f)).sort() const rule = "=".repeat(78) const parts: string[] = [ `LLeMbas CLI — the licences of what its release binaries contain.\nGenerated by scripts/licenses.ts; \`lembas --licenses\` prints this.\n`, `${rule}\nLLeMbas CLI\n${rule}\n${readFileSync(join(root, "LICENSE"), "utf8").trim()}\n\nLLeMbas CLI builds on OpenCode, Hermes Agent and LLeMbas; see THIRD_PARTY.md in its source.`, ] for (const p of pkgs) { const dir = join(root, "node_modules", p) const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as { version: string; license?: string; homepage?: string } const files = licenceFiles(dir) const native = p.startsWith("@opentui/core-linux") ? "\n(libopentui.so: OpenTUI's native library, with Ghostty's terminal code, Little CMS, libwebp, stb, Wuffs and Meta's Yoga layout compiled in; their notices follow.)" : "" parts.push(`${rule}\n${p} ${pkg.version} — ${pkg.license ?? "see below"}${native}\n${rule}\n${files.map((f) => `--- ${f} ---\n${readFileSync(join(dir, f), "utf8").trim()}`).join("\n\n") || "(no licence file in the package)"}`) } parts.push( `${rule}\nYoga (in libopentui.so) — MIT\n${rule}\nCopyright (c) Meta Platforms, Inc. and affiliates. Licensed under the MIT license: https://github.com/facebook/yoga/blob/main/LICENSE`, `${rule}\nTree-sitter grammars (OpenTUI's syntax highlighting: javascript, typescript, markdown, zig) — MIT\n${rule}\nhttps://github.com/tree-sitter/tree-sitter-javascript, https://github.com/tree-sitter/tree-sitter-typescript, https://github.com/tree-sitter-grammars/tree-sitter-markdown, https://github.com/tree-sitter-grammars/tree-sitter-zig — each MIT.`, `${rule}\nBun ${BUN} (the runtime compiled into the binary)\n${rule}\n${readFileSync(join(root, `third_party/bun-${BUN}-LICENSE.md`), "utf8").trim()}\n\nRelinking: LLeMbas CLI's whole source is public, so the binary can be rebuilt against a modified Bun (and so a modified JavaScriptCore) with \`bun run build\` — https://github.com/oven-sh/bun at bun-v${BUN}, https://github.com/oven-sh/WebKit for its JavaScriptCore.`, ) const text = `${parts.join("\n\n")}\n` const file = join(root, "LICENSES.txt") if (process.argv.includes("--check")) { if (!existsSync(file) || readFileSync(file, "utf8") !== text) { console.error("LICENSES.txt is not current: bun run licenses") process.exit(1) } console.log(`LICENSES.txt is current (${pkgs.length} packages)`) } else { writeFileSync(file, text) console.log(`LICENSES.txt: ${pkgs.length} packages — ${pkgs.join(", ")}`) }