@carllee1983/dbcli 2.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/skills/dbcli/reference.md +38 -6
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/skills/dbcli/reference.md +38 -6
- package/CHANGELOG.md +471 -0
- package/assets/reference.md +38 -6
- package/dist/cli-runtime.mjs +2536 -1332
- package/dist/cli.mjs +4 -1
- package/dist/core.d.ts +172 -420
- package/dist/core.mjs +354 -2697
- package/gemini-extension.json +1 -1
- package/package.json +4 -1
- package/plugins/dbcli-agent/.codex-plugin/plugin.json +1 -1
- package/plugins/dbcli-agent/skills/dbcli/reference.md +38 -6
- package/skills/dbcli/reference.md +38 -6
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carllee1983/dbcli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Database CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -70,6 +70,8 @@
|
|
|
70
70
|
"release:check": "bash scripts/release-check.sh",
|
|
71
71
|
"plugin:sync": "bun run scripts/sync-plugin-assets.ts --write",
|
|
72
72
|
"plugin:check": "bun run scripts/sync-plugin-assets.ts",
|
|
73
|
+
"manifest:sync": "bun run scripts/check-plugin-manifests.ts --write",
|
|
74
|
+
"manifest:check": "bun run scripts/check-plugin-manifests.ts",
|
|
73
75
|
"test": "bun test --timeout 30000",
|
|
74
76
|
"test:unit": "bun test tests/unit tests/core",
|
|
75
77
|
"test:integration": "bun test tests/integration",
|
|
@@ -82,6 +84,7 @@
|
|
|
82
84
|
"platform:check": "bun run scripts/check-platform-parity.ts",
|
|
83
85
|
"agent-core:check": "bun run scripts/check-agent-core-purity.ts",
|
|
84
86
|
"core-stdout:check": "bun run scripts/check-core-no-stdout.ts",
|
|
87
|
+
"plan:check": "bun run scripts/check-plan-acceptance.ts",
|
|
85
88
|
"typecheck": "tsc --noEmit --pretty false",
|
|
86
89
|
"typecheck:tests": "tsc --noEmit --pretty false -p tsconfig.tests.json",
|
|
87
90
|
"test:perf": "bun test ./tests/perf/*.bench.ts",
|
|
@@ -1573,7 +1573,7 @@ Opt-in flag trio that persists a **VerificationArtifact JSON** (schema v1) under
|
|
|
1573
1573
|
|
|
1574
1574
|
**Planned vs Result evidence.** `dbcli skill tasks plan safe-backfill-verify --format json` returns a plan containing a `verification` block with `status: "planned"`. That block is the **planned** evidence definition — it describes which check will run. Running `assert --write-verification-artifact` on the actual data produces **result** evidence (`status: "verified"` or `status: "not_verified"`). The two records are distinct; `"planned"` does **not** indicate that verification has run or passed.
|
|
1575
1575
|
|
|
1576
|
-
> **
|
|
1576
|
+
> **Numeric note:** Postgres returns `count(*)` and `sum()` as bigint, which arrives as a string. A numeric expectation is compared numerically, so `value == 0` matches it without a cast. A quoted expectation stays a text comparison: `value == "0"` matches the text, not the number. The `::int` casts in the examples below are harmless and no longer required.
|
|
1577
1577
|
|
|
1578
1578
|
```bash
|
|
1579
1579
|
dbcli assert "SELECT count(*)::int FROM orders WHERE status IS NULL" \
|
|
@@ -2637,7 +2637,8 @@ Inside the shell:
|
|
|
2637
2637
|
> not as the answer. Tier one (the y/N on ordinary writes) is deliberately not wired here — every line
|
|
2638
2638
|
> is typed by a person. Piped input (`dbcli shell < script.sql`) has nobody to answer, so a
|
|
2639
2639
|
> tier-two statement is refused and the remaining lines still run. Redis, MongoDB and
|
|
2640
|
-
> Elasticsearch shells are
|
|
2640
|
+
> Elasticsearch shells do not use the write gate; their writes are bounded by the
|
|
2641
|
+
> connection's permission tier instead.
|
|
2641
2642
|
>
|
|
2642
2643
|
> dbcli **subcommands** typed in the shell (`query "..."`, `delete ...`) run as separate
|
|
2643
2644
|
> processes with no stdin, so they cannot ask anything: a tier-two statement there is
|
|
@@ -2721,7 +2722,7 @@ dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a
|
|
|
2721
2722
|
|
|
2722
2723
|
| Flag | Purpose | Default |
|
|
2723
2724
|
|---|---|---|
|
|
2724
|
-
| `--claims <file>` | Required JSON file with exactly `subject` and `claims`; every claim has an `id` and plain-language `text`. SQL-shaped text, credentials, error content, and blacklisted identifiers are rejected. | — |
|
|
2725
|
+
| `--claims <file>` | Required JSON file with exactly `subject` and `claims`; every claim has an `id` and plain-language `text`. SQL-shaped text, credentials, error content, and blacklisted identifiers are rejected. A blacklisted identifier has to appear as an identifier — a term like `id` matches `orders.id` but not the word "identifier" — and the refusal names the field, never the matched term. | — |
|
|
2725
2726
|
| `--verification <selector...>` | One or more verification artifact ids, unique prefixes, filenames, or in-bounds paths. | none |
|
|
2726
2727
|
| `--audit <selector...>` | One or more audit ids or unique prefixes (minimum four characters) from the active connection. | none |
|
|
2727
2728
|
| `--receipt <path...>` | Explicit workspace-relative receipt path(s). Receipts contribute safe provenance only; they are not execution approval. | none |
|
|
@@ -2729,8 +2730,11 @@ dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a
|
|
|
2729
2730
|
| `--format <json\|markdown>` | Compose receipt format printed to stdout. | `json` |
|
|
2730
2731
|
|
|
2731
2732
|
At least one `--verification`, `--audit`, or `--receipt` reference is required. The resulting pack
|
|
2732
|
-
|
|
2733
|
-
|
|
2733
|
+
carries a canonical SHA-256 digest over its content, and its `id` is derived from that digest, so
|
|
2734
|
+
composing the same claims and references twice produces the same pack. `createdAt` records when the
|
|
2735
|
+
pack was written and is deliberately outside the digest — it is the one field a restamp can change
|
|
2736
|
+
without breaking validation. Whether a referenced source is still resolvable is reported by
|
|
2737
|
+
`evidence validate`, not stored in the pack.
|
|
2734
2738
|
|
|
2735
2739
|
#### `evidence validate`
|
|
2736
2740
|
|
|
@@ -2748,6 +2752,22 @@ verification artifact later rotates, is cleared, disappears, or belongs to a dif
|
|
|
2748
2752
|
active connection, output stays parseable with `integrity: "valid"` and
|
|
2749
2753
|
`references: "source-expired"`, then exits `1`.
|
|
2750
2754
|
|
|
2755
|
+
`validate` first names the pack's artifact format, which carries its own `version`
|
|
2756
|
+
independent of the dbcli package version (ADR-0013). The current format is `version: 2`;
|
|
2757
|
+
dbcli 3.0.0 and earlier wrote `version: 1` in two incompatible layouts. The JSON report
|
|
2758
|
+
leads with `status` and `trust`:
|
|
2759
|
+
|
|
2760
|
+
| `status` | `trust` | Meaning | Exit |
|
|
2761
|
+
|---|---|---|---|
|
|
2762
|
+
| `current-valid` | `current-valid` | Current format, digest verified, references resolvable. | `0` |
|
|
2763
|
+
| `current-references-expired` | `current-valid` | Current format and digest, a referenced source is gone. | `1` |
|
|
2764
|
+
| `recognized-legacy` | `not-current-valid` | Written by an older dbcli; `legacyFormat` and `producedBy` name which, `integrity` reports that format's own digest, references are not evaluated. | `1` |
|
|
2765
|
+
| `unsupported` | `not-current-valid` | Unknown version, or version and structure disagree. | `1` |
|
|
2766
|
+
|
|
2767
|
+
A legacy pack is never silently treated as current-valid and is never migrated: a pack's
|
|
2768
|
+
`id` derives from its digest, so rewriting one would mint a new artifact carrying an old
|
|
2769
|
+
one's provenance. Compose a new pack from current evidence instead.
|
|
2770
|
+
|
|
2751
2771
|
#### `evidence render`
|
|
2752
2772
|
|
|
2753
2773
|
```bash
|
|
@@ -3659,7 +3679,19 @@ GET /orders/_search
|
|
|
3659
3679
|
```
|
|
3660
3680
|
|
|
3661
3681
|
- Enter a request line `<METHOD> /<path>`, then an optional multi-line JSON body; a **blank line** submits the block. Responses render as pretty-printed JSON.
|
|
3662
|
-
-
|
|
3682
|
+
- **The connection's permission tier applies, as it does to `dbcli query`.** The request is classified through the same classifier the query path uses, on the path the server will actually route — the query string is discarded and percent-encoding and dot segments are resolved first, so `?filter_path=...` cannot change the verdict.
|
|
3683
|
+
- **Reads are an allowlist; anything else needs `admin`.** Permitted below `admin`: `_search` and `_count` on an index or unscoped, `_doc` / `_source` reads, `_mapping` / `_settings` / `_alias` reads, `_cat/*` (except `_cat/aliases` and `_cat/tasks`), `_cluster/health`, and `GET` / `HEAD` of a bare index name. Writes to one document are the usual tiers. **Everything else — including any endpoint not listed here — requires `admin`**, so `DELETE /<index>`, `DELETE /_all`, `_delete_by_query`, `_update_by_query`, `_reindex`, `_aliases`, `PUT /<index>/_mapping`, `PUT /<index>/_settings`, `_sql`, `_scripts`, `_security`, `_snapshot`, `_nodes` and `_cluster/state` are all refused below it. The list is a floor, not a claim that everything absent from it is dangerous: a missing entry costs you an unnecessary `admin` requirement rather than a bypass. See [ADR-0014](https://github.com/CarlLee1983/dbcli/blob/main/docs/adr/0014-elasticsearch-reads-are-an-allowlist.md).
|
|
3684
|
+
- A path is refused unless it is **byte-identical** to what the URL parser produces — the same parser the request goes through — and the refusal tells you the canonical spelling to write instead. This is what stops a path that reads one way here and routes another way at the server: `#` truncates a path in transit (`POST /_reindex#/_count` reaches Elasticsearch as `POST /_reindex`), and tab, newline and `\` are rewritten too. A document id containing a space or a non-ASCII character must be written percent-encoded; the error message gives you the exact string.
|
|
3685
|
+
- A quoted string request body is refused; write the body as JSON. So is a `source=` query parameter — Elasticsearch accepts it in place of a body, where none of the body checks can see it. `_source`, `_source_includes` and `_source_excludes` are unaffected.
|
|
3686
|
+
- Blacklisted field names are refused in the query string as well as the body, because the URI-search form names fields directly (`?q=…`, `?sort=…`, `?docvalue_fields=…`). A name matches on any dot component, so `password.keyword` — the multi-field every `text` field gets by default — and `params._source.password` inside a script are both refused.
|
|
3687
|
+
- **Three limits worth knowing.** A mapping-level `alias` pointing at a protected field names it under a different word, which dbcli cannot resolve. A script that builds the name from pieces defeats any text scan. And a wildcard field expression is expanded by Elasticsearch after the request leaves, so `?q=pass*:hunter*` can be used to confirm a value from hit counts — the value itself is still stripped from the response, but the match is not hidden. Blacklist column rules remain a display filter, not an access control.
|
|
3688
|
+
- A refusal names the tier that would work, and the request is never sent.
|
|
3689
|
+
- Every request is written to the audit log whether it executed or was refused, tiered by what the request would do rather than by the command that issued it.
|
|
3690
|
+
- Index-level blacklist rejects protected indices at the front end; a `_search` whose body omits `size` is auto-capped at 1000 hits.
|
|
3691
|
+
|
|
3692
|
+
> **Before v4.0.0 this path applied no permission check at all.** A `query-only`
|
|
3693
|
+
> Elasticsearch connection could delete documents, drop an index or rewrite a mapping
|
|
3694
|
+
> through the shell, and nothing was recorded. See the 4.0.0 entry in `CHANGELOG.md`.
|
|
3663
3695
|
|
|
3664
3696
|
### Doctor and diagnostics
|
|
3665
3697
|
|
|
@@ -1573,7 +1573,7 @@ Opt-in flag trio that persists a **VerificationArtifact JSON** (schema v1) under
|
|
|
1573
1573
|
|
|
1574
1574
|
**Planned vs Result evidence.** `dbcli skill tasks plan safe-backfill-verify --format json` returns a plan containing a `verification` block with `status: "planned"`. That block is the **planned** evidence definition — it describes which check will run. Running `assert --write-verification-artifact` on the actual data produces **result** evidence (`status: "verified"` or `status: "not_verified"`). The two records are distinct; `"planned"` does **not** indicate that verification has run or passed.
|
|
1575
1575
|
|
|
1576
|
-
> **
|
|
1576
|
+
> **Numeric note:** Postgres returns `count(*)` and `sum()` as bigint, which arrives as a string. A numeric expectation is compared numerically, so `value == 0` matches it without a cast. A quoted expectation stays a text comparison: `value == "0"` matches the text, not the number. The `::int` casts in the examples below are harmless and no longer required.
|
|
1577
1577
|
|
|
1578
1578
|
```bash
|
|
1579
1579
|
dbcli assert "SELECT count(*)::int FROM orders WHERE status IS NULL" \
|
|
@@ -2637,7 +2637,8 @@ Inside the shell:
|
|
|
2637
2637
|
> not as the answer. Tier one (the y/N on ordinary writes) is deliberately not wired here — every line
|
|
2638
2638
|
> is typed by a person. Piped input (`dbcli shell < script.sql`) has nobody to answer, so a
|
|
2639
2639
|
> tier-two statement is refused and the remaining lines still run. Redis, MongoDB and
|
|
2640
|
-
> Elasticsearch shells are
|
|
2640
|
+
> Elasticsearch shells do not use the write gate; their writes are bounded by the
|
|
2641
|
+
> connection's permission tier instead.
|
|
2641
2642
|
>
|
|
2642
2643
|
> dbcli **subcommands** typed in the shell (`query "..."`, `delete ...`) run as separate
|
|
2643
2644
|
> processes with no stdin, so they cannot ask anything: a tier-two statement there is
|
|
@@ -2721,7 +2722,7 @@ dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a
|
|
|
2721
2722
|
|
|
2722
2723
|
| Flag | Purpose | Default |
|
|
2723
2724
|
|---|---|---|
|
|
2724
|
-
| `--claims <file>` | Required JSON file with exactly `subject` and `claims`; every claim has an `id` and plain-language `text`. SQL-shaped text, credentials, error content, and blacklisted identifiers are rejected. | — |
|
|
2725
|
+
| `--claims <file>` | Required JSON file with exactly `subject` and `claims`; every claim has an `id` and plain-language `text`. SQL-shaped text, credentials, error content, and blacklisted identifiers are rejected. A blacklisted identifier has to appear as an identifier — a term like `id` matches `orders.id` but not the word "identifier" — and the refusal names the field, never the matched term. | — |
|
|
2725
2726
|
| `--verification <selector...>` | One or more verification artifact ids, unique prefixes, filenames, or in-bounds paths. | none |
|
|
2726
2727
|
| `--audit <selector...>` | One or more audit ids or unique prefixes (minimum four characters) from the active connection. | none |
|
|
2727
2728
|
| `--receipt <path...>` | Explicit workspace-relative receipt path(s). Receipts contribute safe provenance only; they are not execution approval. | none |
|
|
@@ -2729,8 +2730,11 @@ dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a
|
|
|
2729
2730
|
| `--format <json\|markdown>` | Compose receipt format printed to stdout. | `json` |
|
|
2730
2731
|
|
|
2731
2732
|
At least one `--verification`, `--audit`, or `--receipt` reference is required. The resulting pack
|
|
2732
|
-
|
|
2733
|
-
|
|
2733
|
+
carries a canonical SHA-256 digest over its content, and its `id` is derived from that digest, so
|
|
2734
|
+
composing the same claims and references twice produces the same pack. `createdAt` records when the
|
|
2735
|
+
pack was written and is deliberately outside the digest — it is the one field a restamp can change
|
|
2736
|
+
without breaking validation. Whether a referenced source is still resolvable is reported by
|
|
2737
|
+
`evidence validate`, not stored in the pack.
|
|
2734
2738
|
|
|
2735
2739
|
#### `evidence validate`
|
|
2736
2740
|
|
|
@@ -2748,6 +2752,22 @@ verification artifact later rotates, is cleared, disappears, or belongs to a dif
|
|
|
2748
2752
|
active connection, output stays parseable with `integrity: "valid"` and
|
|
2749
2753
|
`references: "source-expired"`, then exits `1`.
|
|
2750
2754
|
|
|
2755
|
+
`validate` first names the pack's artifact format, which carries its own `version`
|
|
2756
|
+
independent of the dbcli package version (ADR-0013). The current format is `version: 2`;
|
|
2757
|
+
dbcli 3.0.0 and earlier wrote `version: 1` in two incompatible layouts. The JSON report
|
|
2758
|
+
leads with `status` and `trust`:
|
|
2759
|
+
|
|
2760
|
+
| `status` | `trust` | Meaning | Exit |
|
|
2761
|
+
|---|---|---|---|
|
|
2762
|
+
| `current-valid` | `current-valid` | Current format, digest verified, references resolvable. | `0` |
|
|
2763
|
+
| `current-references-expired` | `current-valid` | Current format and digest, a referenced source is gone. | `1` |
|
|
2764
|
+
| `recognized-legacy` | `not-current-valid` | Written by an older dbcli; `legacyFormat` and `producedBy` name which, `integrity` reports that format's own digest, references are not evaluated. | `1` |
|
|
2765
|
+
| `unsupported` | `not-current-valid` | Unknown version, or version and structure disagree. | `1` |
|
|
2766
|
+
|
|
2767
|
+
A legacy pack is never silently treated as current-valid and is never migrated: a pack's
|
|
2768
|
+
`id` derives from its digest, so rewriting one would mint a new artifact carrying an old
|
|
2769
|
+
one's provenance. Compose a new pack from current evidence instead.
|
|
2770
|
+
|
|
2751
2771
|
#### `evidence render`
|
|
2752
2772
|
|
|
2753
2773
|
```bash
|
|
@@ -3659,7 +3679,19 @@ GET /orders/_search
|
|
|
3659
3679
|
```
|
|
3660
3680
|
|
|
3661
3681
|
- Enter a request line `<METHOD> /<path>`, then an optional multi-line JSON body; a **blank line** submits the block. Responses render as pretty-printed JSON.
|
|
3662
|
-
-
|
|
3682
|
+
- **The connection's permission tier applies, as it does to `dbcli query`.** The request is classified through the same classifier the query path uses, on the path the server will actually route — the query string is discarded and percent-encoding and dot segments are resolved first, so `?filter_path=...` cannot change the verdict.
|
|
3683
|
+
- **Reads are an allowlist; anything else needs `admin`.** Permitted below `admin`: `_search` and `_count` on an index or unscoped, `_doc` / `_source` reads, `_mapping` / `_settings` / `_alias` reads, `_cat/*` (except `_cat/aliases` and `_cat/tasks`), `_cluster/health`, and `GET` / `HEAD` of a bare index name. Writes to one document are the usual tiers. **Everything else — including any endpoint not listed here — requires `admin`**, so `DELETE /<index>`, `DELETE /_all`, `_delete_by_query`, `_update_by_query`, `_reindex`, `_aliases`, `PUT /<index>/_mapping`, `PUT /<index>/_settings`, `_sql`, `_scripts`, `_security`, `_snapshot`, `_nodes` and `_cluster/state` are all refused below it. The list is a floor, not a claim that everything absent from it is dangerous: a missing entry costs you an unnecessary `admin` requirement rather than a bypass. See [ADR-0014](https://github.com/CarlLee1983/dbcli/blob/main/docs/adr/0014-elasticsearch-reads-are-an-allowlist.md).
|
|
3684
|
+
- A path is refused unless it is **byte-identical** to what the URL parser produces — the same parser the request goes through — and the refusal tells you the canonical spelling to write instead. This is what stops a path that reads one way here and routes another way at the server: `#` truncates a path in transit (`POST /_reindex#/_count` reaches Elasticsearch as `POST /_reindex`), and tab, newline and `\` are rewritten too. A document id containing a space or a non-ASCII character must be written percent-encoded; the error message gives you the exact string.
|
|
3685
|
+
- A quoted string request body is refused; write the body as JSON. So is a `source=` query parameter — Elasticsearch accepts it in place of a body, where none of the body checks can see it. `_source`, `_source_includes` and `_source_excludes` are unaffected.
|
|
3686
|
+
- Blacklisted field names are refused in the query string as well as the body, because the URI-search form names fields directly (`?q=…`, `?sort=…`, `?docvalue_fields=…`). A name matches on any dot component, so `password.keyword` — the multi-field every `text` field gets by default — and `params._source.password` inside a script are both refused.
|
|
3687
|
+
- **Three limits worth knowing.** A mapping-level `alias` pointing at a protected field names it under a different word, which dbcli cannot resolve. A script that builds the name from pieces defeats any text scan. And a wildcard field expression is expanded by Elasticsearch after the request leaves, so `?q=pass*:hunter*` can be used to confirm a value from hit counts — the value itself is still stripped from the response, but the match is not hidden. Blacklist column rules remain a display filter, not an access control.
|
|
3688
|
+
- A refusal names the tier that would work, and the request is never sent.
|
|
3689
|
+
- Every request is written to the audit log whether it executed or was refused, tiered by what the request would do rather than by the command that issued it.
|
|
3690
|
+
- Index-level blacklist rejects protected indices at the front end; a `_search` whose body omits `size` is auto-capped at 1000 hits.
|
|
3691
|
+
|
|
3692
|
+
> **Before v4.0.0 this path applied no permission check at all.** A `query-only`
|
|
3693
|
+
> Elasticsearch connection could delete documents, drop an index or rewrite a mapping
|
|
3694
|
+
> through the shell, and nothing was recorded. See the 4.0.0 entry in `CHANGELOG.md`.
|
|
3663
3695
|
|
|
3664
3696
|
### Doctor and diagnostics
|
|
3665
3697
|
|