@carllee1983/dbcli 1.38.1 → 1.39.1
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 +15 -4
- package/.github/skills/dbcli/SKILL.md +15 -4
- package/CHANGELOG.md +16 -0
- package/assets/SKILL.md +15 -4
- package/assets/SKILL.zh-TW.md +11 -2
- package/assets/ui-template.html +8 -8
- package/dist/cli.mjs +59 -49
- package/dist/core.mjs +13 -2
- package/dist/ui-style.css +1 -1
- package/package.json +1 -1
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +15 -4
- package/skills/dbcli/SKILL.md +15 -4
package/dist/cli.mjs
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "@carllee1983/dbcli",
|
|
55
|
-
version: "1.
|
|
55
|
+
version: "1.39.1",
|
|
56
56
|
description: "Database CLI for AI agents",
|
|
57
57
|
type: "module",
|
|
58
58
|
publishConfig: {
|
|
@@ -8809,8 +8809,9 @@ class BlacklistManager {
|
|
|
8809
8809
|
}
|
|
8810
8810
|
|
|
8811
8811
|
// src/core/saved-queries/types.ts
|
|
8812
|
-
var SavedQueryError;
|
|
8812
|
+
var SUPPORTED_CHART_TYPES, SavedQueryError;
|
|
8813
8813
|
var init_types2 = __esm(() => {
|
|
8814
|
+
SUPPORTED_CHART_TYPES = ["line", "bar", "area", "pie"];
|
|
8814
8815
|
SavedQueryError = class SavedQueryError extends Error {
|
|
8815
8816
|
code;
|
|
8816
8817
|
file;
|
|
@@ -9577,7 +9578,7 @@ function parseFrontmatter(yaml, input) {
|
|
|
9577
9578
|
const tags = Array.isArray(raw.tags) ? raw.tags.map(String) : [];
|
|
9578
9579
|
const index = typeof raw.index === "string" ? raw.index : undefined;
|
|
9579
9580
|
const intent = normaliseIntent(raw.intent, input);
|
|
9580
|
-
const visual = normaliseVisual(raw.visual);
|
|
9581
|
+
const visual = normaliseVisual(raw.visual, input);
|
|
9581
9582
|
const target = typeof raw.target === "string" ? raw.target : undefined;
|
|
9582
9583
|
const operation = raw.operation === "find" || raw.operation === "aggregate" ? raw.operation : undefined;
|
|
9583
9584
|
const verify = normaliseVerify(raw.verify, input);
|
|
@@ -9599,7 +9600,7 @@ function parseFrontmatter(yaml, input) {
|
|
|
9599
9600
|
warnings
|
|
9600
9601
|
};
|
|
9601
9602
|
}
|
|
9602
|
-
function normaliseVisual(value) {
|
|
9603
|
+
function normaliseVisual(value, input) {
|
|
9603
9604
|
if (value === undefined || value === null || typeof value !== "object")
|
|
9604
9605
|
return;
|
|
9605
9606
|
const raw = value;
|
|
@@ -9625,6 +9626,9 @@ function normaliseVisual(value) {
|
|
|
9625
9626
|
if (typeof item === "object" && item !== null) {
|
|
9626
9627
|
const c2 = item;
|
|
9627
9628
|
if (typeof c2.type === "string" && typeof c2.x === "string" && Array.isArray(c2.y)) {
|
|
9629
|
+
if (!SUPPORTED_CHART_TYPES.includes(c2.type)) {
|
|
9630
|
+
throw new SavedQueryError(`Snippet '${input.key}' has invalid chart type '${c2.type}' (supported: ${SUPPORTED_CHART_TYPES.join(", ")})`, "PARSE_ERROR", input.file);
|
|
9631
|
+
}
|
|
9628
9632
|
charts.push({
|
|
9629
9633
|
type: c2.type,
|
|
9630
9634
|
title: typeof c2.title === "string" ? c2.title : undefined,
|
|
@@ -24863,6 +24867,17 @@ class RedisAdapter {
|
|
|
24863
24867
|
const regexes = rules.map((p) => globToRegex(p));
|
|
24864
24868
|
return keys.filter((k) => !regexes.some((r) => r.test(k))).map((name2) => ({ name: name2 }));
|
|
24865
24869
|
}
|
|
24870
|
+
async sampleKeyNames(limit) {
|
|
24871
|
+
const client = this.requireClient();
|
|
24872
|
+
const keys = await scanAllKeys(client, "*", limit, limit);
|
|
24873
|
+
const truncated = keys.length >= limit;
|
|
24874
|
+
const rules = this.blacklistRules;
|
|
24875
|
+
if (rules.length === 0)
|
|
24876
|
+
return { names: keys, truncated };
|
|
24877
|
+
const regexes = rules.map((p) => globToRegex(p));
|
|
24878
|
+
const names = keys.filter((k) => !regexes.some((r) => r.test(k)));
|
|
24879
|
+
return { names, truncated };
|
|
24880
|
+
}
|
|
24866
24881
|
async getDbSize() {
|
|
24867
24882
|
const client = this.requireClient();
|
|
24868
24883
|
const reply = await client.send("DBSIZE", []);
|
|
@@ -25145,7 +25160,7 @@ function parseRedisCommand(input) {
|
|
|
25145
25160
|
push();
|
|
25146
25161
|
return tokens;
|
|
25147
25162
|
}
|
|
25148
|
-
async function scanAllKeys(client, pattern, count) {
|
|
25163
|
+
async function scanAllKeys(client, pattern, count, maxKeys = 1e5) {
|
|
25149
25164
|
const seen = new Set;
|
|
25150
25165
|
let cursor = "0";
|
|
25151
25166
|
do {
|
|
@@ -25160,7 +25175,7 @@ async function scanAllKeys(client, pattern, count) {
|
|
|
25160
25175
|
for (const k of batch)
|
|
25161
25176
|
seen.add(k);
|
|
25162
25177
|
cursor = next;
|
|
25163
|
-
if (seen.size >=
|
|
25178
|
+
if (seen.size >= maxKeys)
|
|
25164
25179
|
break;
|
|
25165
25180
|
} while (cursor !== "0");
|
|
25166
25181
|
return Array.from(seen);
|
|
@@ -96295,32 +96310,6 @@ var init_meta_commands = __esm(() => {
|
|
|
96295
96310
|
VALID_FORMATS = ["table", "json", "csv"];
|
|
96296
96311
|
});
|
|
96297
96312
|
|
|
96298
|
-
// src/core/repl/command-registry.ts
|
|
96299
|
-
var exports_command_registry = {};
|
|
96300
|
-
__export(exports_command_registry, {
|
|
96301
|
-
setReplCommandNames: () => setReplCommandNames,
|
|
96302
|
-
isReplCommandKnown: () => isReplCommandKnown,
|
|
96303
|
-
getReplCommandNames: () => getReplCommandNames,
|
|
96304
|
-
REPL_DENYLIST: () => REPL_DENYLIST
|
|
96305
|
-
});
|
|
96306
|
-
function setReplCommandNames(input) {
|
|
96307
|
-
const allowed = input.filter((n) => !REPL_DENYLIST.has(n));
|
|
96308
|
-
names = allowed;
|
|
96309
|
-
known = new Set(allowed);
|
|
96310
|
-
}
|
|
96311
|
-
function getReplCommandNames() {
|
|
96312
|
-
return names;
|
|
96313
|
-
}
|
|
96314
|
-
function isReplCommandKnown(name2) {
|
|
96315
|
-
return known.has(name2);
|
|
96316
|
-
}
|
|
96317
|
-
var REPL_DENYLIST, names, known;
|
|
96318
|
-
var init_command_registry = __esm(() => {
|
|
96319
|
-
REPL_DENYLIST = new Set(["shell"]);
|
|
96320
|
-
names = [];
|
|
96321
|
-
known = new Set;
|
|
96322
|
-
});
|
|
96323
|
-
|
|
96324
96313
|
// src/core/repl/command-dispatcher.ts
|
|
96325
96314
|
function parseCommandLine(input) {
|
|
96326
96315
|
const trimmed = input.trim();
|
|
@@ -96356,12 +96345,9 @@ function splitRespectingQuotes(input) {
|
|
|
96356
96345
|
}
|
|
96357
96346
|
return tokens;
|
|
96358
96347
|
}
|
|
96359
|
-
function isKnownCommand(name2) {
|
|
96360
|
-
return
|
|
96348
|
+
function isKnownCommand(name2, commandNames) {
|
|
96349
|
+
return commandNames.includes(name2);
|
|
96361
96350
|
}
|
|
96362
|
-
var init_command_dispatcher = __esm(() => {
|
|
96363
|
-
init_command_registry();
|
|
96364
|
-
});
|
|
96365
96351
|
|
|
96366
96352
|
// src/core/repl/history-manager.ts
|
|
96367
96353
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -96488,7 +96474,7 @@ class ReplEngine {
|
|
|
96488
96474
|
}
|
|
96489
96475
|
async handleCommand(input) {
|
|
96490
96476
|
const parsed = parseCommandLine(input);
|
|
96491
|
-
if (!isKnownCommand(parsed.command)) {
|
|
96477
|
+
if (!isKnownCommand(parsed.command, this.context.commandNames)) {
|
|
96492
96478
|
if (this.context.system === "redis") {
|
|
96493
96479
|
this.history.add(input);
|
|
96494
96480
|
return this.executeSql(input);
|
|
@@ -96631,7 +96617,6 @@ var import_picocolors3;
|
|
|
96631
96617
|
var init_repl_engine = __esm(() => {
|
|
96632
96618
|
init_input_classifier();
|
|
96633
96619
|
init_meta_commands();
|
|
96634
|
-
init_command_dispatcher();
|
|
96635
96620
|
init_history_manager();
|
|
96636
96621
|
init_permission_guard();
|
|
96637
96622
|
init_query_result_formatter();
|
|
@@ -96679,7 +96664,7 @@ function createCompleter(ctx) {
|
|
|
96679
96664
|
}
|
|
96680
96665
|
const words = trimmed.split(/\s+/);
|
|
96681
96666
|
if (words.length === 1) {
|
|
96682
|
-
const cmdHits = matchWithSuffix([...
|
|
96667
|
+
const cmdHits = matchWithSuffix([...ctx.commandNames], lastWord);
|
|
96683
96668
|
const sqlHits = matchWithSuffix([...SQL_KEYWORDS_FOR_COMPLETION], lastWord);
|
|
96684
96669
|
return [[...cmdHits[0], ...sqlHits[0]], lastWord];
|
|
96685
96670
|
}
|
|
@@ -96732,7 +96717,6 @@ function matchWithSuffix(candidates, prefix) {
|
|
|
96732
96717
|
var REDIS_COMMANDS, TABLE_POSITION_KEYWORDS, COMMANDS_TAKING_TABLE_ARG;
|
|
96733
96718
|
var init_completer = __esm(() => {
|
|
96734
96719
|
init_types10();
|
|
96735
|
-
init_command_registry();
|
|
96736
96720
|
init_command_metadata();
|
|
96737
96721
|
REDIS_COMMANDS = Object.keys(REDIS_COMMAND_TABLE);
|
|
96738
96722
|
TABLE_POSITION_KEYWORDS = new Set([
|
|
@@ -96921,6 +96905,20 @@ var init_es_shell = __esm(() => {
|
|
|
96921
96905
|
import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
96922
96906
|
});
|
|
96923
96907
|
|
|
96908
|
+
// src/core/repl/command-registry.ts
|
|
96909
|
+
var exports_command_registry = {};
|
|
96910
|
+
__export(exports_command_registry, {
|
|
96911
|
+
deriveReplCommandNames: () => deriveReplCommandNames,
|
|
96912
|
+
REPL_DENYLIST: () => REPL_DENYLIST
|
|
96913
|
+
});
|
|
96914
|
+
function deriveReplCommandNames(topLevel) {
|
|
96915
|
+
return topLevel.filter((n) => !REPL_DENYLIST.has(n));
|
|
96916
|
+
}
|
|
96917
|
+
var REPL_DENYLIST;
|
|
96918
|
+
var init_command_registry = __esm(() => {
|
|
96919
|
+
REPL_DENYLIST = new Set(["shell"]);
|
|
96920
|
+
});
|
|
96921
|
+
|
|
96924
96922
|
// src/commands/shell.ts
|
|
96925
96923
|
import { createInterface as createInterface3 } from "readline";
|
|
96926
96924
|
import { join as join32 } from "path";
|
|
@@ -96945,6 +96943,14 @@ async function populateMongoColumns(mongoAdapter, collectionNames, threshold = M
|
|
|
96945
96943
|
}
|
|
96946
96944
|
return columnsByTable;
|
|
96947
96945
|
}
|
|
96946
|
+
async function populateRedisKeyCompletion(adapter, limit = REDIS_COMPLETION_KEY_LIMIT) {
|
|
96947
|
+
try {
|
|
96948
|
+
const { names, truncated } = await adapter.sampleKeyNames(limit);
|
|
96949
|
+
return { tableNames: names, truncated };
|
|
96950
|
+
} catch {
|
|
96951
|
+
return { tableNames: [], truncated: false };
|
|
96952
|
+
}
|
|
96953
|
+
}
|
|
96948
96954
|
async function runShell(options, configPath) {
|
|
96949
96955
|
let config;
|
|
96950
96956
|
try {
|
|
@@ -96972,9 +96978,12 @@ async function runShell(options, configPath) {
|
|
|
96972
96978
|
let tableNames = [];
|
|
96973
96979
|
let columnsByTable = {};
|
|
96974
96980
|
if (isRedis) {
|
|
96975
|
-
const
|
|
96976
|
-
tableNames =
|
|
96981
|
+
const completion = await populateRedisKeyCompletion(redisInner);
|
|
96982
|
+
tableNames = completion.tableNames;
|
|
96977
96983
|
columnsByTable = {};
|
|
96984
|
+
if (completion.truncated) {
|
|
96985
|
+
console.error(import_picocolors5.default.dim(`Redis shell: large keyspace; tab completion limited to the first ${REDIS_COMPLETION_KEY_LIMIT} keys.`));
|
|
96986
|
+
}
|
|
96978
96987
|
} else if (isMongoDB) {
|
|
96979
96988
|
const collections = await adapter.listTables();
|
|
96980
96989
|
tableNames = collections.map((collection) => collection.name);
|
|
@@ -96994,17 +97003,18 @@ async function runShell(options, configPath) {
|
|
|
96994
97003
|
}
|
|
96995
97004
|
}
|
|
96996
97005
|
}
|
|
97006
|
+
const { buildProgram } = await Promise.resolve().then(() => (init_program(), exports_program));
|
|
97007
|
+
const { buildCompletionTree: buildCompletionTree2, listTopLevelCommandNames: listTopLevelCommandNames2 } = await Promise.resolve().then(() => exports_command_tree);
|
|
97008
|
+
const { deriveReplCommandNames: deriveReplCommandNames2 } = await Promise.resolve().then(() => (init_command_registry(), exports_command_registry));
|
|
97009
|
+
const commandNames = deriveReplCommandNames2(listTopLevelCommandNames2(buildCompletionTree2(buildProgram())));
|
|
96997
97010
|
const context = {
|
|
96998
97011
|
configPath,
|
|
96999
97012
|
permission: config.permission,
|
|
97000
97013
|
system: config.connection.system,
|
|
97001
97014
|
tableNames,
|
|
97002
|
-
columnsByTable
|
|
97015
|
+
columnsByTable,
|
|
97016
|
+
commandNames
|
|
97003
97017
|
};
|
|
97004
|
-
const { buildProgram } = await Promise.resolve().then(() => (init_program(), exports_program));
|
|
97005
|
-
const { buildCompletionTree: buildCompletionTree2, listTopLevelCommandNames: listTopLevelCommandNames2 } = await Promise.resolve().then(() => exports_command_tree);
|
|
97006
|
-
const { setReplCommandNames: setReplCommandNames2 } = await Promise.resolve().then(() => (init_command_registry(), exports_command_registry));
|
|
97007
|
-
setReplCommandNames2(listTopLevelCommandNames2(buildCompletionTree2(buildProgram())));
|
|
97008
97018
|
const engine = new ReplEngine(adapter, context, HISTORY_PATH, config);
|
|
97009
97019
|
const complete = createCompleter(context);
|
|
97010
97020
|
console.error(import_picocolors5.default.bold(t_vars("shell.welcome", {
|
|
@@ -97094,7 +97104,7 @@ async function runBatchSession(engine, input) {
|
|
|
97094
97104
|
}
|
|
97095
97105
|
}
|
|
97096
97106
|
}
|
|
97097
|
-
var import_picocolors5, HISTORY_PATH, MONGO_COMPLETION_EAGER_THRESHOLD = 20, shellCommand;
|
|
97107
|
+
var import_picocolors5, HISTORY_PATH, MONGO_COMPLETION_EAGER_THRESHOLD = 20, REDIS_COMPLETION_KEY_LIMIT = 1000, shellCommand;
|
|
97098
97108
|
var init_shell3 = __esm(() => {
|
|
97099
97109
|
init_esm();
|
|
97100
97110
|
init_config();
|
package/dist/core.mjs
CHANGED
|
@@ -15956,6 +15956,17 @@ class RedisAdapter {
|
|
|
15956
15956
|
const regexes = rules.map((p) => globToRegex(p));
|
|
15957
15957
|
return keys.filter((k) => !regexes.some((r) => r.test(k))).map((name) => ({ name }));
|
|
15958
15958
|
}
|
|
15959
|
+
async sampleKeyNames(limit) {
|
|
15960
|
+
const client = this.requireClient();
|
|
15961
|
+
const keys = await scanAllKeys(client, "*", limit, limit);
|
|
15962
|
+
const truncated = keys.length >= limit;
|
|
15963
|
+
const rules = this.blacklistRules;
|
|
15964
|
+
if (rules.length === 0)
|
|
15965
|
+
return { names: keys, truncated };
|
|
15966
|
+
const regexes = rules.map((p) => globToRegex(p));
|
|
15967
|
+
const names = keys.filter((k) => !regexes.some((r) => r.test(k)));
|
|
15968
|
+
return { names, truncated };
|
|
15969
|
+
}
|
|
15959
15970
|
async getDbSize() {
|
|
15960
15971
|
const client = this.requireClient();
|
|
15961
15972
|
const reply = await client.send("DBSIZE", []);
|
|
@@ -16238,7 +16249,7 @@ function parseRedisCommand(input) {
|
|
|
16238
16249
|
push();
|
|
16239
16250
|
return tokens;
|
|
16240
16251
|
}
|
|
16241
|
-
async function scanAllKeys(client, pattern, count) {
|
|
16252
|
+
async function scanAllKeys(client, pattern, count, maxKeys = 1e5) {
|
|
16242
16253
|
const seen = new Set;
|
|
16243
16254
|
let cursor = "0";
|
|
16244
16255
|
do {
|
|
@@ -16253,7 +16264,7 @@ async function scanAllKeys(client, pattern, count) {
|
|
|
16253
16264
|
for (const k of batch)
|
|
16254
16265
|
seen.add(k);
|
|
16255
16266
|
cursor = next;
|
|
16256
|
-
if (seen.size >=
|
|
16267
|
+
if (seen.size >= maxKeys)
|
|
16257
16268
|
break;
|
|
16258
16269
|
} while (cursor !== "0");
|
|
16259
16270
|
return Array.from(seen);
|
package/dist/ui-style.css
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
|
|
2
2
|
|
|
3
|
-
/*! 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}.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-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-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/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-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-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-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-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}}
|
|
3
|
+
/*! 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-primary-100{--tw-border-opacity:1;border-color:rgb(219 234 254/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-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-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-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-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}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dbcli
|
|
3
|
-
description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
|
|
3
|
+
description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, generate DB reports or interactive HTML dashboards, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, generating a report/dashboard/HTML UI from raw SQL or saved snippets, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# dbcli
|
|
@@ -30,6 +30,7 @@ the CLI package has not been installed globally.
|
|
|
30
30
|
| --- | --- |
|
|
31
31
|
| A named workflow fits ("diagnose slow query", "audit permissions") | `skill tasks list` → `skill tasks plan <pack>` — **prefer this; do not invent steps** |
|
|
32
32
|
| A fixed diagnostic goal | `guide <goal>` (`slow-query` / `capacity` / `health` / `index-usage` / `permissions` / `schema-overview`; `guide --list`) |
|
|
33
|
+
| A DB report / dashboard / HTML UI | `blacklist list` → `queries search <keywords>` or `queries suggest <intent>` → `queries show @<name>` → browser: `q @<name> --param k=v --ui`; file: `q @<name> --format html > report.html` or `export "<SQL>" --format html --output report.html` |
|
|
33
34
|
| Setting up a connection | see **Connection setup** |
|
|
34
35
|
| Anything else | run commands manually; consult the **Developer workflows** cheat-sheet |
|
|
35
36
|
|
|
@@ -89,6 +90,7 @@ in **How to use dbcli** still applies.
|
|
|
89
90
|
| Situation | Minimum safe path |
|
|
90
91
|
| --- | --- |
|
|
91
92
|
| DB-backed feature | `blacklist list` → `schema <object>` → `queries suggest <intent>` |
|
|
93
|
+
| DB report / dashboard request | `blacklist list` → `queries search <keywords>` / `queries suggest <intent>` → `queries show @<name>` → `q @<name> --ui` or `--format html` |
|
|
92
94
|
| Application data bug | `audit tail --for-agent --n 10` → `blacklist list` → `schema <object>` → narrow query |
|
|
93
95
|
| ORM or migration work | `schema --format json` → `diff --snapshot <name>` → `migrate add-index`/`add-column` (preview SQL) → `diff --against <snapshot>` |
|
|
94
96
|
| PR database review | Review changed persistence paths, then propose concrete `schema` / `plan` / `dry-run` / `report` / `guide` commands per material claim. |
|
|
@@ -103,6 +105,11 @@ dbcli inspect --for-agent --format json
|
|
|
103
105
|
dbcli blacklist list --format json
|
|
104
106
|
dbcli schema <object> --format json
|
|
105
107
|
dbcli queries suggest <intent> --format json
|
|
108
|
+
dbcli queries search <report keywords> --format json
|
|
109
|
+
dbcli queries show @<name> --format json
|
|
110
|
+
dbcli q @<name> --param k=v --ui
|
|
111
|
+
dbcli q @<name> --param k=v --format html > report.html
|
|
112
|
+
dbcli export "<SQL>" --format html --output report.html
|
|
106
113
|
dbcli audit tail --for-agent --n 10
|
|
107
114
|
dbcli diff --snapshot <name>
|
|
108
115
|
dbcli report --section perf --format json
|
|
@@ -425,14 +432,18 @@ Run with `dbcli q @diag/<topic>` (engine variant auto-picked by the active conne
|
|
|
425
432
|
|
|
426
433
|
```bash
|
|
427
434
|
dbcli query "SELECT day, dau FROM dau_daily" --ui # open in browser
|
|
435
|
+
dbcli q @analytics/revenue --param days=30 --ui # snippet metadata + charts/KPIs
|
|
436
|
+
dbcli q @analytics/revenue --param days=30 --format html > report.html
|
|
428
437
|
dbcli query "SELECT * FROM orders" --format html > out.html # pipe HTML to stdout
|
|
429
438
|
dbcli export "SELECT * FROM orders" --format html --output orders.html
|
|
430
439
|
```
|
|
431
440
|
|
|
432
441
|
`--ui` implies `--format html` and opens the file; `--format html` alone prints to stdout.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
442
|
+
When a saved snippet exists, prefer `q @<name> --ui` / `q @<name> --format html` because snippet
|
|
443
|
+
metadata can drive titles, KPI cards, and charts. Blacklist redaction is applied **before**
|
|
444
|
+
rendering. To get KPIs and charts instead of a plain table, add a `visual:` block (`title`,
|
|
445
|
+
`kpis[]`, `charts[]`) to the snippet frontmatter — see reference.md for the full `visual:`
|
|
446
|
+
schema. Raw `query` / `export` invocations render a sortable table only.
|
|
436
447
|
|
|
437
448
|
## Common workflows
|
|
438
449
|
|
package/skills/dbcli/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dbcli
|
|
3
|
-
description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
|
|
3
|
+
description: Database CLI for AI agents with permission-based access control. Use to set up new connections, query, inspect schemas, insert/update/delete, export results, generate DB reports or interactive HTML dashboards, and blacklist sensitive columns/tables. Supports MySQL, PostgreSQL, MariaDB, MongoDB, Redis, and Elasticsearch with multiple named connections per project and custom env files. Trigger when configuring a database connection (`.dbcli` / `.env`), choosing between v1 single and v2 multi-connection layouts, picking auth modes (URI, env refs, Cloud ID, API key), running SQL / MongoDB JSON / Redis commands / Elasticsearch DSL, generating a report/dashboard/HTML UI from raw SQL or saved snippets, exploring table/collection/key/index structures, switching database environments, protecting sensitive data from AI access, or performing automated recovery and guided remediation after command failures. For exhaustive flags and examples, read the sibling `reference.md`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# dbcli
|
|
@@ -30,6 +30,7 @@ the CLI package has not been installed globally.
|
|
|
30
30
|
| --- | --- |
|
|
31
31
|
| A named workflow fits ("diagnose slow query", "audit permissions") | `skill tasks list` → `skill tasks plan <pack>` — **prefer this; do not invent steps** |
|
|
32
32
|
| A fixed diagnostic goal | `guide <goal>` (`slow-query` / `capacity` / `health` / `index-usage` / `permissions` / `schema-overview`; `guide --list`) |
|
|
33
|
+
| A DB report / dashboard / HTML UI | `blacklist list` → `queries search <keywords>` or `queries suggest <intent>` → `queries show @<name>` → browser: `q @<name> --param k=v --ui`; file: `q @<name> --format html > report.html` or `export "<SQL>" --format html --output report.html` |
|
|
33
34
|
| Setting up a connection | see **Connection setup** |
|
|
34
35
|
| Anything else | run commands manually; consult the **Developer workflows** cheat-sheet |
|
|
35
36
|
|
|
@@ -89,6 +90,7 @@ in **How to use dbcli** still applies.
|
|
|
89
90
|
| Situation | Minimum safe path |
|
|
90
91
|
| --- | --- |
|
|
91
92
|
| DB-backed feature | `blacklist list` → `schema <object>` → `queries suggest <intent>` |
|
|
93
|
+
| DB report / dashboard request | `blacklist list` → `queries search <keywords>` / `queries suggest <intent>` → `queries show @<name>` → `q @<name> --ui` or `--format html` |
|
|
92
94
|
| Application data bug | `audit tail --for-agent --n 10` → `blacklist list` → `schema <object>` → narrow query |
|
|
93
95
|
| ORM or migration work | `schema --format json` → `diff --snapshot <name>` → `migrate add-index`/`add-column` (preview SQL) → `diff --against <snapshot>` |
|
|
94
96
|
| PR database review | Review changed persistence paths, then propose concrete `schema` / `plan` / `dry-run` / `report` / `guide` commands per material claim. |
|
|
@@ -103,6 +105,11 @@ dbcli inspect --for-agent --format json
|
|
|
103
105
|
dbcli blacklist list --format json
|
|
104
106
|
dbcli schema <object> --format json
|
|
105
107
|
dbcli queries suggest <intent> --format json
|
|
108
|
+
dbcli queries search <report keywords> --format json
|
|
109
|
+
dbcli queries show @<name> --format json
|
|
110
|
+
dbcli q @<name> --param k=v --ui
|
|
111
|
+
dbcli q @<name> --param k=v --format html > report.html
|
|
112
|
+
dbcli export "<SQL>" --format html --output report.html
|
|
106
113
|
dbcli audit tail --for-agent --n 10
|
|
107
114
|
dbcli diff --snapshot <name>
|
|
108
115
|
dbcli report --section perf --format json
|
|
@@ -425,14 +432,18 @@ Run with `dbcli q @diag/<topic>` (engine variant auto-picked by the active conne
|
|
|
425
432
|
|
|
426
433
|
```bash
|
|
427
434
|
dbcli query "SELECT day, dau FROM dau_daily" --ui # open in browser
|
|
435
|
+
dbcli q @analytics/revenue --param days=30 --ui # snippet metadata + charts/KPIs
|
|
436
|
+
dbcli q @analytics/revenue --param days=30 --format html > report.html
|
|
428
437
|
dbcli query "SELECT * FROM orders" --format html > out.html # pipe HTML to stdout
|
|
429
438
|
dbcli export "SELECT * FROM orders" --format html --output orders.html
|
|
430
439
|
```
|
|
431
440
|
|
|
432
441
|
`--ui` implies `--format html` and opens the file; `--format html` alone prints to stdout.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
442
|
+
When a saved snippet exists, prefer `q @<name> --ui` / `q @<name> --format html` because snippet
|
|
443
|
+
metadata can drive titles, KPI cards, and charts. Blacklist redaction is applied **before**
|
|
444
|
+
rendering. To get KPIs and charts instead of a plain table, add a `visual:` block (`title`,
|
|
445
|
+
`kpis[]`, `charts[]`) to the snippet frontmatter — see reference.md for the full `visual:`
|
|
446
|
+
schema. Raw `query` / `export` invocations render a sortable table only.
|
|
436
447
|
|
|
437
448
|
## Common workflows
|
|
438
449
|
|