diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index 626c17d747..73d46e4a0f 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -26,7 +26,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write( { name: pkg.name, bin: { - "altimate": "./bin/altimate", + altimate: "./bin/altimate", "altimate-code": "./bin/altimate-code", }, scripts: { @@ -51,6 +51,48 @@ const tasks = Object.entries(binaries).map(async ([name]) => { await Promise.all(tasks) await $`cd ./dist/${pkg.name} && bun pm pack && npm publish *.tgz --access public --tag ${Script.channel}` +// Publish unscoped `altimate-code` wrapper package so users can `npm i -g altimate-code` +const unscopedName = "altimate-code" +const unscopedDir = `./dist/${unscopedName}` +try { + await $`mkdir -p ${unscopedDir}` + await $`cp -r ./bin ${unscopedDir}/bin` + await $`cp ./script/postinstall.mjs ${unscopedDir}/postinstall.mjs` + await Bun.file(`${unscopedDir}/LICENSE`).write(await Bun.file("../../LICENSE").text()) + await Bun.file(`${unscopedDir}/CHANGELOG.md`).write(await Bun.file("../../CHANGELOG.md").text()) + await Bun.file(`${unscopedDir}/README.md`).write(await Bun.file("../../README.md").text()) + await Bun.file(`${unscopedDir}/package.json`).write( + JSON.stringify( + { + name: unscopedName, + description: "The AI-native data engineering agent for the terminal", + repository: { + type: "git", + url: "git+https://github.com/AltimateAI/altimate-code.git", + }, + homepage: "https://github.com/AltimateAI/altimate-code#readme", + bugs: "https://github.com/AltimateAI/altimate-code/issues", + bin: { + altimate: "./bin/altimate", + "altimate-code": "./bin/altimate-code", + }, + scripts: { + postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs", + }, + version: version, + license: pkg.license, + optionalDependencies: binaries, + }, + null, + 2, + ), + ) + await $`cd ${unscopedDir} && bun pm pack && npm publish *.tgz --access public --tag ${Script.channel}` +} catch (e) { + console.error("Unscoped package publish failed:", e) + process.exit(1) +} + // Docker (non-fatal — requires buildx multi-platform setup) try { const image = "ghcr.io/altimateai/altimate-code" @@ -65,10 +107,14 @@ try { // registries if (!Script.preview) { // Calculate SHA values - const arm64Sha = await $`sha256sum ./dist/altimate-code-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) + const arm64Sha = await $`sha256sum ./dist/altimate-code-linux-arm64.tar.gz | cut -d' ' -f1` + .text() + .then((x) => x.trim()) const x64Sha = await $`sha256sum ./dist/altimate-code-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) const macX64Sha = await $`sha256sum ./dist/altimate-code-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - const macArm64Sha = await $`sha256sum ./dist/altimate-code-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const macArm64Sha = await $`sha256sum ./dist/altimate-code-darwin-arm64.zip | cut -d' ' -f1` + .text() + .then((x) => x.trim()) const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) diff --git a/packages/opencode/test/install/publish-package.test.ts b/packages/opencode/test/install/publish-package.test.ts index c05bb81c75..e78c964af0 100644 --- a/packages/opencode/test/install/publish-package.test.ts +++ b/packages/opencode/test/install/publish-package.test.ts @@ -74,3 +74,51 @@ describe("publish package validation", () => { expect(wrapper).toContain('"@altimateai"') }) }) + +describe("unscoped package (altimate-code)", () => { + const publishScript = fs.readFileSync(path.join(REPO_PKG_DIR, "script/publish.ts"), "utf-8") + + test("publish.ts creates unscoped altimate-code wrapper package", () => { + expect(publishScript).toContain('const unscopedName = "altimate-code"') + expect(publishScript).toContain("`./dist/${unscopedName}`") + }) + + test("unscoped package gets same bin entries as scoped package", () => { + // Both scoped and unscoped packages should register the same bin commands + // Keys may be quoted or unquoted in the source + expect(publishScript).toContain('./bin/altimate"') + expect(publishScript).toContain('"altimate-code": "./bin/altimate-code"') + }) + + test("unscoped package gets postinstall, bin, LICENSE, CHANGELOG, and README", () => { + expect(publishScript).toContain("cp -r ./bin ${unscopedDir}/bin") + expect(publishScript).toContain("cp ./script/postinstall.mjs ${unscopedDir}/postinstall.mjs") + expect(publishScript).toContain("${unscopedDir}/LICENSE") + expect(publishScript).toContain("${unscopedDir}/CHANGELOG.md") + expect(publishScript).toContain("${unscopedDir}/README.md") + }) + + test("unscoped package includes npm metadata for discoverability", () => { + expect(publishScript).toContain("github.com/AltimateAI/altimate-code") + expect(publishScript).toContain("homepage") + expect(publishScript).toContain("bugs") + expect(publishScript).toContain("repository") + }) + + test("unscoped package publish has error handling", () => { + // The unscoped publish block should be wrapped in try/catch + const unscopedSection = publishScript.substring(publishScript.indexOf("const unscopedName")) + expect(unscopedSection).toContain("try {") + expect(unscopedSection).toContain("catch (e)") + expect(unscopedSection).toContain("Unscoped package publish failed") + }) + + test("unscoped package uses optionalDependencies for platform binaries", () => { + // The unscoped package includes platform binaries directly so it works standalone + expect(publishScript).toContain("optionalDependencies: binaries") + }) + + test("unscoped package is published with --access public", () => { + expect(publishScript).toContain("${unscopedDir} && bun pm pack && npm publish *.tgz --access public") + }) +})