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.49.0",
"version": "0.50.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
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ plugins/kbagent/

```
# Global options: --json, --verbose, --no-color, --config-dir, --hint client|service (deprecated, use kbagent serve REST API), --deny-writes, --deny-destructive, --allow-env-manage-token
# Headless / token-only (0.50.0+): export KBAGENT_PROJECT_FROM_ENV=1 + KBC_TOKEN + KBC_STORAGE_API_URL to synthesize an in-memory `__env__` project (no `project add`, no config.json on disk; token never persisted). Use `--project __env__`. Same env setup also powers `kbagent serve`.

kbagent project add --project NAME --url URL --token TOKEN
kbagent project list
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.49.0",
"version": "0.50.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: 2 additions & 0 deletions plugins/kbagent/agents/keboola-expert.md
Comment thread
padak marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ a critical failure.
`sync push` fresh-CREATE variable-link resolution + `--branch <id>` default-tree promote = 0.47.2+,
`feature` group (stack/project/user feature flags, Manage API) = 0.48.0+,
`dev-portal` command group = 0.49.0+,
headless `__env__` project (`KBAGENT_PROJECT_FROM_ENV=1` + `KBC_TOKEN` + `KBC_STORAGE_API_URL`) + forgiving stack-URL normalization (bare host / full project deep-link) = 0.50.0+,
`storage retype` is a future composite), you
MUST refuse the task and return a handoff message to the parent:
`"Cannot proceed safely on kbagent <version>. Missing: <commands>.
Expand Down Expand Up @@ -180,6 +181,7 @@ a critical failure.
| Remove a secret / env-var key from a data app | `kbagent data-app secrets-remove --project P --app-id N --key 'KEY' --yes` (0.43.9+: `#` optional; removes encrypted + plain) -- idempotent; missing keys exit 0, `removed: 0` | `tool call update_config` with the secrets sub-dict deleted -- ONLY for batch removes needing a custom change description | `config update --set 'parameters.dataApp.secrets={}'` -- drops EVERY secret, not just the named ones |
| Pre-flight a data-app repo before create | `kbagent data-app validate-repo --git-repo URL --type python-js [--git-pat-env VAR]` (0.29.0+) -- BLOCKING / WARN / OK with help-doc citations; ≤5 GitHub API calls regardless of repo size | git-clone the repo locally and inspect by hand | `data-app create --dry-run` (only shows the request bodies; does not validate repo structure) |
| Rename a project alias | `kbagent project edit --project OLD --new-alias NEW [--dry-run]` (0.31.0+) -- cascades through `config.json` (`projects` key + `default_project`) and the nested-sync directory `<cwd>/<old-alias>/`. Combined with `--url`/`--token` in one call, those mutations target the new alias post-rename. `--dry-run` previews collision detection, planned disk-rename method, and the lineage-cache warning without mutating state. **Lineage cache (if any) is NOT auto-updated**: rebuild via `kbagent lineage build` after the rename | `kbagent project remove` + `kbagent project add` (re-enters the token; loses any nested sync workspace) | hand-editing `~/.config/keboola-agent-cli/config.json` (no validation, easy to miss `default_project` cascade) |
| Run kbagent headless from a daemon / container / CI with only a token in env (no `project add`, no `config.json`) | Export `KBAGENT_PROJECT_FROM_ENV=1` + `KBC_TOKEN` + `KBC_STORAGE_API_URL`, then `kbagent --json storage file-upload --project __env__ --file X` (0.50.0+). Synthesizes an in-memory `__env__` project; token NEVER persisted (stripped on any save); same env setup also powers `kbagent serve` (POST `project=__env__`) | a one-shot `kbagent project add --project env --token ... --url ...` (works but writes the token to `config.json` on disk -- defeats "no local config") | hand-writing a `config.json` with the token, or passing `--token` per command (no such passthrough on storage/job/config commands) |
| Call the running `kbagent serve` from a scheduled-agent subprocess | `kbagent http get/post/patch/delete <PATH>` (0.40.0+) -- uses `KBAGENT_SERVE_URL` + `KBAGENT_SERVE_TOKEN` env vars auto-injected by the scheduler. `kbagent http get /openapi.json` to discover endpoints. Treats the live serve as source-of-truth (no stale local config) | forking `kbagent <command>` (also fine -- `KBAGENT_CONFIG_DIR` is propagated so the spawned CLI sees the SAME config the serve uses; no more "I'm in the wrong directory" surprises) | `curl $KBAGENT_SERVE_URL/...` by hand (works, but `kbagent http` adds auth header automatically, structured error mapping, and JSON-mode formatting) |
| Launch the web UI for an end-user (browser dashboard, no Node BFF) | `kbagent serve --ui [--port PORT] [--ui-dist PATH]` (0.40.0+) -- single-process FastAPI mounts the bundled React SPA at `/`, sets an HttpOnly `kbagent_session` cookie on `GET /` so the browser is auto-authenticated. EventSource SSE works via the same cookie -- no token in URL, JS heap, or access log. Requires the bundled wheel (Node 20+ on the install host) OR `make web-build` from a checkout. CORS origins customisable via `--cors-origin` | `kbagent serve` (plain API) + Vite dev server + Node BFF -- the legacy three-process flow with hot reload, see `web/README.md` "Dev mode" section | inventing a `--token-in-url` flag; running uvicorn directly against `web.frontend.dist` -- the path-rewrite middleware + cookie bootstrap only fire from `kbagent serve --ui` |
| Schedule / manage Agent Tasks | `kbagent agent <verb>` (0.44.0+) -- CRUD `list/show/create/update/delete`, exec `run [--stream]`, history `runs/run-detail/run-events`, util `test/cron-preview/prompt-improve`. Local-only; cron needs `kbagent serve`. See [agent-tasks-cli-workflow](../skills/kbagent/references/agent-tasks-cli-workflow.md) | `kbagent http <verb> /agents...` (0.40.0+) in scheduled subprocesses; Web UI for human authoring | hand-editing `agents.json` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,9 @@ CLI parity for the `/agents` REST surface. Reads/writes `<config_dir>/agents.jso
## Environment Variables
| Variable | Purpose |
|----------|---------|
| `KBC_TOKEN` | Fallback for `--token` |
| `KBC_STORAGE_API_URL` | Default stack URL |
| `KBC_TOKEN` | Fallback for `--token`. Also the credential source for headless `__env__` mode (see `KBAGENT_PROJECT_FROM_ENV`) |
| `KBC_STORAGE_API_URL` | Default stack URL. Also the stack source for headless `__env__` mode |
| `KBAGENT_PROJECT_FROM_ENV` | Set to `1`/`true`/`yes`/`on` to synthesize an in-memory project `__env__` from `KBC_TOKEN` + `KBC_STORAGE_API_URL` (since 0.50.0). Headless / token-only: no `project add`, no `config.json` on disk; token stays in memory (never persisted). Use `--project __env__`. Works for CLI and `kbagent serve`. Fails fast if creds missing |
| `KBC_MANAGE_API_TOKEN` | Manage API token (org setup, project refresh, data-app password). Default-DENY since 0.28.0: requires top-level `--allow-env-manage-token` to opt in, otherwise ignored with a warning. |
| `KBAGENT_CONFIG_DIR` | Override config directory |
| `KBAGENT_SERVE_URL` | Self-URL of `kbagent serve` (used by `kbagent http`; auto-injected into scheduled-agent subprocesses) |
Expand Down
46 changes: 46 additions & 0 deletions plugins/kbagent/skills/kbagent/references/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -2258,3 +2258,49 @@ command without `--dry-run` themselves.
Reads (`dev-portal list`, `dev-portal get`) are unrestricted — peer-research
patterns ("show me how MySQL and Postgres extractors configure themselves")
are agent-friendly via `list --vendor` + `get --app`.

## Headless / token-only invocation: the `__env__` project (since v0.50.0)

A daemon, container, or CI job that has only a token in its environment can run
kbagent with **no `kbagent project add` and no `config.json` on disk**. Set all
three:

```bash
export KBAGENT_PROJECT_FROM_ENV=1
export KBC_TOKEN=<storage-api-token>
export KBC_STORAGE_API_URL=https://connection.<region>.keboola.com
kbagent --json storage file-upload --project __env__ --file screenshot.png
```

kbagent synthesizes an in-memory project under the reserved alias `__env__`.
Pass it as `--project __env__` (or rely on it being the sole/default project for
commands that fall back to the default).

Gotchas:
- **Opt-in is the flag, not the token.** `KBC_TOKEN` alone does nothing here —
it stays a `project add` fallback. Only `KBAGENT_PROJECT_FROM_ENV` (truthy:
`1`/`true`/`yes`/`on`) triggers injection. This avoids a phantom project on a
dev machine that exported `KBC_TOKEN` for an unrelated `project add`.
- **Token is never persisted.** `__env__` is `ephemeral`; even if a write op
triggers a `config.json` write, the env token is stripped first. There is no
way to leak it to disk through normal operation.
- **Fail-fast.** Flag set but `KBC_TOKEN` or `KBC_STORAGE_API_URL` missing →
exit 5 (`config error`) with a clear message, not a silent skip.
- **Same chokepoint for `serve`.** `kbagent serve` started with the same three
env vars exposes `__env__` too — POST endpoints take `project=__env__`. Both
CLI and serve resolve through `ConfigStore.load()`, so one env setup covers
both consumption styles.
- The alias is literally `__env__` (double underscore both sides) — chosen so it
cannot collide with a real user alias. A real project already registered under
`__env__` wins; no injection happens.
- **`__env__` shows `project_id` but a blank name in `project list`.** `load()`
is offline, so the injection recovers `project_id` from the token prefix
(`{projectId}-{tokenId}-{secret}`) but cannot fetch the real project name.
Run `kbagent project status --project __env__` (or `project info`) to verify
the token against the API and see the real name.
- **`KBC_STORAGE_API_URL` is forgiving (since v0.50.0).** A bare host
(`connection.keboola.com`), a trailing slash, or a full project deep-link
(`.../admin/projects/123/dashboard`) all normalize to `https://<host>`. Same
normalization applies to `project add --url` / `project edit --url`. Explicit
`http://` / `file://` is still rejected; a bad URL fails fast with a clean
config error (exit 5), not a traceback.
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.49.0"
version = "0.50.0"
description = "AI-friendly CLI for managing Keboola projects"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
5 changes: 5 additions & 0 deletions src/keboola_agent_cli/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

