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.89.0",
"version": "0.90.0",
"source": "./plugins/kbagent",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, sync configs as files, manage dev branches, and debug SQL in workspaces",
"category": "development"
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ kbagent data-app git-credentials-create --project NAME --app-id ID --type ssh_ke

kbagent component list [--project NAME] [--type TYPE] [--query QUERY]
kbagent component detail --component-id ID [--project NAME]
# component detail (since vNEXT): the AI Service indexes the PUBLIC catalog only, so a private/
# component detail (since 0.90.0): the AI Service indexes the PUBLIC catalog only, so a private/
# deprecated component the project can run (keboola.mcp-server-tool, keboola.data-apps) 404'd
# there while `component list` showed it -- over `serve` as an HTTP 502. A NOT_FOUND now falls
# back to the project's Storage component catalog; `documentation_source` ("ai_service" vs
Expand Down Expand Up @@ -965,7 +965,7 @@ kbagent update [--beta]
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] [--no-banner]
# `--no-banner` (since vNEXT): suppress the web UI's "What's new" popup. The UI shows a curated
# `--no-banner` (since 0.90.0): suppress the web UI's "What's new" popup. The UI shows a curated
# per-version highlights modal once per version (localStorage `kbagent.whatsnew.seen`); this flag
# turns the UNSOLICITED popup off fleet-wide. Surfaced to the SPA via `GET /ui-config`
# ({"banner": bool}) -- NOT injected into index.html: that injection point was removed in favour of
Expand Down
16 changes: 8 additions & 8 deletions docs/web-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ else lives here, with their own agents that know their projects.
Auto-generated OpenAPI spec at `/openapi.json`, Swagger UI at `/docs`.

