@carllee1983/dbcli 1.52.0 → 1.53.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/.cursor/rules/dbcli.mdc +39 -28
- package/.cursor/skills/dbcli/reference.md +233 -21
- package/.github/skills/dbcli/SKILL.md +36 -25
- package/.github/skills/dbcli/reference.md +233 -21
- package/CHANGELOG.md +38 -0
- package/assets/SKILL.md +36 -25
- package/assets/SKILL.zh-TW.md +48 -30
- package/assets/reference.md +233 -21
- package/assets/ui-template.html +2 -2
- package/dist/cli-runtime.mjs +101686 -0
- package/dist/cli.mjs +140 -109572
- package/dist/core.d.ts +4 -0
- package/dist/core.mjs +157 -254
- package/dist/ui-style.css +2 -2
- package/package.json +4 -4
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +36 -25
- package/plugins/dbcli-agent/skills/dbcli/reference.md +233 -21
- package/skills/dbcli/SKILL.md +36 -25
- package/skills/dbcli/reference.md +233 -21
package/assets/reference.md
CHANGED
|
@@ -4,6 +4,69 @@ Companion to [SKILL.md](SKILL.md). Exhaustive flags, copy-paste examples, `shell
|
|
|
4
4
|
|
|
5
5
|
For cross-engine support status, see `docs/feature-matrix.md` in the repository.
|
|
6
6
|
|
|
7
|
+
## Index
|
|
8
|
+
|
|
9
|
+
Jump straight to what you need — this file is long, and reading it end to end is
|
|
10
|
+
never the right move.
|
|
11
|
+
|
|
12
|
+
| Section | What is in it |
|
|
13
|
+
|---|---|
|
|
14
|
+
| [Global options and placement](#global-options-and-placement) | Flags that must precede the command (`--use`, `--config`), and [redirecting output](#redirecting-output). |
|
|
15
|
+
| [Commands](#commands) | Every command, one `###` each — see the command list below. |
|
|
16
|
+
| [Recovery Cookbook](#recovery-cookbook-agent-walkthroughs) | Failure-to-fix walkthroughs S1–S6, the [scenario index](#scenario-index), [risk gate cheat sheet](#risk-gate-cheat-sheet), and [common pitfalls](#common-pitfalls). |
|
|
17
|
+
| [Interactive HTML dashboard](#interactive-html-dashboard) | `--ui` / `--format html`: [entry points](#entry-points), [data injection contract](#data-injection-contract), [`meta` shape](#meta-shape). |
|
|
18
|
+
| [MongoDB Support](#mongodb-support) | Connection shapes, JSON query bodies, write-stage guard. |
|
|
19
|
+
| [Redis Support](#redis-support) | Command permission tiers, size guards, key masking. |
|
|
20
|
+
| [Elasticsearch Support](#elasticsearch-support) | DSL and Lucene queries, scroll export, mapping flattening. |
|
|
21
|
+
|
|
22
|
+
**Commands** —
|
|
23
|
+
[init](#init) ·
|
|
24
|
+
[use](#use) ·
|
|
25
|
+
[list](#list) ·
|
|
26
|
+
[schema](#schema) ·
|
|
27
|
+
[query](#query) ·
|
|
28
|
+
[explain](#explain) ·
|
|
29
|
+
[lint](#lint) ·
|
|
30
|
+
[plan](#plan) ·
|
|
31
|
+
[q](#q) ·
|
|
32
|
+
[queries](#queries) ·
|
|
33
|
+
[insert](#insert) ·
|
|
34
|
+
[update](#update) ·
|
|
35
|
+
[delete](#delete) ·
|
|
36
|
+
[export](#export) ·
|
|
37
|
+
[blacklist](#blacklist) ·
|
|
38
|
+
[check](#check) ·
|
|
39
|
+
[diff](#diff) ·
|
|
40
|
+
[design](#design) ·
|
|
41
|
+
[impact assess](#impact-assess) ·
|
|
42
|
+
[snapshot](#snapshot) ·
|
|
43
|
+
[assert](#assert) ·
|
|
44
|
+
[proxy](#proxy) ·
|
|
45
|
+
[status](#status) ·
|
|
46
|
+
[inspect](#inspect) ·
|
|
47
|
+
[report](#report) ·
|
|
48
|
+
[guide](#guide) ·
|
|
49
|
+
[recovery](#recovery) ·
|
|
50
|
+
[recover](#recover) ·
|
|
51
|
+
[audit](#audit) ·
|
|
52
|
+
[verify](#verify) ·
|
|
53
|
+
[verification](#verification) ·
|
|
54
|
+
[backfill](#backfill) ·
|
|
55
|
+
[evidence](#evidence) ·
|
|
56
|
+
[doctor](#doctor) ·
|
|
57
|
+
[completion](#completion) ·
|
|
58
|
+
[upgrade](#upgrade) ·
|
|
59
|
+
[shell](#dbcli-shell) ·
|
|
60
|
+
[migrate](#migrate) ·
|
|
61
|
+
[semantic](#semantic) ·
|
|
62
|
+
[contract](#contract) ·
|
|
63
|
+
[skill](#skill) ·
|
|
64
|
+
[skill context](#skill-context) ·
|
|
65
|
+
[skill tasks](#skill-tasks-agent-task-packs)
|
|
66
|
+
|
|
67
|
+
Also worth knowing before you connect:
|
|
68
|
+
[Agent configuration trust boundary](#agent-configuration-trust-boundary).
|
|
69
|
+
|
|
7
70
|
## Global options and placement
|
|
8
71
|
|
|
9
72
|
These options are available on the root `dbcli` command. Root-level options must
|
|
@@ -561,13 +624,15 @@ dbcli q @analytics/revenue --param days=30 --format html > report.html
|
|
|
561
624
|
- `--ui` — open the rendered HTML dashboard in the system browser (implies `--format html`; writes to a temp file then invokes `open` / `xdg-open` / `start`)
|
|
562
625
|
- `--param <key=value>` — pass a parameter (repeatable)
|
|
563
626
|
- `--param-file <path>` — JSON object whose keys are param names
|
|
564
|
-
- `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT
|
|
627
|
+
- `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT 1001` wrap (the effective cap is 1000; one extra row is fetched to detect truncation)
|
|
565
628
|
- `--dry-run` — print the bound SQL + values without executing
|
|
566
|
-
- `--use <name>` — pick a v2 named connection
|
|
567
629
|
- `--slow-ms <number>` — passive slow-query hint threshold (default `1000`; `0` disables). Same contract as `query` — see "Passive slow-query hint" there
|
|
568
630
|
- `--recovery` — emit a `RecoveryEnvelope` on failure (see `recover`)
|
|
569
631
|
- `--verify` — run the snippet's verification assertions after execution (only if the snippet defines them)
|
|
570
632
|
|
|
633
|
+
`q` has no command-level `--use`. To run a snippet against a v2 named connection,
|
|
634
|
+
use the global form: `dbcli --use <name> q @<snippet>`.
|
|
635
|
+
|
|
571
636
|
**Permission:** query-only+
|
|
572
637
|
|
|
573
638
|
#### Snippet file format
|
|
@@ -691,8 +756,9 @@ Size guard: `LRANGE` / `ZRANGE` stop overridden when `< 0` or `> 1000`; `SCAN` /
|
|
|
691
756
|
##### MongoDB snippets
|
|
692
757
|
|
|
693
758
|
File extension: `.mongodb.sql`. Frontmatter must declare `engine: mongodb` and
|
|
694
|
-
`operation: find` or `operation: aggregate`. `target: <collection>`
|
|
695
|
-
collection
|
|
759
|
+
`operation: find` or `operation: aggregate`. `target: <collection>` declares the
|
|
760
|
+
collection the snippet runs against; the CLI has no flag to override it, so a different
|
|
761
|
+
collection means a different snippet. The body is JSON: an object
|
|
696
762
|
for `find` and an array for `aggregate`. Each `{{param}}` placeholder is JSON-encoded
|
|
697
763
|
at substitution time — strings are quoted and escaped, so an attacker-supplied string
|
|
698
764
|
cannot escape into operator position.
|
|
@@ -1126,6 +1192,7 @@ dbcli design validate [--file <path>] [--format <format>]
|
|
|
1126
1192
|
dbcli design render [--file <path>] [--format <format>]
|
|
1127
1193
|
dbcli design diff (--against-cache | --against-orm <paths>) [options]
|
|
1128
1194
|
dbcli design propose (--against-cache | --against-orm <paths>) [options]
|
|
1195
|
+
dbcli impact assess --design <path> (--against-cache | --against-orm <paths>) --output <path> [options]
|
|
1129
1196
|
```
|
|
1130
1197
|
|
|
1131
1198
|
```bash
|
|
@@ -1137,6 +1204,7 @@ dbcli design render --format mermaid
|
|
|
1137
1204
|
dbcli design diff --against-cache --format markdown
|
|
1138
1205
|
dbcli design diff --against-orm ./prisma/schema.prisma --format markdown
|
|
1139
1206
|
dbcli design propose --against-orm ./prisma/schema.prisma --format markdown
|
|
1207
|
+
dbcli impact assess --design ./dbcli.design.json --against-cache --output ./impact.json --fail-on warn
|
|
1140
1208
|
```
|
|
1141
1209
|
|
|
1142
1210
|
| Option | Applies to | Default | Meaning |
|
|
@@ -1153,8 +1221,14 @@ dbcli design propose --against-orm ./prisma/schema.prisma --format markdown
|
|
|
1153
1221
|
`diff` and `propose` require **exactly one** comparison target; passing both or
|
|
1154
1222
|
neither is an error.
|
|
1155
1223
|
|
|
1224
|
+
### impact assess
|
|
1225
|
+
|
|
1226
|
+
Writes an offline JSON or Markdown report for the known declared impact of a design change. It requires `--design`, exactly one baseline (`--against-cache` or `--against-orm`), and an explicit workspace-contained `--output`. `--format` is `json` (default) or `markdown`; `--fail-on` is `error`, `warn`, or `never` (default). The threshold changes only the successful command exit code after the report is written. The report is limited to declared/partial coverage and never claims complete coverage. It may join the optional reviewed `dbcli.data-access.json` manifest only after validating its canonical semantic references and existing workspace-relative source paths; it never reads those sources. An optional explicit `--events <path>` is bounded and streamed through a redaction-first projection that retains recent safe table metadata only. It never starts a proxy, reads a rotated log, renders SQL/literals/errors/sessions/paths, or makes the command fail solely because workload evidence is absent or advisory. It reads no database, executes no SQL, and never reads saved-query bodies.
|
|
1227
|
+
|
|
1156
1228
|
#### Artifact shape
|
|
1157
1229
|
|
|
1230
|
+
This example validates clean (0 errors, 0 warnings):
|
|
1231
|
+
|
|
1158
1232
|
```json
|
|
1159
1233
|
{
|
|
1160
1234
|
"version": 1,
|
|
@@ -1166,14 +1240,24 @@ neither is an error.
|
|
|
1166
1240
|
"description": "Completed purchases.",
|
|
1167
1241
|
"fields": [
|
|
1168
1242
|
{ "name": "id", "type": "bigint", "nullable": false, "primaryKey": true, "unique": true },
|
|
1169
|
-
{ "name": "customer_id", "type": "bigint", "nullable": false
|
|
1243
|
+
{ "name": "customer_id", "type": "bigint", "nullable": false },
|
|
1244
|
+
{ "name": "created_at", "type": "timestamptz", "nullable": false }
|
|
1170
1245
|
],
|
|
1171
|
-
"indexes": [
|
|
1246
|
+
"indexes": [
|
|
1247
|
+
{ "name": "orders_customer_idx", "columns": ["customer_id", "created_at"], "unique": false }
|
|
1248
|
+
]
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
"name": "customers",
|
|
1252
|
+
"table": "customers",
|
|
1253
|
+
"fields": [
|
|
1254
|
+
{ "name": "id", "type": "bigint", "nullable": false, "primaryKey": true, "unique": true }
|
|
1255
|
+
]
|
|
1172
1256
|
}
|
|
1173
1257
|
],
|
|
1174
1258
|
"relationships": [
|
|
1175
1259
|
{
|
|
1176
|
-
"name": "
|
|
1260
|
+
"name": "orders-customer",
|
|
1177
1261
|
"from": { "model": "orders", "field": "customer_id" },
|
|
1178
1262
|
"to": { "model": "customers", "field": "id" },
|
|
1179
1263
|
"cardinality": "many-to-one"
|
|
@@ -1188,11 +1272,38 @@ It holds no SQL, credentials, rows, or provider configuration. `design init`
|
|
|
1188
1272
|
emits this envelope with empty `models`, `relationships`, `accessPatterns`, and
|
|
1189
1273
|
`decisions`.
|
|
1190
1274
|
|
|
1275
|
+
#### Naming and limits
|
|
1276
|
+
|
|
1277
|
+
Two different naming rules apply, and mixing them up is the most common way an
|
|
1278
|
+
artifact fails before any review rule runs.
|
|
1279
|
+
|
|
1280
|
+
| Applies to | Rule |
|
|
1281
|
+
|---|---|
|
|
1282
|
+
| `models[].name`, `relationships[].name`, `relationships[].from/to.model`, `accessPatterns[].model`, `decisions[].name` | lowercase kebab-case, `^[a-z][a-z0-9-]*$` — underscores are rejected |
|
|
1283
|
+
| `models[].table`, `fields[].name`, `indexes[].name`, `indexes[].columns[]`, `filters[]`, `sort[]` | SQL identifier, `^[A-Za-z_][A-Za-z0-9_]*$` |
|
|
1284
|
+
|
|
1285
|
+
Relationship endpoints reference a **model name**, not a table name. Every object
|
|
1286
|
+
is strict: an unknown key is an error, not ignored. `description` and `rationale`
|
|
1287
|
+
are 1–1000 characters and must not contain SQL keywords or a connection string —
|
|
1288
|
+
"rows we delete after 30 days" is rejected for the word `delete`. `fields[].type`
|
|
1289
|
+
is at most 100 characters with no `;` or newline. `primaryKey`, `unique`,
|
|
1290
|
+
`indexes`, `filters`, `sort`, `relationships`, `accessPatterns`, and `decisions`
|
|
1291
|
+
may all be omitted.
|
|
1292
|
+
|
|
1293
|
+
Limits: file 256 KiB, 100 models, 100 fields per model, 200 relationships,
|
|
1294
|
+
200 access patterns, 100 decisions, 1–16 columns per index, ≤16 entries in
|
|
1295
|
+
`filters` and `sort`.
|
|
1296
|
+
|
|
1191
1297
|
#### Review findings
|
|
1192
1298
|
|
|
1193
1299
|
`validate` is fail-closed: any `error` finding exits `1`, and `render`, `diff`,
|
|
1194
1300
|
and `propose` refuse to do their work while errors remain.
|
|
1195
1301
|
|
|
1302
|
+
Structural problems — malformed JSON, an unknown key, a naming or type violation,
|
|
1303
|
+
a missing file, or a file over 256 KiB — are rejected before review runs and are
|
|
1304
|
+
reported as a single `INVALID_ARTIFACT` finding (`error`) whose `path` points at
|
|
1305
|
+
the offending JSON location. None of the codes below appear in that case.
|
|
1306
|
+
|
|
1196
1307
|
| Severity | Codes |
|
|
1197
1308
|
|---|---|
|
|
1198
1309
|
| `error` | `NO_MODELS`, `DUPLICATE_MODEL`, `DUPLICATE_TABLE`, `DUPLICATE_FIELD`, `PRIMARY_KEY_COUNT`, `NULLABLE_PRIMARY_KEY`, `UNKNOWN_INDEX_FIELD`, `DUPLICATE_RELATIONSHIP`, `REVERSE_RELATIONSHIP`, `UNKNOWN_RELATIONSHIP_MODEL`, `UNKNOWN_RELATIONSHIP_FIELD`, `RELATIONSHIP_TYPE_MISMATCH`, `MANY_TO_MANY_REQUIRES_BRIDGE`, `ONE_TO_ONE_REQUIRES_UNIQUE_FK`, `UNKNOWN_ACCESS_MODEL`, `UNKNOWN_ACCESS_FIELD` |
|
|
@@ -1297,7 +1408,7 @@ Opt-in flag trio that persists a **VerificationArtifact JSON** (schema v1) under
|
|
|
1297
1408
|
| Flag | Required | Description |
|
|
1298
1409
|
| :--- | :--- | :--- |
|
|
1299
1410
|
| `--write-verification-artifact` | opt-in | Trigger artifact write. No-op when no verdict has been produced. |
|
|
1300
|
-
| `--verification-subject <kind:name>` | yes (when flag is set) | Subject identifier. Format: `<kind>:<name>`. Allowed kinds: `recovery`, `task-pack`, `assertion`, `migration`, `backfill`, `manual`. |
|
|
1411
|
+
| `--verification-subject <kind:name>` | yes (when flag is set) | Subject identifier. Format: `<kind>:<name>`. Allowed kinds: `recovery`, `task-pack`, `assertion`, `migration`, `backfill`, `table`, `manual`. |
|
|
1301
1412
|
| `--verification-summary <text>` | no | Free-text summary line stored in the artifact. Default when pass: "Assertion verified the expected state." Default when fail: "Assertion did not verify the expected state." |
|
|
1302
1413
|
|
|
1303
1414
|
**Output contract:**
|
|
@@ -1559,7 +1670,7 @@ Examples:
|
|
|
1559
1670
|
|
|
1560
1671
|
Boundaries:
|
|
1561
1672
|
- Recovery only **suggests** commands; agents (or humans) execute them. No automatic remediation in v1.15.0.
|
|
1562
|
-
-
|
|
1673
|
+
- `--recovery` is honored on `query`, `q`, `insert`, `update`, `delete`, `export`, `schema`, `inspect`, `lint`, and `diff`. Other commands (`report`, `guide`, `doctor`, `migrate`, `init`, `use`, `status`, `list`, `check`, `plan`, `shell`, `blacklist`, `completion`, `upgrade`, `skill`) keep their existing error behavior.
|
|
1563
1674
|
- `dbcli inspect --require-schema-cache` throws `SCHEMA_CACHE_MISSING` when the active SQL connection has no usable schema cache. Combine with `--recovery` for the structured envelope.
|
|
1564
1675
|
- `BLACKLIST_COLUMN_WRITE` and `PERMISSION_DENIED` envelopes prepend a `risk: 'dry-run'` step (e.g. `dbcli insert <table> --dry-run`) when the failing operation was an INSERT / UPDATE / DELETE.
|
|
1565
1676
|
- Recovery steps reuse the v1.14.0 `GuideStep` shape, including the full `risk` enum (`readonly` / `dry-run` / `write` / `unknown`).
|
|
@@ -1593,7 +1704,7 @@ Boundaries:
|
|
|
1593
1704
|
|---|---|---|
|
|
1594
1705
|
| `readonly` | local read-only | `dbcli inspect`, `dbcli doctor`, `dbcli blacklist list`, `dbcli schema <table>` |
|
|
1595
1706
|
| `dry-run` | write subcommand invoked with `--dry-run` | `dbcli update orders --where id=1 --dry-run`, `dbcli q @x --dry-run` |
|
|
1596
|
-
| `local-write` | writes local config / cache / blacklist | `dbcli blacklist remove <table>`, `dbcli use <name>`, `dbcli schema --refresh` |
|
|
1707
|
+
| `local-write` | writes local config / cache / blacklist | `dbcli blacklist table remove <table>`, `dbcli use <name>`, `dbcli schema --refresh` |
|
|
1597
1708
|
| `db-write` | mutates the connected database | `dbcli update orders --where id=1 --set …` (no `--dry-run`), `dbcli q @x` (no `--dry-run`) |
|
|
1598
1709
|
| `interactive` | requires TTY | `dbcli init`, `dbcli init --force` |
|
|
1599
1710
|
|
|
@@ -1746,7 +1857,7 @@ dbcli recover --next --after-step 1 --result '{"status":"ok"}' --format markdown
|
|
|
1746
1857
|
|
|
1747
1858
|
(v1.20.0+) Inspect, query, and manage the per-connection audit log written to `.dbcli/audit/<connection>.jsonl`.
|
|
1748
1859
|
|
|
1749
|
-
Audit entries are metadata-only by design — never raw SQL bodies, `--param` values, or result cell contents (D3 lock). Redaction is sourced from `
|
|
1860
|
+
Audit entries are metadata-only by design — never raw SQL bodies, `--param` values, or result cell contents (D3 lock). Redaction is sourced from `src/utils/redaction.ts` (same source as `inspect` / `guide` / `recover` agent contracts).
|
|
1750
1861
|
|
|
1751
1862
|
#### Subcommands
|
|
1752
1863
|
|
|
@@ -1783,7 +1894,9 @@ Examples:
|
|
|
1783
1894
|
| `<id-prefix>` | Positional. UUID or prefix ≥ 4 characters; ambiguous prefix exits 1 with disambiguation hint; prefix < 4 chars exits 1. | — |
|
|
1784
1895
|
| `--recovery-ref <id>` | Find the audit entry whose `recovery_ref` field matches this id (exact, not prefix). Mutually exclusive with positional `<id-prefix>` (D-38). | — |
|
|
1785
1896
|
| `--all` | Search across all connections. Output is an envelope `{ connection, entry }` (single-hit also envelope, for shape stability — D-36). | off |
|
|
1786
|
-
| `--
|
|
1897
|
+
| `--brief` | Trim `metadata` and `redacted_query` from the entry. | off |
|
|
1898
|
+
| `--for-agent` | Shortcut for `--format json --brief`. | off |
|
|
1899
|
+
| `--no-brief` | Disable brief mode when a higher-level default enables it (e.g. `--for-agent`). | off |
|
|
1787
1900
|
| `--format <fmt>` | `table` \| `json`. | `table` |
|
|
1788
1901
|
|
|
1789
1902
|
Examples:
|
|
@@ -1817,7 +1930,7 @@ Output reports: writer enabled/disabled, last write result, file-lock state, rot
|
|
|
1817
1930
|
#### Boundaries
|
|
1818
1931
|
|
|
1819
1932
|
- Entries are append-only JSONL; rotation triggers at `~10 MB` or `~1000` entries (whichever first). Previous segment is preserved as `.jsonl.1`.
|
|
1820
|
-
- Bi-directional `recovery_ref` / `audit_ref` linkage is wired on every command that accepts `--recovery`: `query`, `inspect`, `insert`, `update`, `delete`, `export`, `q`, and `schema`. Use `audit
|
|
1933
|
+
- Bi-directional `recovery_ref` / `audit_ref` linkage is wired on every command that accepts `--recovery`: `query`, `inspect`, `insert`, `update`, `delete`, `export`, `q`, and `schema`. Use `audit show --recovery-ref <id>` to find the audit entry an envelope was emitted alongside.
|
|
1821
1934
|
- Audit writer failures are non-fatal (D6): main command result and exit code are preserved; a stderr warning is emitted. `audit health` surfaces the failure reason.
|
|
1822
1935
|
- Reader truncation tolerance: a crash-truncated last line is skipped with a stderr warn `[dbcli audit] skipping truncated last line in <file>`; a mid-file non-JSON line is treated as corruption, exits 1, and points at `dbcli audit clear`.
|
|
1823
1936
|
|
|
@@ -1835,7 +1948,7 @@ Output reports: writer enabled/disabled, last write result, file-lock state, rot
|
|
|
1835
1948
|
### verify
|
|
1836
1949
|
|
|
1837
1950
|
Run a verification scenario. `verify` **runs** verification scenarios (safe-backfill,
|
|
1838
|
-
migration, rollback) and never executes writes/DDL. `verification` **inspects and manages**
|
|
1951
|
+
migration, rollback, constraint) and never executes writes/DDL. `verification` **inspects and manages**
|
|
1839
1952
|
the local result artifacts those scenarios produce under `.dbcli/verification/`.
|
|
1840
1953
|
|
|
1841
1954
|
```bash
|
|
@@ -1847,7 +1960,8 @@ dbcli verify safe-backfill \
|
|
|
1847
1960
|
--expect "value == 0"
|
|
1848
1961
|
|
|
1849
1962
|
# After-write: re-run guards, run the read-back assertion, write a v1 artifact.
|
|
1850
|
-
|
|
1963
|
+
# An optional receipt is provenance only, never approval to execute a write.
|
|
1964
|
+
dbcli verify safe-backfill ... --after-write --evidence-receipt .dbcli/evidence/verify-receipt.json
|
|
1851
1965
|
|
|
1852
1966
|
# JSON for agents.
|
|
1853
1967
|
dbcli verify safe-backfill ... --format json
|
|
@@ -1855,7 +1969,11 @@ dbcli verify safe-backfill ... --format json
|
|
|
1855
1969
|
|
|
1856
1970
|
Options: `--table` (req), `--query` (req, analyzed not executed), `--verify-query`
|
|
1857
1971
|
(req, **plain SELECT only**), `--expect` (req), `--after-write`, `--format <table|json>`,
|
|
1858
|
-
`--subject-name <name>`, `--summary <text
|
|
1972
|
+
`--subject-name <name>`, `--summary <text>`, `--evidence-receipt <workspace-relative-path>`
|
|
1973
|
+
(after-write only). A receipt is safe provenance for the already-executed verification,
|
|
1974
|
+
not approval to execute a write. Its `succeeded`/`failed` outcome remains distinct from
|
|
1975
|
+
the artifact status (`verified`, `not_verified`, `indeterminate`, or `blocked`); task-pack
|
|
1976
|
+
`planned` evidence stays plan-only.
|
|
1859
1977
|
|
|
1860
1978
|
Guard constraints (fail closed): `--verify-query` must be a **plain `SELECT`** —
|
|
1861
1979
|
`EXPLAIN`/`EXPLAIN ANALYZE`, `SHOW`, `DESCRIBE`, and data-modifying CTEs are rejected
|
|
@@ -2078,7 +2196,7 @@ dbcli verification list --include-invalid --format json
|
|
|
2078
2196
|
| `--format <json\|table>` | Output format. | `json` |
|
|
2079
2197
|
| `--limit <n>` | Maximum number of entries to return. | `20` |
|
|
2080
2198
|
| `--status <status>` | Filter by status. One of: `verified`, `not_verified`, `indeterminate`, `blocked`. | all |
|
|
2081
|
-
| `--subject <kind[:name]>` | Filter by subject kind or exact `kind:name`. Allowed kinds: `recovery`, `task-pack`, `assertion`, `migration`, `backfill`, `manual`. | all |
|
|
2199
|
+
| `--subject <kind[:name]>` | Filter by subject kind or exact `kind:name`. Allowed kinds: `recovery`, `task-pack`, `assertion`, `migration`, `backfill`, `table`, `manual`. | all |
|
|
2082
2200
|
| `--include-invalid` | Surface malformed artifact files (normally skipped silently). Invalid files are returned as a separate top-level `invalid` array in JSON output, each entry shaped `{ "path": "...", "filename": "...", "error": "..." }`. When off, `invalid` is `[]`. | off |
|
|
2083
2201
|
|
|
2084
2202
|
**Missing directory:** if `.dbcli/verification/` does not exist, exits `0` with an
|
|
@@ -2192,6 +2310,7 @@ includes `storageDir`, `dryRun`, `cutoff`, `criteria`, `protected`, `candidates`
|
|
|
2192
2310
|
| `assertion` | General-purpose inline assertions. |
|
|
2193
2311
|
| `migration` | Schema migration pre/post checks. |
|
|
2194
2312
|
| `backfill` | Data backfill verification assertions. |
|
|
2313
|
+
| `table` | `verify constraint` artifacts — the subject name is the table checked. |
|
|
2195
2314
|
| `manual` | Manually triggered or ad-hoc verification runs. |
|
|
2196
2315
|
|
|
2197
2316
|
**Storage root:** `<cwd>/.dbcli/verification/` (cwd-relative; independent of `--config`).
|
|
@@ -2359,6 +2478,66 @@ dbcli migrate drop-enum status --execute --force
|
|
|
2359
2478
|
|
|
2360
2479
|
**AI agent note:** Always use dry-run first (no `--execute`) to preview generated SQL. Only add `--execute` after confirming the SQL is correct. For DROP operations, both `--execute` and `--force` are required.
|
|
2361
2480
|
|
|
2481
|
+
### evidence
|
|
2482
|
+
|
|
2483
|
+
Compose a canonical, offline **Evidence Pack** from existing verification artifacts
|
|
2484
|
+
and audit entries. This command never opens a database connection. A pack retains only
|
|
2485
|
+
safe pointers and outcome fields: it excludes SQL, targets, audit metadata, verification
|
|
2486
|
+
summaries, result data, and credentials. Claims are external statements, never dbcli
|
|
2487
|
+
verification verdicts.
|
|
2488
|
+
|
|
2489
|
+
#### `evidence compose`
|
|
2490
|
+
|
|
2491
|
+
```bash
|
|
2492
|
+
dbcli evidence compose --claims ./claims.json --verification ver_abcd --audit 1a2b \
|
|
2493
|
+
--receipt .dbcli/evidence/verify-receipt.json \
|
|
2494
|
+
--output .dbcli/evidence/review.json --format json
|
|
2495
|
+
```
|
|
2496
|
+
|
|
2497
|
+
| Flag | Purpose | Default |
|
|
2498
|
+
|---|---|---|
|
|
2499
|
+
| `--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. | — |
|
|
2500
|
+
| `--verification <selector...>` | One or more verification artifact ids, unique prefixes, filenames, or in-bounds paths. | none |
|
|
2501
|
+
| `--audit <selector...>` | One or more audit ids or unique prefixes (minimum four characters) from the active connection. | none |
|
|
2502
|
+
| `--receipt <path...>` | Explicit workspace-relative receipt path(s). Receipts contribute safe provenance only; they are not execution approval. | none |
|
|
2503
|
+
| `--output <path>` | Required new JSON path under the current workspace. Existing files and paths outside the workspace are refused. | — |
|
|
2504
|
+
| `--format <json\|markdown>` | Compose receipt format printed to stdout. | `json` |
|
|
2505
|
+
|
|
2506
|
+
At least one `--verification`, `--audit`, or `--receipt` reference is required. The resulting pack
|
|
2507
|
+
contains a canonical SHA-256 integrity digest and `coverage.completeForDeclaredEvidence:
|
|
2508
|
+
true` for its explicitly selected references.
|
|
2509
|
+
|
|
2510
|
+
#### `evidence validate`
|
|
2511
|
+
|
|
2512
|
+
```bash
|
|
2513
|
+
dbcli evidence validate --file .dbcli/evidence/review.json --format json
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
| Flag | Purpose | Default |
|
|
2517
|
+
|---|---|---|
|
|
2518
|
+
| `--file <path>` | Required evidence-pack JSON file. | — |
|
|
2519
|
+
| `--format <json\|markdown>` | Validation report format. | `json` |
|
|
2520
|
+
|
|
2521
|
+
Exit `0` when digest and all selected sources are valid. If a referenced audit or
|
|
2522
|
+
verification artifact later rotates, is cleared, disappears, or belongs to a different
|
|
2523
|
+
active connection, output stays parseable with `integrity: "valid"` and
|
|
2524
|
+
`references: "source-expired"`, then exits `1`.
|
|
2525
|
+
|
|
2526
|
+
#### `evidence render`
|
|
2527
|
+
|
|
2528
|
+
```bash
|
|
2529
|
+
dbcli evidence render --file .dbcli/evidence/review.json --format markdown
|
|
2530
|
+
```
|
|
2531
|
+
|
|
2532
|
+
| Flag | Purpose | Default |
|
|
2533
|
+
|---|---|---|
|
|
2534
|
+
| `--file <path>` | Required evidence-pack JSON file. | — |
|
|
2535
|
+
| `--format <json\|markdown>` | Render format. | `markdown` |
|
|
2536
|
+
|
|
2537
|
+
`render` validates the pack digest and active blacklist policy but does not resolve its
|
|
2538
|
+
original references. It is therefore available for historical review after `validate`
|
|
2539
|
+
reports `source-expired`, unless an exposed pack field is now blacklisted.
|
|
2540
|
+
|
|
2362
2541
|
### semantic
|
|
2363
2542
|
|
|
2364
2543
|
Validate or print the optional, version-controlled `dbcli.semantic.json` in the
|
|
@@ -2485,6 +2664,39 @@ limits, audit, and recovery safeguards remain in force.
|
|
|
2485
2664
|
|
|
2486
2665
|
**Permission:** n/a (local files only; no database connection).
|
|
2487
2666
|
|
|
2667
|
+
### contract
|
|
2668
|
+
|
|
2669
|
+
Inspect the optional, version-controlled `dbcli.contracts.json` without opening a
|
|
2670
|
+
database connection. A contract adds a reviewed owner and descriptive evidence policy
|
|
2671
|
+
to canonical semantic references; it is not an executable data contract, assertion,
|
|
2672
|
+
query, or verification scenario.
|
|
2673
|
+
|
|
2674
|
+
```bash
|
|
2675
|
+
dbcli contract validate --format json
|
|
2676
|
+
dbcli contract context --format json
|
|
2677
|
+
dbcli contract context --format markdown
|
|
2678
|
+
dbcli contract search customer --format json
|
|
2679
|
+
dbcli contract drift --format json
|
|
2680
|
+
```
|
|
2681
|
+
|
|
2682
|
+
The strict v1 artifact contains `version` and `contracts`. Each contract has a unique
|
|
2683
|
+
canonical `name`, `status` (`draft`, `approved`, or `deprecated`), bounded plain-text
|
|
2684
|
+
`description` and `owner`, one or more canonical semantic `subjects`, optional aliases,
|
|
2685
|
+
and an `evidencePolicy` of `none`, `receipt-required`, or `verification-required`.
|
|
2686
|
+
Subjects must remain in the existing visible semantic registry; protected identifiers,
|
|
2687
|
+
unknown keys, duplicate names or subjects, SQL-like text, credentials, and stale
|
|
2688
|
+
references fail closed without exposing protected names.
|
|
2689
|
+
|
|
2690
|
+
`contract validate` requires an explicit valid artifact. `contract context` and
|
|
2691
|
+
`contract search` return only valid approved contracts; draft and deprecated contracts
|
|
2692
|
+
remain local review artifacts. `contract drift` reports `valid`, `stale`, `invalid`, or
|
|
2693
|
+
`unavailable` evidence offline and exits non-zero except for `valid`. A missing default
|
|
2694
|
+
file is allowed by `skill context` and leaves ordinary semantic context unchanged; a
|
|
2695
|
+
present invalid file fails closed. No contract command writes a file, opens a database
|
|
2696
|
+
connection, widens a `QueryDraft`, or changes permission/blacklist behavior.
|
|
2697
|
+
|
|
2698
|
+
**Permission:** n/a (local files only; no database connection).
|
|
2699
|
+
|
|
2488
2700
|
### skill
|
|
2489
2701
|
|
|
2490
2702
|
Emit `SKILL.md` (and the companion `reference.md`) to stdout, a file, or an
|
|
@@ -3024,9 +3236,9 @@ Permission is derived from the command's first token (case-insensitive). Unknown
|
|
|
3024
3236
|
|
|
3025
3237
|
| Tier | Commands |
|
|
3026
3238
|
|------|----------|
|
|
3027
|
-
| `query-only` | `GET`, `MGET`, `STRLEN`, `EXISTS`, `TTL`, `PTTL`, `TYPE`, `SCAN`, `HGET`, `HGETALL`, `HKEYS`, `HVALS`, `HLEN`, `HEXISTS`, `HMGET`, `LRANGE`, `LLEN`, `LINDEX`, `SMEMBERS`, `SCARD`, `SISMEMBER`, `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZCARD`, `ZSCORE`, `PING`, `ECHO` |
|
|
3028
|
-
| `read-write` | `SET`, `SETEX`, `SETNX`, `PSETEX`, `MSET`, `MSETNX`, `APPEND`, `INCR`/`INCRBY`, `DECR`/`DECRBY`, `HSET`/`HSETNX`/`HMSET`/`HINCRBY`, `LPUSH`/`RPUSH`/`LPOP`/`RPOP`/`LSET`, `SADD`/`SREM`, `ZADD`/`ZREM`, `EXPIRE`/`EXPIREAT`/`PEXPIRE`/`PERSIST`, `RENAME` |
|
|
3029
|
-
| `data-admin` | `DEL`, `UNLINK`, `HDEL` |
|
|
3239
|
+
| `query-only` | `GET`, `MGET`, `STRLEN`, `EXISTS`, `TTL`, `PTTL`, `TYPE`, `SCAN`, `HGET`, `HGETALL`, `HKEYS`, `HVALS`, `HLEN`, `HEXISTS`, `HMGET`, `LRANGE`, `LLEN`, `LINDEX`, `SMEMBERS`, `SCARD`, `SISMEMBER`, `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZCARD`, `ZSCORE`, `XLEN`, `XREAD`, `XRANGE`, `XREVRANGE`, `PING`, `ECHO` |
|
|
3240
|
+
| `read-write` | `SET`, `SETEX`, `SETNX`, `PSETEX`, `MSET`, `MSETNX`, `APPEND`, `INCR`/`INCRBY`, `DECR`/`DECRBY`, `HSET`/`HSETNX`/`HMSET`/`HINCRBY`, `LPUSH`/`RPUSH`/`LPOP`/`RPOP`/`LSET`/`LREM`, `SADD`/`SREM`, `ZADD`/`ZREM`, `XADD`, `EXPIRE`/`EXPIREAT`/`PEXPIRE`/`PERSIST`, `RENAME` |
|
|
3241
|
+
| `data-admin` | `DEL`, `UNLINK`, `HDEL`, `XDEL` |
|
|
3030
3242
|
| `admin` | `FLUSHDB`, `FLUSHALL`, `CONFIG`, `INFO`, `CLIENT`, `DEBUG`, `SHUTDOWN`, `KEYS`, `MONITOR`, `SAVE`, `BGSAVE`, `BGREWRITEAOF`, `REPLICAOF`, `SLAVEOF`, `ACL` |
|
|
3031
3243
|
|
|
3032
3244
|
### Schema inspection
|
|
@@ -3237,5 +3449,5 @@ GET /orders/_search
|
|
|
3237
3449
|
|
|
3238
3450
|
- Writes (`insert`/`update`/`delete`) are not exposed yet — the adapter implements them, but the CLI currently only routes them for SQL and MongoDB. Read-only `export` (v1.22) and the interactive `shell` (v1.22) are available.
|
|
3239
3451
|
- No `_search/scroll` or PIT pagination at the CLI layer; large pulls need a saved external script.
|
|
3240
|
-
- `check`, `diff`,
|
|
3452
|
+
- `check`, `diff`, and `migrate` are SQL-only and exit with errors (or fall through to a generic "unsupported" path). `q` **is** supported — Elasticsearch snippets use the `.elasticsearch.sql` extension (see `@diag/es-cluster-health`).
|
|
3241
3453
|
- Blacklist column rules are applied to flattened hit rows on `query`; table-level blacklist rejects an index up front.
|
package/assets/ui-template.html
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>dbcli Interactive Report</title>
|
|
7
7
|
<style id="dbcli-style">
|
|
8
|
-
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap")
|
|
8
|
+
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
|
|
9
9
|
|
|
10
|
-
/*! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}body{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity));font-family:Inter,system-ui,sans-serif;--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.card{overflow:hidden;border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));--tw-shadow:0 2px 15px -3px #00000012,0 4px 6px -2px #0000000d;--tw-shadow-colored:0 2px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sticky{position:sticky}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-8{margin-bottom:2rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[450px\]{height:450px}.h-full{height:100%}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 250 252/var(--tw-divide-opacity))}.overflow-x-auto{overflow-x:auto}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity))}.border-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity))}.border-sky-200{--tw-border-opacity:1;border-color:rgb(186 230 253/var(--tw-border-opacity))}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity))}.border-l-primary-500{--tw-border-opacity:1;border-left-color:rgb(59 130 246/var(--tw-border-opacity))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity))}.bg-slate-50\/30{background-color:#f8fafc4d}.bg-slate-50\/50{background-color:#f8fafc80}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/80{background-color:#fffc}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity))}.text-amber-950{--tw-text-opacity:1;color:rgb(69 26 3/var(--tw-text-opacity))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity))}.text-sky-950{--tw-text-opacity:1;color:rgb(8 47 73/var(--tw-text-opacity))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.hover\:bg-primary-50\/30:hover{background-color:#eff6ff4d}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group:hover .group-hover\:text-primary-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}@media (min-width:640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:p-8{padding:2rem}}
|
|
10
|
+
/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1));font-family:Inter,system-ui,sans-serif;--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.card{overflow:hidden;border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));--tw-shadow:0 2px 15px -3px rgba(0,0,0,.07),0 4px 6px -2px rgba(0,0,0,.05);--tw-shadow-colored:0 2px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sticky{position:sticky}.top-0{top:0}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-8{margin-bottom:2rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[450px\]{height:450px}.h-full{height:100%}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(248 250 252/var(--tw-divide-opacity,1))}.overflow-x-auto{overflow-x:auto}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity,1))}.border-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/var(--tw-border-opacity,1))}.border-sky-200{--tw-border-opacity:1;border-color:rgb(186 230 253/var(--tw-border-opacity,1))}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity,1))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.border-l-primary-500{--tw-border-opacity:1;border-left-color:rgb(59 130 246/var(--tw-border-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-50\/30{background-color:rgba(248,250,252,.3)}.bg-slate-50\/50{background-color:rgba(248,250,252,.5)}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/80{background-color:hsla(0,0%,100%,.8)}.p-1\.5{padding:.375rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-1{padding-bottom:.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-amber-950{--tw-text-opacity:1;color:rgb(69 26 3/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-primary-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-primary-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity,1))}.text-sky-950{--tw-text-opacity:1;color:rgb(8 47 73/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-primary-50:hover{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.hover\:bg-primary-50\/30:hover{background-color:rgba(239,246,255,.3)}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group:hover .group-hover\:text-primary-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:p-8{padding:2rem}}
|
|
11
11
|
</style>
|
|
12
12
|
</head>
|
|
13
13
|
<body class="bg-slate-50">
|