# Ordered newest-first. Each value is a list of brief one-line descriptions.
CHANGELOG: dict[str, list[str]] = {
"0.50.0": [
"New: headless / token-only invocation (issue #359). Set `KBAGENT_PROJECT_FROM_ENV=1` together with `KBC_TOKEN` + `KBC_STORAGE_API_URL` and kbagent synthesizes an in-memory project under the reserved alias `__env__` -- no `kbagent project add`, no `config.json` on disk. Lets a daemon (e.g. the jasnost bridge), a container, or a CI job run any storage/job/config command with `--project __env__`, or talk to a `kbagent serve` started the same way. Both the CLI and `serve` resolve the project through the same `ConfigStore.load()` chokepoint, so both work from the single env-injection.",
"UX: stack URLs are now normalized everywhere a project is created (`project add`, `project edit --url`, and the headless `__env__` injection). A bare host (`connection.keboola.com`), a trailing slash, surrounding whitespace, or even a full project deep-link (`https://connection.keboola.com/admin/projects/10105/dashboard`) are all reduced to the clean `https://<host>` base instead of erroring. Explicit non-https schemes (`http://`, `file://`, ...) are still rejected as an SSRF / protocol-abuse guard, and an unusable URL in `KBC_STORAGE_API_URL` now fails fast with a clean config error rather than a raw pydantic traceback.",
"Security: the env-synthesized `__env__` project lives in memory only. It is marked `ephemeral` and stripped by `ConfigStore.save()`, so the `KBC_TOKEN` from the environment is never written to `config.json`. Opt-in is explicit (the `KBAGENT_PROJECT_FROM_ENV` flag, not the mere presence of `KBC_TOKEN`) to avoid a phantom project surprising a developer who exported the token only for `project add`. If the flag is set but the credential vars are missing, the CLI fails fast with a clear error instead of silently skipping. Mutating ops that target the synthesized project (`project remove/edit/rename`, branch switch) are rejected with an actionable message rather than reporting a success that silently vanishes on the next load. `project list` recovers the `project_id` offline from the token prefix; the real project name shows via `project status` / `project info`. The org-info backfill that `project status` runs skips the ephemeral project, so even `project status` writes nothing to disk in headless mode.",
],
"0.49.0": [
"New: `kbagent dev-portal` command group — v1 operations against the Keboola Developer Portal (`apps-api.keboola.com`). Lets component developers inspect and update portal entries without leaving the terminal. Read commands (`dev-portal list --vendor V`, `dev-portal get --app VENDOR.APP_ID`) are unrestricted and support peer-config research (pull reference schemas from existing extractors/writers for design reference). Write commands (`dev-portal create`, `dev-portal patch`, `dev-portal upload-icon`, `dev-portal publish`, `dev-portal deprecate`) always print the full pending request diff and then require the user to type a random hex code on a real terminal; there is no `--yes` flag and no env-var bypass; non-TTY shells exit 6 (`EXIT_PERMISSION_DENIED`). `--dry-run` produces the same preview and exits 0 -- the agent-safe path.",
"New: multi-identity credential storage for the Developer Portal. Portal logins (email + password, with optional MFA for personal accounts) are stored per-alias in the same `config.json` as KB project tokens under 0600 protection. Identity lifecycle: `dev-portal identity add --alias A --username U [--password P | --password-stdin] [--role-hint vendor|admin] [--vendor V]`, `identity list`, `identity remove`, `identity edit`, `identity use ALIAS`, `identity current`, `identity verify`.",
Expand Down
7 changes: 7 additions & 0 deletions src/keboola_agent_cli/commands/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,13 @@
KBC_MASTER_TOKEN_* Per-project master token (e.g. KBC_MASTER_TOKEN_PROD)
KBAGENT_CONFIG_DIR Override config directory
KBAGENT_PROJECT Override the pinned default project for this shell/session (beats pin, loses to --project)
KBAGENT_PROJECT_FROM_ENV Set to "1" (or true/yes/on) to synthesize an in-memory project under the
reserved alias __env__ from KBC_TOKEN + KBC_STORAGE_API_URL (since 0.50.0).
Headless / token-only mode: no `project add`, no config.json on disk. Use
`--project __env__` (or rely on it as the sole/default project). The token
lives in memory only -- it is NEVER persisted, even if a write op runs.
Works for both the CLI and `kbagent serve`. Fails fast if the flag is set
but KBC_TOKEN / KBC_STORAGE_API_URL are missing.
KBAGENT_MAX_PARALLEL_WORKERS Max concurrent threads for multi-project ops (default 10, max 100)
KBAGENT_AUTO_UPDATE Set to "false" to disable automatic update on startup
KBAGENT_UPDATED_FROM Set to an older version to trigger "What's new" display on next run
Expand Down
Loading
Loading