An upstream Keboola failure surfaces through one global handler: a
`NOT_FOUND` answers **404** (since vNEXT — it used to be 502, which told
`NOT_FOUND` answers **404** (since 0.90.0 — it used to be 502, which told
callers to retry a request that can never succeed), an expired/missing browser
session answers **401**, and every other `KeboolaApiError` answers **502**. The
body is always the `{"status": "error", "error": {"code", "message"}}`
Expand Down Expand Up @@ -133,9 +133,9 @@ A NERD-themed React SPA that drives the API:
- **Command palette** — `Ctrl+K` / `Cmd+K` anywhere: fuzzy jump to any
page, switch the active project, toggle the theme, open Swagger `/docs`,
reopen **What's new**. Arrows + enter, esc closes.
- **What's new popup** *(since vNEXT)* — a curated per-version highlights
- **What's new popup** *(since 0.90.0)* — a curated per-version highlights
modal, shown once per version. See
[What's-new popup](#whats-new-popup-since-vnext) below for the curated
[What's-new popup](#whats-new-popup) below for the curated
list's location, the storage key, and the `--no-banner` opt-out.
- **Dashboard** — greeting, big Kai chat input, stat tiles (projects /
agents / doctor / recent jobs / PAYG credits), scheduled-agent
Expand Down Expand Up @@ -324,7 +324,7 @@ session cookie, and the auth middleware accepts that cookie whenever no
`Authorization` header is present. Scripted callers keep using the header.

Two layers keep that cookie from going stale across server restarts
*(since vNEXT)* — previously a restart (new token) could leave a tab that
*(since 0.90.0)* — previously a restart (new token) could leave a tab that
reloaded from the browser cache silently 401-ing on every API call, with
each list rendering as empty:

Expand All @@ -338,11 +338,11 @@ each list rendering as empty:
`SESSION_NOT_FOUND` the banner carries the server message, which names
the on-host `kbagent auth login` remedy).

### What's-new popup *(since vNEXT)*
### What's-new popup

The web UI shows a curated per-version highlights modal on load, once per
version, so features like the command palette get discovered instead of
waiting to be stumbled upon.
*(since 0.90.0)* The web UI shows a curated per-version highlights modal on
load, once per version, so features like the command palette get discovered
instead of waiting to be stumbled upon.

**Curated list — `web/frontend/src/whatsnew.ts`.** A hand-maintained
`WhatsNewRelease[]`, deliberately *not* the raw `changelog.py` output: the
Expand Down
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.89.0",
"version": "0.90.0",
"description": "AI-friendly interface to Keboola Connection projects — explore configs, jobs, lineage, sync configs as files, manage dev branches, and debug SQL in workspaces",
"author": {
"name": "Keboola",
Expand Down
8 changes: 8 additions & 0 deletions plugins/kbagent/agents/keboola-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ its absence is NOT a promise the entry is version-independent (see §1 Rule 6).
Service-Account component (`keboola.ex-linkedin-ads`, ...) fails with an
opaque empty-body 400 -- check the version before blaming the action or the
credentials.
- `component detail` on **0.90.0+** falls back to the project's Storage catalog
when the AI Service does not index the component (private/deprecated:
`keboola.mcp-server-tool`, `keboola.data-apps`) -- it used to NOT_FOUND there
while `component list` showed the component. Read `documentation_source`
before concluding anything from counts: on `storage_catalog` the
`examples_count` is always 0 because that source carries no examples, NOT
because the component ships none. Reach for `config examples` or an existing
config in the project instead.

**Reading job logs / table usage / narrow config search (0.88.0+)**
- `job detail --log-tail-lines N` -- the ONLY route to an already-finished job's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Requires a **super-admin** Manage API token (same kind as `org setup`). Same def
- `component sync-action ACTION_NAME --component-id ID --project ALIAS (--config-id ID [--row-id ID] | --config-data JSON|@file|-) [--branch ID] [--timeout N]` (since 0.73.0) -- run a synchronous component action (`testConnection`, `getTables`, ...) on the `sync-actions.{stack}` service. `ACTION_NAME` is freeform (component-defined; discover via `component detail` `synchronous_actions`). `--row-id` shallow-merges the row over the root config at TOP level only (row `parameters`/`storage` replace root wholesale -- NOT deep merge; MCP `run_sync_action` parity). `--config-data` sends explicit `configData` verbatim. Response is action-specific pass-through. Ports the `run_sync_action` MCP tool. **Since 0.89.0 (#620)** the ROOT configuration's `authorization` and `runtime` blocks are forwarded into `configData` too -- root only (a `--row-id` never overrides them), and only when non-empty. `authorization.oauth_api.id` is the OAuth broker reference the sync-actions service resolves and decrypts, so on 0.88.0 and earlier every sync action on an OAuth / Service-Account component (`keboola.ex-linkedin-ads`, ...) failed with an opaque empty-body 400.
- `config examples --component-id ID [--project NAME] [--row]` (since 0.73.0) -- sample root/row configurations from the AI-service component detail. `--json` emits `{component_id, root_examples, row_examples}`; `--row` limits to row examples. Ports the `get_config_examples` MCP tool.
- `component list [--project NAME] [--type TYPE] [--query "text"]` -- list/search components (AI-powered with `--query`)
- `component detail --component-id ID [--project NAME]` -- show component schema, docs URL, examples. **Since vNEXT** a component the AI Service does not index (private/deprecated: `keboola.mcp-server-tool`, `keboola.data-apps`) no longer errors -- it falls back to the project's Storage component catalog. `documentation_source` (`"ai_service"` vs `"storage_catalog"`) is on BOTH paths and tells them apart; the fallback has NO configuration examples (`examples_count`/`row_examples_count` always 0), so read `documentation_source` before treating 0 as "this component ships none". `NOT_FOUND` is still raised when both sources miss. **Since vNEXT** omitting `--project` really does use the first configured project -- on <= 0.89.x it failed with `CONFIG_ERROR: Project 'None' not found` despite the help text, so pass `--project` explicitly there. See `gotchas.md`.
- `component detail --component-id ID [--project NAME]` -- show component schema, docs URL, examples. **Since 0.90.0** a component the AI Service does not index (private/deprecated: `keboola.mcp-server-tool`, `keboola.data-apps`) no longer errors -- it falls back to the project's Storage component catalog. `documentation_source` (`"ai_service"` vs `"storage_catalog"`) is on BOTH paths and tells them apart; the fallback has NO configuration examples (`examples_count`/`row_examples_count` always 0), so read `documentation_source` before treating 0 as "this component ships none". `NOT_FOUND` is still raised when both sources miss. **Since 0.90.0** omitting `--project` really does use the first configured project -- on <= 0.89.x it failed with `CONFIG_ERROR: Project 'None' not found` despite the help text, so pass `--project` explicitly there. See `gotchas.md`.

## Configuration Browsing
- `config list [--project NAME] [--component-type TYPE] [--component-id ID] [--branch ID] [--include-rows]` -- list configs across projects (branch-aware). With `--include-rows` each row extends to include the full `configuration` and `rows` body (noticeably larger payload -- use only when the bodies are needed; the summary default covers name/description/component/last_modified/folder)
Expand All @@ -131,7 +131,7 @@ Requires a **super-admin** Manage API token (same kind as `org setup`). Same def
- `config delete --project NAME --component-id ID --config-id ID [--branch ID] [--dry-run]` -- SOFT-delete a configuration into the Storage trash (restorable). Since 0.89.0 it locates the config first and a config already in the trash is NOT deleted again -- the raw API purges permanently on a second DELETE (the retry-after-timeout trap); kbagent reports `already_in_trash` and exits 0 instead. `--dry-run` reports the located state without writing. Undo with `config restore`. Permission class `destructive`
- `config restore --project NAME --component-id ID --config-id ID [--branch ID]` *(since v0.89.0)* -- restore a trashed configuration (versions, rows and metadata come back). Only works on a config currently in the trash. Permission class `write`
- `config trash-list --project NAME [--component-id ID] [--branch ID]` *(since v0.89.0)* -- list configurations in the trash; each row carries `component_id`, `config_id`, `name`, `version` and `deleted_at`, which is exactly what `config restore` needs. Permission class `read`
- `config new --component-id ID [--project NAME] [--name NAME] [--output-dir DIR] [--push --no-files --description D --configuration JSON|@file|- --configuration-file PATH --no-validate --branch ID --dry-run --allow-plaintext-on-encrypt-failure]` -- **two modes**. **Default (no `--push`)**: scaffold new config from component schema; writes files to `--output-dir` or prints to stdout. **No Storage write calls** -- one AI Service read fetches the component schema/examples (that is what `--project` authenticates even without `--push`). **With `--push`** (0.33.0+, requires `--project` + non-empty `--name`): also POSTs to `/v2/storage/components/{cid}/configs` for a one-shot remote create. `#`-prefixed secrets in the pushed body auto-encrypt via the Encryption API first (fail-closed; since 0.54.0, #378; `--allow-plaintext-on-encrypt-failure` overrides). `--no-files` skips the filesystem step entirely (FIIA-style empty-shell pattern). `--configuration` / `--configuration-file` override the POSTed body (default is `{}`, with validation auto-skipped for the default empty shell). `--dry-run` previews the planned POST + validation result without creating. Since 0.89.0, `--push` + `--output-dir` writes the scaffold WITH `_keboola.config_id` into the subtree of the branch the config was created in (registering the branch in the manifest when missing), so the next `sync push` adopts it instead of duplicating (issue #644); with an explicit body the local file mirrors the pushed encrypted configuration. Schema validation runs by default when an explicit body is given (fail-closed: `ConfigError` exit 5 on mismatch) but skips silently if the AI Service has no schema for the component or returns an error; `--no-validate` opts out. Works for ALL component types including `keboola.snowflake-transformation`. **Since vNEXT** the scaffold mode's optional `--project` really does fall back to the first configured project when omitted -- on <= 0.89.x it failed with `CONFIG_ERROR: Project 'None' not found` (see `gotchas.md`).
- `config new --component-id ID [--project NAME] [--name NAME] [--output-dir DIR] [--push --no-files --description D --configuration JSON|@file|- --configuration-file PATH --no-validate --branch ID --dry-run --allow-plaintext-on-encrypt-failure]` -- **two modes**. **Default (no `--push`)**: scaffold new config from component schema; writes files to `--output-dir` or prints to stdout. **No Storage write calls** -- one AI Service read fetches the component schema/examples (that is what `--project` authenticates even without `--push`). **With `--push`** (0.33.0+, requires `--project` + non-empty `--name`): also POSTs to `/v2/storage/components/{cid}/configs` for a one-shot remote create. `#`-prefixed secrets in the pushed body auto-encrypt via the Encryption API first (fail-closed; since 0.54.0, #378; `--allow-plaintext-on-encrypt-failure` overrides). `--no-files` skips the filesystem step entirely (FIIA-style empty-shell pattern). `--configuration` / `--configuration-file` override the POSTed body (default is `{}`, with validation auto-skipped for the default empty shell). `--dry-run` previews the planned POST + validation result without creating. Since 0.89.0, `--push` + `--output-dir` writes the scaffold WITH `_keboola.config_id` into the subtree of the branch the config was created in (registering the branch in the manifest when missing), so the next `sync push` adopts it instead of duplicating (issue #644); with an explicit body the local file mirrors the pushed encrypted configuration. Schema validation runs by default when an explicit body is given (fail-closed: `ConfigError` exit 5 on mismatch) but skips silently if the AI Service has no schema for the component or returns an error; `--no-validate` opts out. Works for ALL component types including `keboola.snowflake-transformation`. **Since 0.90.0** the scaffold mode's optional `--project` really does fall back to the first configured project when omitted -- on <= 0.89.x it failed with `CONFIG_ERROR: Project 'None' not found` (see `gotchas.md`).
- `config clone --project P --component-id ID --config-id ID --name NAME [--target-project P2] [--description D] [--set PATH=VALUE ...] [--secret PATH=VALUE ...] [--branch ID] [--target-branch ID] [--dry-run] [--allow-plaintext-on-encrypt-failure]` (0.84.2+, #587) -- duplicate a configuration **whole**. Reach for this instead of reading `config detail` and rebuilding a body: copying only `configuration["parameters"]` silently drops its siblings (`runtime`, `storage`, `authorization`), and a lost `runtime.parallelism` makes Keboola fall back to `parallelism: 1` -- the reporter's 65-row writer went sequential, 140 min instead of ~60-90, with nothing in any output pointing at it. **Same project** (default): server-side copy via `POST .../configs/{id}/versions/{v}/create`; rows and `KBC::` encrypted values travel with it (verified live). `--set PATH=VALUE` is applied as a follow-up update on the copy, so an override can never be the reason a key went missing. **Cross project** (`--target-project`): reassembled client-side and rows recreated one by one, because encrypted values **cannot** travel -- a Keboola ciphertext is scoped to the project it was encrypted in. Any `KBC::` value makes the clone **fail with exit 5**, listing every path, until re-supplied via `--secret PATH=VALUE` (encrypted in the TARGET project on write). `--dry-run` reports those paths instead of refusing -- run it first to learn what to gather. Storage bucket/table IDs are copied **verbatim, never remapped**; `sync clone` is the command that remaps.
- `config variables-set --project NAME --component-id ID --config-id ID --var KEY=VALUE [--var ...] [--replace] [--variables-id ID] [--values-id ID] [--branch ID] [--dry-run] [--allow-plaintext-on-encrypt-failure] [--yes]` -- attach variable values to a config. Auto-creates a sibling `keboola.variables` config + default row on first use and links it via the parent's `runtime.variables_id` / `variables_values_id`. Defaults to merge; `--replace` drops keys not in `--var`. `#`-prefixed values encrypt via the Encryption API (fail-closed; exit non-zero on `ENCRYPTION_FAILED`). See `variables-workflow.md`
- `config variables-get --project NAME --component-id ID --config-id ID [--branch ID]` -- resolve `variables_id` + `values_id` from the parent config and fetch the current KEY=VALUE map. Returns `{linked: bool, variables_id, values_id, values}`; `linked=false` means the parent has no variables attached
Expand Down Expand Up @@ -440,7 +440,7 @@ CLI parity for the `/agents` REST surface. Reads/writes `<config_dir>/agents.jso
- `doctor` -- health checks (no `--fix` since v0.85.0 -- it only installed the MCP server). Includes a `sync_secrets` check (since 0.55.0): when run inside a sync working tree (`.keboola/manifest.json`), warns if any in-sync config holds plaintext `#`-secrets (#378); `skip` outside a sync tree. Also an `mcp_tool_tasks` check: FAILs when `agents.json` holds tasks using the removed `mcp_tool` action, listing each task in `details.tasks[]` -- map the tool to its native command via `docs/mcp-migration.md` and recreate the task as `--type cli_command`
- `version` -- show the kbagent version and check for kbagent updates (kbagent only since v0.85.0); on a standalone binary it advertises the native channel's upgrade command instead of `kbagent update` (v0.79.0+)
- `context` -- full usage instructions for AI agents
- `serve [--host HOST] [--port PORT] [--ui] [--ui-dist PATH] [--reload] [--log-level LVL] [--cors-origin ORIGIN] [--config-dir DIR] [--no-banner]` -- launch the FastAPI HTTP server backing the web UI. `--ui` mounts the built SPA at `/` and authenticates the browser with an HttpOnly `kbagent_session` cookie (nothing is injected into `index.html`). `--no-banner` *(since vNEXT)* suppresses the UI's "What's new" popup -- a curated per-version highlights modal shown once per version, dismissal persisted in localStorage `kbagent.whatsnew.seen`. The SPA reads the switch from `GET /ui-config` -> `{"banner": bool}`; it gates only the UNSOLICITED popup, so the command palette's "What's new" action still opens it on request. Requires the optional `server` extra
- `serve [--host HOST] [--port PORT] [--ui] [--ui-dist PATH] [--reload] [--log-level LVL] [--cors-origin ORIGIN] [--config-dir DIR] [--no-banner]` -- launch the FastAPI HTTP server backing the web UI. `--ui` mounts the built SPA at `/` and authenticates the browser with an HttpOnly `kbagent_session` cookie (nothing is injected into `index.html`). `--no-banner` *(since 0.90.0)* suppresses the UI's "What's new" popup -- a curated per-version highlights modal shown once per version, dismissal persisted in localStorage `kbagent.whatsnew.seen`. The SPA reads the switch from `GET /ui-config` -> `{"banner": bool}`; it gates only the UNSOLICITED popup, so the command palette's "What's new" action still opens it on request. Requires the optional `server` extra

## Global Flags
| Flag | Description |
Expand Down
Loading
Loading