Skip to content
Open
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
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ kbagent project member-set-role --project ALIAS --email EMAIL --role admin|guest
kbagent config list [--project NAME] [--component-type TYPE] [--component-id ID] [--branch ID] [--include-rows]
kbagent config detail --project NAME [--project NAME ...] --component-id ID [--config-id ID] [--branch ID] [--with-state]
kbagent config search --query PATTERN [--project NAME] [--component-type TYPE] [--ignore-case] [--regex] [--branch ID]
kbagent config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--change-description TEXT] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config set-default-bucket --project NAME --component-id ID --config-id ID (--bucket BUCKET_ID | --clear) [--dry-run] [--branch ID]
kbagent config rename --project NAME --component-id ID --config-id ID --name "New Name" [--branch ID] [--directory DIR]
kbagent 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]
Expand All @@ -312,7 +312,7 @@ kbagent config set-metadata --project NAME --component-id ID --config-id ID --ke
kbagent config delete-metadata --project NAME --component-id ID --config-id ID --metadata-id ID [--branch ID] [--yes]
kbagent config set-folder --project NAME --component-id ID --config-id ID --name FOLDER [--branch ID]
kbagent config row-create --project NAME --component-id ID --config-id ID --name ROW_NAME [--description D] [--configuration JSON|@file|-] [--is-disabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--change-description TEXT] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config row-delete --project NAME --component-id ID --config-id ID --row-id ID [--branch ID] [--yes]
kbagent config oauth-url --project NAME --component-id ID --config-id ID [--redirect-url URL]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Requires a **super-admin** Manage API token (same kind as `org setup`). Same def
- `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)
- `config detail --project NAME [--project ...] --component-id ID [--config-id ID] [--branch ID] [--with-state]` -- **two modes.** **Single** (with `--config-id`): full config dict, shape unchanged from previous releases (callers depending on `.id`, `.configuration`, `.rows` etc. are unaffected). **Bulk** (omit `--config-id`): returns `{"configs": [...], "errors": [...]}` with every configuration of `--component-id` across one or many projects -- each row tagged with `project_alias`/`branch_id`. One HTTP request per project via `list_components_with_configs` (not one per config; a project with 100 Snowflake writers returns in a single round-trip). `--project` is repeatable in bulk mode; `--config-id` with multiple `--project` is rejected (exit 2) because a single config lives in one project. `--with-state` attaches the runtime `state` dict: single-mode triggers an extra `get_config_state` call, bulk-mode adds `include=state` to the listing call (no N+1)
- `config search --query PATTERN [--project NAME] [-i] [-r] [--branch ID]` -- search config bodies for string/regex (branch-aware)
- `config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]` -- update metadata and/or configuration content. **`#`-prefixed secrets auto-encrypt via the Encryption API before write (fail-closed; since 0.54.0, #378)** -- `--allow-plaintext-on-encrypt-failure` overrides, `--dry-run` keeps plaintext in the diff (ciphertext is non-deterministic). Note `--set '#password=...'` sets a *top-level* key; for a nested secret use `--set 'parameters.#password=...'`. `--set` targets a nested key (e.g. `parameters.db.host=new-host`). `--merge` deep-merges into existing config (preserves sibling keys). `--dry-run` previews changes without applying. Paths are relative to the configuration root (unlike MCP's `update_config` which uses paths relative to `parameters`). **Auto-normalize (0.28.0+; #245 / 0.31.0+; #274)**: `parameters.blocks[].codes[].script` is fixed before pushing to Storage API. **String -> array** (0.28.0+; #245): SQL transformations get statement-level split (respects `'...'` / `"..."` / `$$..$$` / `--` / `#` / `//` / `/* ... */`); Python / R / `kds-team.app-custom-python` get `[script]` wrap. **List-element re-split** (0.31.0+; #274): when `script` is already a list but an element packs multiple `;`-separated statements, each SQL element is re-run through `split_statements()` and replaced inline. Closes the ODBC `Actual statement count N did not match the desired statement count 1` (SQL state 0A000) runtime crash that survives the 0.28.0 string fix. The result envelope's `normalizations: [{path, action: "sql_split"|"wrap_array"|"sql_resplit", before_type, after_type, after_length, before_length?}]` records every change (empty when nothing was malformed; `sql_resplit` adds `before_length` and a `[E]` suffix on `path` pointing at the original element index). Bypassing kbagent (raw REST, MCP `update_sql_transformation` / `create_sql_transformation`) does NOT inherit either pass -- prefer `kbagent config update` for SQL transformation body changes.
- `config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--change-description TEXT] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]` -- update metadata and/or configuration content. `--change-description` sets the new config version's `changeDescription` (the version-history audit line); omit it to keep the auto-generated default (e.g. `Updated configuration via kbagent config update`). Distinct from `--description`, which sets the config's display description. `--dry-run` echoes the `change_description` that would be sent. **`#`-prefixed secrets auto-encrypt via the Encryption API before write (fail-closed; since 0.54.0, #378)** -- `--allow-plaintext-on-encrypt-failure` overrides, `--dry-run` keeps plaintext in the diff (ciphertext is non-deterministic). Note `--set '#password=...'` sets a *top-level* key; for a nested secret use `--set 'parameters.#password=...'`. `--set` targets a nested key (e.g. `parameters.db.host=new-host`). `--merge` deep-merges into existing config (preserves sibling keys). `--dry-run` previews changes without applying. Paths are relative to the configuration root (unlike MCP's `update_config` which uses paths relative to `parameters`). **Auto-normalize (0.28.0+; #245 / 0.31.0+; #274)**: `parameters.blocks[].codes[].script` is fixed before pushing to Storage API. **String -> array** (0.28.0+; #245): SQL transformations get statement-level split (respects `'...'` / `"..."` / `$$..$$` / `--` / `#` / `//` / `/* ... */`); Python / R / `kds-team.app-custom-python` get `[script]` wrap. **List-element re-split** (0.31.0+; #274): when `script` is already a list but an element packs multiple `;`-separated statements, each SQL element is re-run through `split_statements()` and replaced inline. Closes the ODBC `Actual statement count N did not match the desired statement count 1` (SQL state 0A000) runtime crash that survives the 0.28.0 string fix. The result envelope's `normalizations: [{path, action: "sql_split"|"wrap_array"|"sql_resplit", before_type, after_type, after_length, before_length?}]` records every change (empty when nothing was malformed; `sql_resplit` adds `before_length` and a `[E]` suffix on `path` pointing at the original element index). Bypassing kbagent (raw REST, MCP `update_sql_transformation` / `create_sql_transformation`) does NOT inherit either pass -- prefer `kbagent config update` for SQL transformation body changes.
- `config set-default-bucket --project NAME --component-id ID --config-id ID (--bucket BUCKET_ID | --clear) [--dry-run] [--branch ID]` -- set or clear `configuration.storage.output.default_bucket` on a configuration. Discoverable shortcut for the raw-mode workaround at https://keboola.atlassian.net/wiki/spaces/SUP/pages/3770155030/. Read-modify-write that preserves sibling keys; returns `{"changed": false}` when the value already matches the requested state. Honored by output tables that don't pin their own `destination`.
- `config rename --project NAME --component-id ID --config-id ID --name "New Name" [--branch ID] [--directory DIR]` -- rename a configuration (API update + local sync directory rename with git mv support)
- `config delete --project NAME --component-id ID --config-id ID [--branch ID]` -- delete a configuration
Expand All @@ -84,7 +84,7 @@ Requires a **super-admin** Manage API token (same kind as `org setup`). Same def
- `config delete-metadata --project NAME --component-id ID --config-id ID --metadata-id ID [--branch ID] [--yes]` -- delete a configuration metadata entry by its numeric ID (from `metadata-list`)
- `config set-folder --project NAME --component-id ID --config-id ID --name FOLDER [--branch ID]` -- set (or clear, with empty `--name`) the `KBC.configuration.folderName` metadata, which groups configs into named folders in the Keboola UI. See `config-metadata-workflow.md`
- `config row-create --project NAME --component-id ID --config-id ID --name ROW_NAME [--description D] [--configuration JSON|@file|-] [--is-disabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]` -- create a new configuration row. Returns the full row dict with `id`, `name`, `version`. Optional `--configuration` accepts JSON inline, `@file`, or stdin (`-`). `#`-prefixed secrets auto-encrypt before write (fail-closed; since 0.54.0, #378).
- `config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]` -- update an existing configuration row. Pass only the fields you want to change; omitted fields are preserved. `--is-disabled` / `--is-enabled` toggle the row's enabled state. `#`-prefixed secrets auto-encrypt before write (fail-closed; since 0.54.0, #378).
- `config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--change-description TEXT] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]` -- update an existing configuration row. Pass only the fields you want to change; omitted fields are preserved. `--is-disabled` / `--is-enabled` toggle the row's enabled state. `--change-description` sets the new row version's `changeDescription` audit line (default: auto-generated). `#`-prefixed secrets auto-encrypt before write (fail-closed; since 0.54.0, #378).
- `config row-delete --project NAME --component-id ID --config-id ID --row-id ID [--branch ID] [--yes]` -- delete a configuration row. Destructive (gated behind `--allow-destructive`). Branch-aware. Without `--yes` and outside `--json` mode, prompts for interactive confirmation; `--json` mode auto-skips the prompt.
- `config oauth-url --project NAME --component-id ID --config-id ID [--redirect-url URL]` -- return the OAuth authorization URL for a component that uses OAuth authentication. **Requires a master Storage API token** (canManageTokens privilege) -- non-master tokens fail with `MISSING_MASTER_TOKEN` exit 3 on a fail-fast pre-flight check before any HTTP write happens. Open the URL in a browser to complete the OAuth flow.

Expand Down
26 changes: 26 additions & 0 deletions src/keboola_agent_cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,11 @@ def config_update(
"--merge",
help="Deep-merge into existing config instead of replacing",
),
change_description: str | None = typer.Option(
None,
"--change-description",
help="Version changeDescription for the audit trail (default: auto-generated)",
),
dry_run: bool = typer.Option(
False,
"--dry-run",
Expand Down Expand Up @@ -605,6 +610,10 @@ def config_update(
# Preview changes without applying
kbagent config update --project P --component-id C --config-id ID \\
--set 'parameters.db.host=new-host' --dry-run

# Set a meaningful version changeDescription for the audit trail
kbagent config update --project P --component-id C --config-id ID \\
--set 'parameters.db.host=new-host' --change-description "AI-1234: point at new DB host"
"""
formatter = get_formatter(ctx)
service = get_service(ctx, "config_service")
Expand Down Expand Up @@ -666,6 +675,7 @@ def config_update(
set_paths=parsed_sets,
merge=effective_merge,
dry_run=dry_run,
change_description=change_description,
branch_id=branch,
allow_plaintext_fallback=allow_plaintext,
)
Expand Down Expand Up @@ -695,6 +705,9 @@ def config_update(
for change in changes:
formatter.console.print(f" {change}")
formatter.console.print()
change_desc = result.get("change_description")
if change_desc:
formatter.console.print(f"[dim]changeDescription:[/dim] {change_desc}")
_emit_normalizations_warning(formatter, normalizations)
return

Expand Down Expand Up @@ -2172,6 +2185,11 @@ def config_row_update(
"--merge",
help="Deep-merge into existing row config instead of replacing",
),
change_description: str | None = typer.Option(
None,
"--change-description",
help="Version changeDescription for the audit trail (default: auto-generated)",
),
dry_run: bool = typer.Option(
False,
"--dry-run",
Expand Down Expand Up @@ -2226,6 +2244,10 @@ def config_row_update(

# Disable a row (excludes it from job runs)
kbagent config row-update --project P --component-id C --config-id ID --row-id R --is-disabled

# Set a meaningful version changeDescription for the audit trail
kbagent config row-update --project P --component-id C --config-id ID --row-id R \\
--set 'parameters.table=new_table' --change-description "AI-1234: repoint row"
"""
if is_disabled and is_enabled:
formatter = get_formatter(ctx)
Expand Down Expand Up @@ -2282,6 +2304,7 @@ def config_row_update(
set_paths=parsed_sets,
merge=effective_merge,
dry_run=dry_run,
change_description=change_description,
is_disabled=is_disabled_value,
branch_id=branch,
allow_plaintext_fallback=allow_plaintext,
Expand Down Expand Up @@ -2309,6 +2332,9 @@ def config_row_update(
for change in changes:
formatter.console.print(f" {change}")
formatter.console.print()
change_desc = result.get("change_description")
if change_desc:
formatter.console.print(f"[dim]changeDescription:[/dim] {change_desc}")
return

if formatter.json_mode:
Expand Down
4 changes: 2 additions & 2 deletions src/keboola_agent_cli/commands/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
Use storage_workspace_id with `kbagent workspace detail --workspace-id ID`,
NOT parameters.id (which 404s).

kbagent config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config update --project NAME --component-id ID --config-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--configuration-file PATH] [--set PATH=VALUE ...] [--merge] [--change-description TEXT] [--dry-run] [--branch ID] [--allow-plaintext-on-encrypt-failure]
Update config metadata and/or configuration content. --set targets a
nested key (e.g. parameters.db.host=new-host). --merge deep-merges into
existing config (preserves sibling keys). --dry-run previews changes.
Expand Down Expand Up @@ -241,7 +241,7 @@
kbagent config row-create --project NAME --component-id ID --config-id ID --name ROW_NAME [--description D] [--configuration JSON|@file|-] [--is-disabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
Create a new configuration row. Returns the new row ID. Optional --configuration accepts JSON inline, @file, or stdin. #-prefixed secrets auto-encrypt before write (fail-closed; since 0.54.0).

kbagent config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
kbagent config row-update --project NAME --component-id ID --config-id ID --row-id ID [--name N] [--description D] [--configuration JSON|@file|-] [--change-description TEXT] [--is-disabled | --is-enabled] [--branch ID] [--allow-plaintext-on-encrypt-failure]
Update an existing configuration row. Pass only the fields you want to change. #-prefixed secrets auto-encrypt before write (fail-closed; since 0.54.0).

kbagent config row-delete --project NAME --component-id ID --config-id ID --row-id ID [--branch ID] [--yes]
Expand Down
4 changes: 4 additions & 0 deletions src/keboola_agent_cli/server/routers/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ConfigUpdate(BaseModel):
set_paths: list[tuple[str, Any]] | None = None
merge: bool = False
dry_run: bool = False
change_description: str | None = None
branch_id: int | None = None


Expand All @@ -36,6 +37,7 @@ class ConfigUpdateRow(BaseModel):
description: str | None = None
configuration: dict[str, Any] | None = None
is_disabled: bool | None = None
change_description: str | None = None
branch_id: int | None = None


Expand Down Expand Up @@ -156,6 +158,7 @@ def config_update(
set_paths=body.set_paths,
merge=body.merge,
dry_run=body.dry_run,
change_description=body.change_description,
branch_id=body.branch_id,
)

Expand Down Expand Up @@ -385,6 +388,7 @@ def row_update(
name=body.name,
description=body.description,
configuration=body.configuration,
change_description=body.change_description,
is_disabled=body.is_disabled,
branch_id=body.branch_id,
)
Expand Down
Loading