Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"plugins": [
{
"name": "kbagent",
"version": "0.59.0",
"version": "0.60.0",
"source": "./plugins/kbagent",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, call MCP tools, manage dev branches, and debug SQL in workspaces",
"category": "development"
Expand Down
91 changes: 91 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Release wheel

# Publish a prebuilt wheel as a GitHub Release asset so end users install a
# ready-made artifact instead of git-building from source on their machine.
#
# Why this exists (issue #353): `uv tool install git+https://github.com/keboola/cli`
# rebuilds the bundled React SPA via `npm ci` + `vite build` on EVERY install,
# and the uv cache does not cover the npm build. On WSL2 that is 2-4 minutes and
# blows past the auto-update timeout. Building the (universal `py3-none-any`)
# wheel ONCE here, on a fast Linux runner, turns the user-side install/update
# into a few-seconds download. `build_kbagent_upgrade_command` and the
# `install.sh` bootstrap both consume the asset this workflow uploads.

on:
# Fires for both normal and prerelease (beta) releases -- betas get an asset
# too, so `kbagent update --beta` benefits from the same fast path.
release:
types: [published]
# Manual backfill: attach a wheel to an already-published release that predates
# this workflow (e.g. v0.59.0). Provide the existing tag as input.
workflow_dispatch:
inputs:
tag:
description: "Existing release tag to build + attach the wheel to (e.g. v0.59.0)"
required: true
type: string

permissions:
# Required for `gh release upload` to attach the asset.
contents: write

jobs:
build-and-upload:
runs-on: ubuntu-latest
# Route the user-controllable tag through a job env var instead of inlining
# the GitHub expression into shell `run:` steps (Actions security hardening).
env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
# Check out the EXACT released commit, not the default branch. The wheel
# version is derived from pyproject.toml at this ref, so it must match the
# tag or the upload step's version guard below fails.
- uses: actions/checkout@v5
with:
ref: ${{ env.TAG }}

- uses: astral-sh/setup-uv@v7
with:
# Pin uv to download the release asset directly instead of fetching the
# rate-limited uv.ndjson manifest on shared CI egress IPs (see ci.yml).
version: "0.11.16"

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- uses: actions/setup-node@v6
with:
# Node is needed for the hatch build hook to compile the SPA into the
# wheel (scripts/hatch_build.py). Without it the wheel ships UI-less.
node-version: "20"
package-manager-cache: false

- name: Build the wheel (with bundled UI)
run: uv build --wheel

- name: Assert the SPA is bundled
# Guards against shipping a UI-less wheel if the npm build silently
# degraded -- `kbagent serve --ui` would break for everyone otherwise.
run: python scripts/check_wheel_ui.py --expect-ui

- name: Verify the wheel version matches the tag
# The asset URL that build_kbagent_upgrade_command / install.sh construct
# is `keboola_agent_cli-<version>-py3-none-any.whl` under the `v<version>`
# tag. A mismatch here means clients would build a 404 URL, so fail loud.
run: |
tag="$TAG"
version="${tag#v}"
expected="dist/keboola_agent_cli-${version}-py3-none-any.whl"
if [ ! -f "$expected" ]; then
echo "::error::Expected $expected but built: $(ls dist/)"
exit 1
fi
echo "OK: $expected matches tag $tag"

- name: Upload the wheel to the release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --clobber makes a re-run idempotent (overwrites instead of erroring on
# "asset already exists").
run: gh release upload "$TAG" dist/*.whl --clobber
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,10 @@ kbagent update [--beta]
# `--beta` (or env `KBAGENT_INCLUDE_PRERELEASE=1`) opts into pre-release versions
# (PEP 440 betas/rc, e.g. 0.43.0b1). Default (no flag) is stable-only -- auto-update
# startup hook never silently lands on a beta.
# Since 0.60.0 install + self-update prefer a prebuilt wheel Release asset (fast, no
# source build; falls back to git+ when absent). Env `KBAGENT_UPDATE_TIMEOUT` (integer
# seconds, default 300) raises the self-update subprocess timeout for the slow git+
# fallback on WSL. Bootstrap install: `curl -LsSf .../main/install.sh | sh`.
kbagent changelog [--limit N] [--full]
# Default shows a one-line summary (first sentence) per version; --full / -v expands every note.
kbagent serve [--host HOST] [--port PORT] [--ui] [--ui-dist PATH] [--reload] [--log-level LVL] [--cors-origin ORIGIN] [--config-dir DIR]
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ No more switching between the UI, old CLI, MCP server, and raw API calls. `kbage

## Install

```bash
curl -LsSf https://github.com/ghraw/keboola/cli/main/install.sh | sh
```

This installs a **prebuilt wheel** from the latest GitHub release -- a few-seconds download, no source build. Building from `git+` instead recompiles the bundled React SPA via npm on every install, which takes minutes on WSL ([#353](https://github.com/keboola/cli/issues/353)). The script bundles the `[server]` extras by default (set `KBAGENT_NO_SERVER=1` for a CLI-only install) and needs only `curl` + [`uv`](https://docs.astral.sh/uv/).

Prefer to build from source, or pin a specific ref?

```bash
uv tool install git+https://github.com/keboola/cli
```

Auto-updates kbagent **and** its `keboola-mcp-server` dependency on every launch (since 0.30.1) -- no more silently running on a six-month-old MCP server. Run `kbagent changelog` to see what changed.
Auto-updates kbagent **and** its `keboola-mcp-server` dependency on every launch (since 0.30.1) -- no more silently running on a six-month-old MCP server; the self-update prefers the prebuilt wheel when available. Run `kbagent changelog` to see what changed.

## Web UI (optional)

Expand Down
86 changes: 86 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/sh
# kbagent bootstrap installer.
#
# Usage:
# curl -LsSf https://github.com/ghraw/keboola/cli/main/install.sh | sh
#
# Installs the kbagent CLI from a PREBUILT wheel attached to the latest GitHub
# release -- no source build. This is the fast path for issue #353: building
# from `git+` recompiles the bundled React SPA via npm on every install, which
# takes 2-4 minutes on WSL2. A prebuilt wheel is a few-seconds download instead.
#
# Requirements: `uv` (the install backend) and `curl`. The guide installs uv a
# couple of steps before this. If no wheel asset exists for the latest release
# yet (older releases predate the release workflow), this falls back to the
# `git+` source build so the install still succeeds.
#
# Env knobs:
# KBAGENT_NO_SERVER=1 install CLI-only (skip the [server] extras: FastAPI/
# uvicorn for `kbagent serve`). Default bundles them so
# `kbagent serve --ui` works out of the box.

set -eu

REPO="keboola/cli"
PKG="keboola-agent-cli"
DIST="keboola_agent_cli" # normalized distribution name used in the wheel filename

info() { printf '%s\n' "$*" >&2; }

# --- preconditions --------------------------------------------------------
if ! command -v uv >/dev/null 2>&1; then
info "error: 'uv' was not found on PATH. Install it first, then re-run:"
info " curl -LsSf https://astral.sh/uv/install.sh | sh"
info " source \$HOME/.local/bin/env # or restart your shell"
exit 1
fi

# Pick the install spec. [server] pulls in FastAPI/uvicorn so `kbagent serve`
# (REST + MCP + UI) works; KBAGENT_NO_SERVER=1 opts out for a lighter install.
if [ "${KBAGENT_NO_SERVER:-}" = "1" ]; then
spec="$PKG"
else
spec="${PKG}[server]"
fi

# --- resolve the latest release version -----------------------------------
# Read the redirect target of /releases/latest instead of calling the GitHub
# API -- no token, no 60-req/h rate limit. The effective URL after following
# redirects looks like https://github.com/keboola/cli/releases/tag/v0.59.0.
info "Resolving latest ${PKG} release..."
final_url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
"https://github.com/${REPO}/releases/latest" 2>/dev/null || true)
version=$(printf '%s' "$final_url" | sed -n 's#.*/releases/tag/v\{0,1\}##p')

# --- install --------------------------------------------------------------
installed=0
if [ -n "$version" ]; then
wheel_url="https://github.com/${REPO}/releases/download/v${version}/${DIST}-${version}-py3-none-any.whl"
# Confirm the asset exists before committing to it (a release may predate the
# wheel-publishing workflow and have no asset attached).
if curl -fsSL -I "$wheel_url" >/dev/null 2>&1; then
info "Installing prebuilt wheel v${version} (no build)..."
if uv tool install --force "${spec} @ ${wheel_url}"; then
installed=1
else
info "Prebuilt wheel install failed; falling back to source build."
fi
else
info "No prebuilt wheel for v${version} yet; falling back to source build."
fi
else
info "Could not resolve the latest version; falling back to source build."
fi

if [ "$installed" -ne 1 ]; then
info "Building from source via git+ (this can take a few minutes on WSL)..."
uv tool install --force "${spec} @ git+https://github.com/${REPO}"
fi

# --- verify ---------------------------------------------------------------
info ""
if command -v kbagent >/dev/null 2>&1; then
info "Done. $(kbagent --version 2>/dev/null || echo 'kbagent installed')"
else
info "Done. Restart your shell, then run: kbagent --version"
fi
2 changes: 1 addition & 1 deletion plugins/kbagent/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kbagent",
"version": "0.59.0",
"version": "0.60.0",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, call MCP tools, manage dev branches, and debug SQL in workspaces",
"author": {
"name": "Keboola",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "keboola-agent-cli"
version = "0.59.0"
version = "0.60.0"
description = "AI-friendly CLI for managing Keboola projects"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
Loading
Loading