From 5e9809947e29ebef5c2cd6f470b3e6d8c831a2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:08:43 +0200 Subject: [PATCH 1/4] docs: present the CLI as shipped, not an MVP Remove leftover "current MVP" framing from the README intro now that akua is a real, shipped product (v0.10.1+, installable via `brew install akua-dev/tap/akua`, proven end-to-end against production: install, login, free cluster, free managed worker, package, install). Bump the pinned copy-paste install examples and the release-artifact table from v0.9.0 to the current v0.10.1 release; v0.9.0 archives predate the embedded package-runtime change (#42) and don't contain the `node_modules/@akua-dev` tree the current install script extracts and (on Windows) copies, so the old pinned version silently breaks the documented install path. Update test/docs.test.ts's pinned asset-name literals to match. Verified every command example in the README against this repo's current source: `akua --help`, `akua auth login`/`status`/`logout`, `akua commands`, `akua workspaces --help` (human mode), and `akua pkg version` all match what the CLI at v0.10.1 actually outputs. Tested: bun test test/docs.test.ts; bun test (165 pass) --- README.md | 22 +++++++++++----------- test/docs.test.ts | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d723cd7..cf08604 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Akua CLI `akua` is the public Akua Cloud command-line interface. It is a self-contained -Bun/TypeScript executable for humans, automation, and coding agents. The current -MVP implements local token authentication, browser/device login, adaptive +Bun/TypeScript executable for humans, automation, and coding agents. It +implements local token authentication, browser/device login, adaptive structured output, and executable public operationId-driven commands. Effect CLI renders the interactive command tree and validates its documented flags; the generated typed Effect API and its static executor are derived from OpenAPI @@ -41,12 +41,12 @@ test and all published assets have passed post-upload verification. ### GitHub Release: macOS or Linux -This copy-paste example installs v0.9.0 into `~/.local/bin`. Change `VERSION` +This copy-paste example installs v0.10.1 into `~/.local/bin`. Change `VERSION` when selecting a newer release. ```sh set -eu -VERSION=0.9.0 +VERSION=0.10.1 case "$(uname -s)-$(uname -m)" in Darwin-arm64) TARGET=darwin-arm64 ;; Darwin-x86_64) TARGET=darwin-x64 ;; @@ -78,12 +78,12 @@ newer `VERSION`, replacing `~/.local/bin/akua`. The CLI does not self-update. ### GitHub Release: Windows x64 -Run in PowerShell. This installs v0.9.0 into `%USERPROFILE%\bin`; add that +Run in PowerShell. This installs v0.10.1 into `%USERPROFILE%\bin`; add that directory to the user `PATH` if it is not already present. ```powershell $ErrorActionPreference = "Stop" -$Version = "0.9.0" +$Version = "0.10.1" $Asset = "akua-v$Version-windows-x64.zip" $Base = "https://github.com/akua-dev/cli/releases/download/v$Version" Invoke-WebRequest "$Base/$Asset" -OutFile $Asset @@ -108,11 +108,11 @@ Copy-Item "$InstallRoot\node_modules" "$HOME\bin\node_modules" -Recurse -Force | Platform | Architecture | Asset | Runtime baseline | | --- | --- | --- | --- | -| macOS | Apple Silicon arm64 | `akua-v0.9.0-darwin-arm64.tar.gz` | Bun darwin arm64 | -| macOS | Intel x64 | `akua-v0.9.0-darwin-x64.tar.gz` | Bun darwin x64 | -| Linux | glibc arm64 | `akua-v0.9.0-linux-arm64.tar.gz` | Bun linux arm64 | -| Linux | glibc x64 | `akua-v0.9.0-linux-x64.tar.gz` | Bun linux x64 baseline | -| Windows | x64 | `akua-v0.9.0-windows-x64.zip` | Bun windows x64 baseline | +| macOS | Apple Silicon arm64 | `akua-v0.10.1-darwin-arm64.tar.gz` | Bun darwin arm64 | +| macOS | Intel x64 | `akua-v0.10.1-darwin-x64.tar.gz` | Bun darwin x64 | +| Linux | glibc arm64 | `akua-v0.10.1-linux-arm64.tar.gz` | Bun linux arm64 | +| Linux | glibc x64 | `akua-v0.10.1-linux-x64.tar.gz` | Bun linux x64 baseline | +| Windows | x64 | `akua-v0.10.1-windows-x64.zip` | Bun windows x64 baseline | Linux musl, Windows arm64, and other systems are not in the tested release contract. x64 Linux and Windows use Bun's baseline target for older CPUs. Unix diff --git a/test/docs.test.ts b/test/docs.test.ts index 12d31aa..299f95d 100644 --- a/test/docs.test.ts +++ b/test/docs.test.ts @@ -11,8 +11,8 @@ describe("distribution documentation", () => { expect(readme).toContain("brew install akua-dev/tap/akua"); expect(readme).toContain("https://github.com/akua-dev/cli/releases/download/"); - expect(readme).toContain("akua-v0.9.0-darwin-arm64.tar.gz"); - expect(readme).toContain("akua-v0.9.0-windows-x64.zip"); + expect(readme).toContain("akua-v0.10.1-darwin-arm64.tar.gz"); + expect(readme).toContain("akua-v0.10.1-windows-x64.zip"); expect(readme).toContain("checksums.txt"); expect(readme).toContain("sha256sum"); expect(readme).toContain("Get-FileHash"); From 6f5d3518ea741ee701d21d05e4896a4b13734c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:19:49 +0200 Subject: [PATCH 2/4] docs: reframe README as a developer product, not an implementation Rework the README's intro and structure to lead with what akua does (create clusters, add machines, package and install apps) and who it serves (a human at a terminal, a CI pipeline, a coding agent) before any mechanism explanation. Name all three audiences explicitly instead of only "humans, automation, and coding agents" in passing. Remove every mention of Effect, the internal library the CLI happens to be built with. It is not something a user installing and running the CLI needs to know. Where a sentence's real content was the useful, user-facing fact that commands are generated from the OpenAPI spec and therefore never drift out of sync, keep that fact and state it in outcome terms instead of naming the framework. Move the OpenAPI-generation mechanics out of the primary usage flow and into the Development section, since that detail matters to contributors, not to someone running `akua workspaces list`. Reorder first-use so sign-in gets its own section, and consolidate the human vs. CI vs. agent output story into one section explicitly titled for all three. Verified every command and flag shown against the current CLI: `akua --help`, `--version`, `commands --limit 1|5`, `pkg version`, `pkg --help`, `auth --help`, `auth login --help`, `auth status`, `workspaces --help`, and the `workspaces list --input -` stdin-piping example, all run directly from src/bin/akua.ts. Updated test/docs.test.ts's pinned README literals to match: dropped the "generated typed Effect API" and "Generated API commands execute" assertions (text no longer present by design) in favor of phrases that match the new copy, and added a regression test asserting the README never mentions Effect and does name all three audiences. --- README.md | 95 +++++++++++++++++++++++++++-------------------- test/docs.test.ts | 11 +++++- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index cf08604..3d885ff 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Akua CLI -`akua` is the public Akua Cloud command-line interface. It is a self-contained -Bun/TypeScript executable for humans, automation, and coding agents. It -implements local token authentication, browser/device login, adaptive -structured output, and executable public operationId-driven commands. Effect -CLI renders the interactive command tree and validates its documented flags; -the generated typed Effect API and its static executor are derived from OpenAPI -for every public operation. +`akua` drives Akua Cloud from a terminal: create Kubernetes clusters, add +machines, package an application, and install it, all from one command. It is +a single self-contained executable, built for three audiences — a person +typing commands interactively, a CI pipeline calling it non-interactively, and +a coding agent driving it programmatically — and every command adapts its +output to whichever one is running it. + +Every command is generated directly from Akua's public API, so the CLI never +drifts out of sync with what the platform can actually do. The canonical executable is `akua`; there is no `cnap` compatibility binary. @@ -127,23 +129,16 @@ checksums.txt` on macOS. The adjacent `.sha256` files support single-asse verification. Release assets are never replaced in place; a changed binary requires a new version. -## First use and authentication - -Inspect the installed surface first: +## First commands ```sh akua # complete interactive command tree akua auth --help # authentication subcommands and options akua workspaces --help # generated resource commands -akua commands --limit 5 +akua commands --limit 5 # discover the full command surface ``` -For CI and coding agents, prefer an ephemeral environment credential: - -```sh -export AKUA_API_TOKEN='sk_akua_...' -akua auth status -``` +## Sign in For an interactive browser/device login: @@ -159,6 +154,14 @@ the verification in any browser instead. akua auth login --no-browser ``` +For CI and coding agents, prefer an ephemeral environment credential instead of +an interactive login: + +```sh +export AKUA_API_TOKEN='sk_akua_...' +akua auth status +``` + For a local persisted token without an interactive login: ```sh @@ -173,10 +176,11 @@ akua auth logout removes only the stored `token`, also preserving unknown config keys, and cannot clear `AKUA_API_TOKEN` from the parent process. -## Human and agent output +## Built for humans, CI, and agents -An interactive TTY defaults to human prose. The CLI defaults to compact agent -output when any of these signals are active: +An interactive TTY defaults to human prose. The CLI switches to compact agent +output automatically when any of these signals are active, so an agent gets +usable output without extra flags: - `AGENT=true` or `AGENT=` (for example `AGENT=codex`); - a detected provider environment such as Codex, Claude Code, Cursor, Aider, @@ -200,40 +204,33 @@ The supported modes are `human`, `agent`, `json`, and `quiet`. Success data is written to stdout; progress and warnings belong on stderr. Unknown commands, flags, and output modes fail loudly with stable nonzero exit codes. -## OpenAPI command generation +## Discover and run commands -The public source of truth is -`https://api.akua.dev/v1/openapi.json`. The workflow is deliberately explicit: +Every public Akua operation is available as a generated command, kept current +with the API automatically. Discover the current surface instead of relying on +a fixed list: ```sh -mise run spec:fetch # fetch and stably format openapi/public.json -mise run generate # derive command registry and typed Effect API -mise run generate:check # fail if committed generated output has drifted -mise run check # drift check, typecheck/build, and tests +akua commands --json +akua commands --resource workspaces +akua commands --operation-id workspaces.list ``` -Generation is deterministic and operationId-driven. Only operations marked -`x-platform-visibility: PUBLIC` are included. For example, -`operationId: workspaces.list` becomes `akua workspaces list`; registry rows are -sorted by operationId. The generated outputs are -`src/generated/commands.gen.ts`, `src/generated/openapi-api.gen.ts`, and -`src/generated/public-operation-executor.gen.ts`. - -Generated API commands execute through the typed Effect client and accept one -JSON object from stdin or a named file. Its only keys are `path`, `query`, -`headers`, and `body`: +For example, `operationId: workspaces.list` becomes `akua workspaces list`. +Generated commands accept one JSON object from stdin or a named file. Its only +keys are `path`, `query`, `headers`, and `body`: ```sh printf '{"query":{"limit":5}}' | akua workspaces list --input - akua machines create --input - < ./machine.json ``` -Request input is schema-validated before transport and never included in -diagnostics. The CLI remains provider-neutral: it has no provider-specific commands, +Request input is schema-validated before it is sent and never included in +diagnostics. The CLI stays provider-neutral: it has no provider-specific commands, flags, environment variables, or credential loaders. Provider-specific values -belong only in the generated public API request body. +belong only in the generated request body. -## Development and release validation +## Development Prerequisites are [mise](https://mise.jdx.dev/) and the pinned Bun toolchain: @@ -247,6 +244,22 @@ mise run build:binary ./dist/akua commands --limit 1 ``` +The command surface is generated from the public source of truth, +`https://api.akua.dev/v1/openapi.json`: + +```sh +mise run spec:fetch # fetch and stably format openapi/public.json +mise run generate # regenerate the command registry and typed API bindings +mise run generate:check # fail if committed generated output has drifted +mise run check # drift check, typecheck/build, and tests +``` + +Generation is deterministic and operationId-driven; only operations marked +`x-platform-visibility: PUBLIC` are included, and registry rows are sorted by +operationId. The generated outputs are `src/generated/commands.gen.ts`, +`src/generated/openapi-api.gen.ts`, and +`src/generated/public-operation-executor.gen.ts`. + `mise run release:package` cross-compiles all five targets, creates archives and checksums in `dist/release`, and verifies their manifest. `mise run release:smoke` extracts and runs the artifact for the current supported host. diff --git a/test/docs.test.ts b/test/docs.test.ts index 299f95d..79c11fe 100644 --- a/test/docs.test.ts +++ b/test/docs.test.ts @@ -39,7 +39,7 @@ describe("distribution documentation", () => { "mise run generate", "mise run generate:check", "operationId", - "generated typed Effect API", + "generated directly from Akua's public API", "akua workspaces list --input -", "akua machines create --input -", "provider-specific commands", @@ -49,12 +49,19 @@ describe("distribution documentation", () => { } expect(readme).not.toContain("skills/akua/"); expect(readme).not.toContain("akua-dev/skills"); - expect(readme).toContain("Generated API commands execute"); + expect(readme).toContain("Generated commands accept one JSON object"); expect(readme).not.toContain("generic executor is not yet wired"); expect(readme).not.toContain("agent-skills-standard-following"); expect(readme).not.toContain("skills add akua-dev/skills"); }); + test("README treats the underlying framework as an implementation detail", async () => { + const readme = await text("README.md"); + + expect(readme).not.toMatch(/\bEffect\b/); + expect(readme).toContain("humans, CI, and agents"); + }); + test("architecture records device authentication and all generated API artifacts", async () => { const architecture = await text("docs/architecture.md"); From b971dec9c17a9c7f8755f305750692a089e549ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:51:05 +0200 Subject: [PATCH 3/4] docs: split repo-development instructions out of the README Move the Development section (codegen mechanics, release packaging/ verification/smoke, Release Please/Homebrew tap process) into a new CONTRIBUTING.md so README.md only covers using the shipped `akua` executable: what it does, install, sign in, run commands, and where to find the full reference. README now points to docs.akua.dev for the command reference and to CONTRIBUTING.md for repo development. Rationale: reviewer feedback on PR #48 - anything a developer using the CLI doesn't need to see belongs in its own file, not the README. Tested: bun test (168 pass), bun scripts/generate-commands.ts --check, bun scripts/generate-effect-api.ts --check, bun run build. --- CONTRIBUTING.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 54 ++++------------------------- test/docs.test.ts | 41 ++++++++++++++++++++-- 3 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9260d20 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,87 @@ +# Contributing to the Akua CLI + +This file covers building, testing, and releasing this repository's source. +If you only want to use the `akua` executable, see [README.md](README.md) +instead. + +## Prerequisites + +[mise](https://mise.jdx.dev/) manages the pinned Bun toolchain: + +```sh +mise install +bun install --frozen-lockfile +mise run check +mise run build:binary +./dist/akua --version +./dist/akua --help +./dist/akua commands --limit 1 +``` + +`mise run check` runs the drift check, typecheck/build, and tests — the same +gate CI runs. Run it before opening a PR. + +## Command generation + +The command surface is generated from the public source of truth, +`https://api.akua.dev/v1/openapi.json`: + +```sh +mise run spec:fetch # fetch and stably format openapi/public.json +mise run generate # regenerate the command registry and typed API bindings +mise run generate:check # fail if committed generated output has drifted +``` + +Generation is deterministic and operationId-driven; only operations marked +`x-platform-visibility: PUBLIC` are included, and registry rows are sorted by +operationId. The generated outputs are `src/generated/commands.gen.ts`, +`src/generated/openapi-api.gen.ts`, and +`src/generated/public-operation-executor.gen.ts`. Never hand-edit generated +files; run `mise run generate` and commit the result. + +See [docs/architecture.md](docs/architecture.md) for the full command +derivation rules, the API/auth/config model, output modes, exit codes, and +the rest of the CLI's design contract. + +## Testing + +```sh +bun test +``` + +`mise run check` (drift check, build, tests) is the required gate before +release changes; see [docs/architecture.md](docs/architecture.md#testing-strategy) +for what current test coverage includes. + +## Release process + +`mise run release:package` cross-compiles all five targets, creates archives +and checksums in `dist/release`, and verifies their manifest. +`mise run release:verify` re-verifies an already-packaged release directory. +`mise run release:smoke` extracts and runs the artifact for the current +supported host. CI repeats native smoke tests on every platform in the +release matrix (macOS arm64/x64, glibc Linux arm64/x64, Windows x64). + +Release Please creates the version tag and GitHub Release. Its own workflow +then calls artifact publication directly, so publication does not depend on a +tag event that GitHub may suppress for job-token-created tags. Uploads do not +clobber existing assets. Only after downloading and re-verifying the published +assets does the workflow dispatch the Homebrew manifest URL. The +`HOMEBREW_TAP_TOKEN` secret must be a fine-grained credential scoped only to +the tap repository's dispatch permission; failures remain visible as release +job failures. + +`akua-dev/homebrew-tap` owns the `akua` formula, formula tests, and the +reviewed formula-update PR. This repository requests a formula PR only after +every archive has passed a native install smoke test and all published assets +have passed post-upload verification; it never pushes formula commits itself. + +`scripts/release.ts` is the source of truth for target IDs, Bun targets, +archive names, executable names, SHA-256 files, and release manifests. + +## Repository-specific engineering rules + +See [AGENTS.md](AGENTS.md) for durable, repository-wide engineering rules +(Effect v4 production code conventions, the release contract, ownership +boundaries, and the public API command contract) that apply to any change in +`src/` or `scripts/`. diff --git a/README.md b/README.md index 3d885ff..58d76de 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ The canonical executable is `akua`; there is no `cnap` compatibility binary. GitHub Releases and Homebrew are the supported install channels. Every GitHub archive contains the `akua` executable and its adjacent target-native package runtime, and has an adjacent SHA-256 file. A release also publishes -`checksums.txt`, a complete release manifest, and the exact Homebrew -artifact/checksum mapping. +`checksums.txt` and a complete release manifest. ### Homebrew @@ -37,9 +36,7 @@ brew update brew upgrade akua ``` -The formula is maintained in `akua-dev/homebrew-tap`. A CLI release requests a -reviewed formula PR only after every archive has passed a native install smoke -test and all published assets have passed post-upload verification. +The formula is maintained in `akua-dev/homebrew-tap`. ### GitHub Release: macOS or Linux @@ -230,48 +227,9 @@ diagnostics. The CLI stays provider-neutral: it has no provider-specific command flags, environment variables, or credential loaders. Provider-specific values belong only in the generated request body. -## Development +## Learn more -Prerequisites are [mise](https://mise.jdx.dev/) and the pinned Bun toolchain: +The full command reference and platform guides live at +[docs.akua.dev](https://docs.akua.dev). -```sh -mise install -bun install --frozen-lockfile -mise run check -mise run build:binary -./dist/akua --version -./dist/akua --help -./dist/akua commands --limit 1 -``` - -The command surface is generated from the public source of truth, -`https://api.akua.dev/v1/openapi.json`: - -```sh -mise run spec:fetch # fetch and stably format openapi/public.json -mise run generate # regenerate the command registry and typed API bindings -mise run generate:check # fail if committed generated output has drifted -mise run check # drift check, typecheck/build, and tests -``` - -Generation is deterministic and operationId-driven; only operations marked -`x-platform-visibility: PUBLIC` are included, and registry rows are sorted by -operationId. The generated outputs are `src/generated/commands.gen.ts`, -`src/generated/openapi-api.gen.ts`, and -`src/generated/public-operation-executor.gen.ts`. - -`mise run release:package` cross-compiles all five targets, creates archives and -checksums in `dist/release`, and verifies their manifest. `mise run -release:smoke` extracts and runs the artifact for the current supported host. -CI repeats native smoke tests on every platform in the table. - -Release Please creates the version tag and GitHub Release. Its own workflow then -calls artifact publication directly, so publication does not depend on a tag -event that GitHub may suppress for job-token-created tags. Uploads do not -clobber existing assets. Only after downloading and re-verifying the published -assets does the workflow dispatch the Homebrew manifest URL. The -`HOMEBREW_TAP_TOKEN` secret must be a fine-grained credential scoped only to the -tap repository's dispatch permission; failures remain visible as release job -failures. - -See [docs/architecture.md](docs/architecture.md) for the broader CLI contract. +Contributing to this repo? See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/test/docs.test.ts b/test/docs.test.ts index 79c11fe..2dfb4f7 100644 --- a/test/docs.test.ts +++ b/test/docs.test.ts @@ -35,9 +35,6 @@ describe("distribution documentation", () => { "AGENT=true", "AGENT=", "non-TTY", - "mise run spec:fetch", - "mise run generate", - "mise run generate:check", "operationId", "generated directly from Akua's public API", "akua workspaces list --input -", @@ -62,6 +59,44 @@ describe("distribution documentation", () => { expect(readme).toContain("humans, CI, and agents"); }); + test("README stays user-facing and defers repo development to CONTRIBUTING.md", async () => { + const readme = await text("README.md"); + + expect(readme).toContain("docs.akua.dev"); + expect(readme).toContain("Contributing to this repo? See [CONTRIBUTING.md](CONTRIBUTING.md)."); + expect(readme).not.toContain("mise run spec:fetch"); + expect(readme).not.toContain("mise run generate"); + expect(readme).not.toContain("mise run generate:check"); + expect(readme).not.toContain("mise run release:package"); + expect(readme).not.toContain("mise run release:smoke"); + expect(readme).not.toContain("mise install"); + expect(readme).not.toContain("Release Please"); + expect(readme).not.toContain("HOMEBREW_TAP_TOKEN"); + }); + + test("CONTRIBUTING documents generation, testing, and release mechanics", async () => { + const contributing = await text("CONTRIBUTING.md"); + + for (const value of [ + "mise install", + "mise run check", + "mise run spec:fetch", + "mise run generate", + "mise run generate:check", + "bun test", + "mise run release:package", + "mise run release:verify", + "mise run release:smoke", + "Release Please", + "HOMEBREW_TAP_TOKEN", + "src/generated/commands.gen.ts", + "docs/architecture.md", + "AGENTS.md", + ]) { + expect(contributing).toContain(value); + } + }); + test("architecture records device authentication and all generated API artifacts", async () => { const architecture = await text("docs/architecture.md"); From 8e12241bad0743d5f63bdfcc49eccb7e1b783cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:00:31 +0200 Subject: [PATCH 4/4] docs: move manual release-archive install out of README The per-OS GitHub Release install steps (checksum verification, extraction, PATH setup for macOS/Linux/Windows) cluttered the primary README flow for a path almost no one uses; Homebrew is the install method for the vast majority of users. Move the detailed fallback into docs/install.md and leave a single pointer line in README.md. The fallback stays documented (not deleted) because docs/quickstart.mdx in the cnap repo references it as the non-Homebrew install path for agents. --- README.md | 96 +--------------------------------------------- docs/install.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++ test/docs.test.ts | 33 ++++++++++++---- 3 files changed, 126 insertions(+), 101 deletions(-) create mode 100644 docs/install.md diff --git a/README.md b/README.md index 58d76de..fcc5a41 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,6 @@ The canonical executable is `akua`; there is no `cnap` compatibility binary. ## Install -GitHub Releases and Homebrew are the supported install channels. Every GitHub -archive contains the `akua` executable and its adjacent target-native package -runtime, and has an adjacent SHA-256 file. A release also publishes -`checksums.txt` and a complete release manifest. - -### Homebrew - ```sh brew install akua-dev/tap/akua akua --version @@ -38,93 +31,8 @@ brew upgrade akua The formula is maintained in `akua-dev/homebrew-tap`. -### GitHub Release: macOS or Linux - -This copy-paste example installs v0.10.1 into `~/.local/bin`. Change `VERSION` -when selecting a newer release. - -```sh -set -eu -VERSION=0.10.1 -case "$(uname -s)-$(uname -m)" in - Darwin-arm64) TARGET=darwin-arm64 ;; - Darwin-x86_64) TARGET=darwin-x64 ;; - Linux-arm64|Linux-aarch64) TARGET=linux-arm64 ;; - Linux-x86_64) TARGET=linux-x64 ;; - *) echo "Unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;; -esac -ASSET="akua-v${VERSION}-${TARGET}.tar.gz" -BASE="https://github.com/akua-dev/cli/releases/download/v${VERSION}" -curl --fail --location --remote-name "${BASE}/${ASSET}" -curl --fail --location --remote-name "${BASE}/${ASSET}.sha256" -if command -v sha256sum >/dev/null 2>&1; then - sha256sum --check "${ASSET}.sha256" -else - shasum -a 256 --check "${ASSET}.sha256" -fi -INSTALL_ROOT="$HOME/.local/libexec/akua-v${VERSION}" -mkdir -p "$INSTALL_ROOT" "$HOME/.local/bin" -tar -xzf "$ASSET" -C "$INSTALL_ROOT" -ln -sfn "$INSTALL_ROOT/akua" "$HOME/.local/bin/akua" -"$HOME/.local/bin/akua" --version -"$HOME/.local/bin/akua" --help -"$HOME/.local/bin/akua" commands --limit 1 -"$HOME/.local/bin/akua" pkg version -``` - -Ensure `~/.local/bin` is on `PATH`. Manual upgrades repeat these steps with a -newer `VERSION`, replacing `~/.local/bin/akua`. The CLI does not self-update. - -### GitHub Release: Windows x64 - -Run in PowerShell. This installs v0.10.1 into `%USERPROFILE%\bin`; add that -directory to the user `PATH` if it is not already present. - -```powershell -$ErrorActionPreference = "Stop" -$Version = "0.10.1" -$Asset = "akua-v$Version-windows-x64.zip" -$Base = "https://github.com/akua-dev/cli/releases/download/v$Version" -Invoke-WebRequest "$Base/$Asset" -OutFile $Asset -Invoke-WebRequest "$Base/$Asset.sha256" -OutFile "$Asset.sha256" -$Expected = ((Get-Content "$Asset.sha256") -split "\s+")[0].ToLower() -$Actual = (Get-FileHash $Asset -Algorithm SHA256).Hash.ToLower() -if ($Actual -ne $Expected) { throw "SHA-256 mismatch for $Asset" } -Expand-Archive $Asset -DestinationPath .\akua-release -Force -$InstallRoot = "$HOME\libexec\akua-v$Version" -New-Item -ItemType Directory -Force "$InstallRoot" | Out-Null -Copy-Item .\akua-release\* "$InstallRoot" -Recurse -Force -New-Item -ItemType Directory -Force "$HOME\bin" | Out-Null -Copy-Item "$InstallRoot\akua.exe" "$HOME\bin\akua.exe" -Force -Copy-Item "$InstallRoot\node_modules" "$HOME\bin\node_modules" -Recurse -Force -& "$HOME\bin\akua.exe" --version -& "$HOME\bin\akua.exe" --help -& "$HOME\bin\akua.exe" commands --limit 1 -& "$HOME\bin\akua.exe" pkg version -``` - -### Supported release artifacts - -| Platform | Architecture | Asset | Runtime baseline | -| --- | --- | --- | --- | -| macOS | Apple Silicon arm64 | `akua-v0.10.1-darwin-arm64.tar.gz` | Bun darwin arm64 | -| macOS | Intel x64 | `akua-v0.10.1-darwin-x64.tar.gz` | Bun darwin x64 | -| Linux | glibc arm64 | `akua-v0.10.1-linux-arm64.tar.gz` | Bun linux arm64 | -| Linux | glibc x64 | `akua-v0.10.1-linux-x64.tar.gz` | Bun linux x64 baseline | -| Windows | x64 | `akua-v0.10.1-windows-x64.zip` | Bun windows x64 baseline | - -Linux musl, Windows arm64, and other systems are not in the tested release -contract. x64 Linux and Windows use Bun's baseline target for older CPUs. Unix -archives preserve executable mode `0755`; the Windows ZIP contains `akua.exe`. -All archives also contain the target-native package runtime under -`node_modules/@akua-dev`. Keep that directory adjacent to the executable. Bun is -not required on the target machine. - -To audit a whole release, download `checksums.txt` plus the archives and run -`sha256sum --check checksums.txt` on Linux or `shasum -a 256 --check -checksums.txt` on macOS. The adjacent `.sha256` files support single-asset -verification. Release assets are never replaced in place; a changed binary -requires a new version. +No Homebrew? See [manual install](docs/install.md) for checksummed release +archives. ## First commands diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..fb7f752 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,98 @@ +# Manual install from GitHub Releases + +[Homebrew](../README.md#install) is the supported install path for almost +everyone. Use this page only when Homebrew isn't available — for example, a +Linux host without Homebrew, or a CI environment that wants a pinned, +checksummed binary instead of going through a package manager. + +Every GitHub Release archive contains the `akua` executable and its +adjacent target-native package runtime, and has an adjacent SHA-256 file. A +release also publishes `checksums.txt` and a complete release manifest. + +## macOS or Linux + +This copy-paste example installs v0.10.1 into `~/.local/bin`. Change `VERSION` +when selecting a newer release. + +```sh +set -eu +VERSION=0.10.1 +case "$(uname -s)-$(uname -m)" in + Darwin-arm64) TARGET=darwin-arm64 ;; + Darwin-x86_64) TARGET=darwin-x64 ;; + Linux-arm64|Linux-aarch64) TARGET=linux-arm64 ;; + Linux-x86_64) TARGET=linux-x64 ;; + *) echo "Unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;; +esac +ASSET="akua-v${VERSION}-${TARGET}.tar.gz" +BASE="https://github.com/akua-dev/cli/releases/download/v${VERSION}" +curl --fail --location --remote-name "${BASE}/${ASSET}" +curl --fail --location --remote-name "${BASE}/${ASSET}.sha256" +if command -v sha256sum >/dev/null 2>&1; then + sha256sum --check "${ASSET}.sha256" +else + shasum -a 256 --check "${ASSET}.sha256" +fi +INSTALL_ROOT="$HOME/.local/libexec/akua-v${VERSION}" +mkdir -p "$INSTALL_ROOT" "$HOME/.local/bin" +tar -xzf "$ASSET" -C "$INSTALL_ROOT" +ln -sfn "$INSTALL_ROOT/akua" "$HOME/.local/bin/akua" +"$HOME/.local/bin/akua" --version +"$HOME/.local/bin/akua" --help +"$HOME/.local/bin/akua" commands --limit 1 +"$HOME/.local/bin/akua" pkg version +``` + +Ensure `~/.local/bin` is on `PATH`. Manual upgrades repeat these steps with a +newer `VERSION`, replacing `~/.local/bin/akua`. The CLI does not self-update. + +## Windows x64 + +Run in PowerShell. This installs v0.10.1 into `%USERPROFILE%\bin`; add that +directory to the user `PATH` if it is not already present. + +```powershell +$ErrorActionPreference = "Stop" +$Version = "0.10.1" +$Asset = "akua-v$Version-windows-x64.zip" +$Base = "https://github.com/akua-dev/cli/releases/download/v$Version" +Invoke-WebRequest "$Base/$Asset" -OutFile $Asset +Invoke-WebRequest "$Base/$Asset.sha256" -OutFile "$Asset.sha256" +$Expected = ((Get-Content "$Asset.sha256") -split "\s+")[0].ToLower() +$Actual = (Get-FileHash $Asset -Algorithm SHA256).Hash.ToLower() +if ($Actual -ne $Expected) { throw "SHA-256 mismatch for $Asset" } +Expand-Archive $Asset -DestinationPath .\akua-release -Force +$InstallRoot = "$HOME\libexec\akua-v$Version" +New-Item -ItemType Directory -Force "$InstallRoot" | Out-Null +Copy-Item .\akua-release\* "$InstallRoot" -Recurse -Force +New-Item -ItemType Directory -Force "$HOME\bin" | Out-Null +Copy-Item "$InstallRoot\akua.exe" "$HOME\bin\akua.exe" -Force +Copy-Item "$InstallRoot\node_modules" "$HOME\bin\node_modules" -Recurse -Force +& "$HOME\bin\akua.exe" --version +& "$HOME\bin\akua.exe" --help +& "$HOME\bin\akua.exe" commands --limit 1 +& "$HOME\bin\akua.exe" pkg version +``` + +## Supported release artifacts + +| Platform | Architecture | Asset | Runtime baseline | +| --- | --- | --- | --- | +| macOS | Apple Silicon arm64 | `akua-v0.10.1-darwin-arm64.tar.gz` | Bun darwin arm64 | +| macOS | Intel x64 | `akua-v0.10.1-darwin-x64.tar.gz` | Bun darwin x64 | +| Linux | glibc arm64 | `akua-v0.10.1-linux-arm64.tar.gz` | Bun linux arm64 | +| Linux | glibc x64 | `akua-v0.10.1-linux-x64.tar.gz` | Bun linux x64 baseline | +| Windows | x64 | `akua-v0.10.1-windows-x64.zip` | Bun windows x64 baseline | + +Linux musl, Windows arm64, and other systems are not in the tested release +contract. x64 Linux and Windows use Bun's baseline target for older CPUs. Unix +archives preserve executable mode `0755`; the Windows ZIP contains `akua.exe`. +All archives also contain the target-native package runtime under +`node_modules/@akua-dev`. Keep that directory adjacent to the executable. Bun is +not required on the target machine. + +To audit a whole release, download `checksums.txt` plus the archives and run +`sha256sum --check checksums.txt` on Linux or `shasum -a 256 --check +checksums.txt` on macOS. The adjacent `.sha256` files support single-asset +verification. Release assets are never replaced in place; a changed binary +requires a new version. diff --git a/test/docs.test.ts b/test/docs.test.ts index 2dfb4f7..590c015 100644 --- a/test/docs.test.ts +++ b/test/docs.test.ts @@ -6,19 +6,38 @@ async function text(path: string): Promise { } describe("distribution documentation", () => { - test("README documents only the implemented GitHub Release and Homebrew channels", async () => { + test("README documents Homebrew as the primary install channel", async () => { const readme = await text("README.md"); expect(readme).toContain("brew install akua-dev/tap/akua"); - expect(readme).toContain("https://github.com/akua-dev/cli/releases/download/"); - expect(readme).toContain("akua-v0.10.1-darwin-arm64.tar.gz"); - expect(readme).toContain("akua-v0.10.1-windows-x64.zip"); - expect(readme).toContain("checksums.txt"); - expect(readme).toContain("sha256sum"); - expect(readme).toContain("Get-FileHash"); + expect(readme).toContain("brew upgrade akua"); expect(readme).not.toMatch(/npm (?:install|i)|bun add|npx .*@akua-dev\/cli/); }); + test("README points to docs/install.md instead of inlining GitHub Release steps", async () => { + const readme = await text("README.md"); + + expect(readme).toContain("[manual install](docs/install.md)"); + expect(readme).not.toContain("https://github.com/akua-dev/cli/releases/download/"); + expect(readme).not.toContain("akua-v0.10.1-darwin-arm64.tar.gz"); + expect(readme).not.toContain("akua-v0.10.1-windows-x64.zip"); + expect(readme).not.toContain("checksums.txt"); + expect(readme).not.toContain("sha256sum"); + expect(readme).not.toContain("Get-FileHash"); + }); + + test("docs/install.md documents the checksummed GitHub Release fallback", async () => { + const install = await text("docs/install.md"); + + expect(install).toContain("https://github.com/akua-dev/cli/releases/download/"); + expect(install).toContain("akua-v0.10.1-darwin-arm64.tar.gz"); + expect(install).toContain("akua-v0.10.1-windows-x64.zip"); + expect(install).toContain("checksums.txt"); + expect(install).toContain("sha256sum"); + expect(install).toContain("Get-FileHash"); + expect(install).toContain("Homebrew"); + }); + test("README explains auth, adaptive output, generated API status, and upgrades honestly", async () => { const readme = await text("README.md");