@carllee1983/dbcli 1.10.0 → 1.10.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/CHANGELOG.md +16 -0
- package/dist/cli.mjs +116 -96
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to dbcli are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.10.1] - 2026-05-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Packaged `dist/cli.mjs` 找不到 assets**:1.10.0 bundle 在 `task-paths.ts` / `snippet-paths.ts` 用 `import.meta.dir + ../../../` 解析 builtin 目錄,bundle 後三層往上會跳出 package root,npm 全域安裝的使用者執行 `dbcli queries list` / `dbcli skill tasks list` 讀不到資源。抽出 `src/utils/package-root.ts` 以 `package.json` 走訪定位 root,dev 與 bundle 都正確;`skill.ts` 內既有的 `findPackageRoot` 也收斂到同一處。
|
|
13
|
+
- **`dbcli q` 略過 blacklist 檢查(安全)**:`q.ts` 把空字串當作 `tableName` 傳給 `BlacklistValidator.filterColumns`,column-level redaction 永遠不命中;同時也沒呼叫 `checkTableBlacklist`,使用者可以透過 saved snippet 直接 SELECT 黑名單表/欄位繞開保護。改為從 `prepared.rewrittenSql` 抽出主表(SQL)或 `prepared.execHints.index`(ES),執行前先 `checkTableBlacklist('SELECT', target)`,並把真正的 `tableName` 餵給 `filterColumns`;Redis 維持原樣。
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **dist/ 整合 smoke 測試**:`tests/integration/dist-smoke.test.ts` 從 OS tmpdir 執行 `dist/cli.mjs`,覆蓋 `--version`、`skill --output`、`queries list`、`skill tasks list`,守住 packaged assets path 不再回退。
|
|
18
|
+
- **`q` blacklist 迴歸測試**:`tests/unit/commands/q-blacklist.test.ts` 覆蓋黑名單表阻擋、欄位 redact、未受影響 snippet 三種情境。
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Lint release-blocking**:`bun run lint` / `lint:fix` 加上 `--max-warnings=0`;同時清掉 45 個 `@typescript-eslint/no-explicit-any` warnings(以正型替代為主,`elasticsearch-adapter.ts` 因刻意不引入 `@elastic/elasticsearch` SDK 而以檔案層 `eslint-disable` 標註理由)。任何新 warning 從此會擋住 release。
|
|
23
|
+
|
|
8
24
|
## [1.10.0] - 2026-05-08
|
|
9
25
|
|
|
10
26
|
### Added
|
package/dist/cli.mjs
CHANGED
|
@@ -78734,26 +78734,55 @@ var init_runner = __esm(() => {
|
|
|
78734
78734
|
init_types2();
|
|
78735
78735
|
});
|
|
78736
78736
|
|
|
78737
|
+
// src/utils/package-root.ts
|
|
78738
|
+
import * as path from "path";
|
|
78739
|
+
function findPackageRoot() {
|
|
78740
|
+
if (cached)
|
|
78741
|
+
return cached;
|
|
78742
|
+
let dir = HERE;
|
|
78743
|
+
for (let i = 0;i < 6; i++) {
|
|
78744
|
+
if (Bun.file(path.join(dir, "package.json")).size > 0) {
|
|
78745
|
+
cached = dir;
|
|
78746
|
+
return dir;
|
|
78747
|
+
}
|
|
78748
|
+
const parent = path.dirname(dir);
|
|
78749
|
+
if (parent === dir)
|
|
78750
|
+
break;
|
|
78751
|
+
dir = parent;
|
|
78752
|
+
}
|
|
78753
|
+
cached = path.resolve(HERE, "..", "..");
|
|
78754
|
+
return cached;
|
|
78755
|
+
}
|
|
78756
|
+
function packageAssetPath(...segments) {
|
|
78757
|
+
return path.join(findPackageRoot(), "assets", ...segments);
|
|
78758
|
+
}
|
|
78759
|
+
var cached = null, HERE;
|
|
78760
|
+
var init_package_root = __esm(() => {
|
|
78761
|
+
HERE = import.meta.dir;
|
|
78762
|
+
});
|
|
78763
|
+
|
|
78737
78764
|
// src/core/saved-queries/snippet-paths.ts
|
|
78738
|
-
import { join as
|
|
78765
|
+
import { join as join12 } from "path";
|
|
78739
78766
|
function resolveBuiltinDir() {
|
|
78740
|
-
return
|
|
78767
|
+
return packageAssetPath("snippets");
|
|
78741
78768
|
}
|
|
78742
78769
|
function resolveSnippetDirs(workspaceRoot) {
|
|
78743
78770
|
return {
|
|
78744
78771
|
builtinDir: resolveBuiltinDir(),
|
|
78745
|
-
sharedDir:
|
|
78746
|
-
localDir:
|
|
78772
|
+
sharedDir: join12(workspaceRoot, ".dbcli-shared", "queries"),
|
|
78773
|
+
localDir: join12(workspaceRoot, ".dbcli", "queries")
|
|
78747
78774
|
};
|
|
78748
78775
|
}
|
|
78749
78776
|
function snippetKeyToFile(workspaceRoot, key2, source) {
|
|
78750
78777
|
const rel = key2.replace(/^@/, "") + ".sql";
|
|
78751
78778
|
if (source === "builtin")
|
|
78752
|
-
return
|
|
78779
|
+
return join12(resolveBuiltinDir(), rel);
|
|
78753
78780
|
const dir = source === "shared" ? ".dbcli-shared/queries" : ".dbcli/queries";
|
|
78754
|
-
return
|
|
78781
|
+
return join12(workspaceRoot, dir, rel);
|
|
78755
78782
|
}
|
|
78756
|
-
var init_snippet_paths = () => {
|
|
78783
|
+
var init_snippet_paths = __esm(() => {
|
|
78784
|
+
init_package_root();
|
|
78785
|
+
});
|
|
78757
78786
|
|
|
78758
78787
|
// src/core/saved-queries/engine-map.ts
|
|
78759
78788
|
function mapSystemToEngine(system) {
|
|
@@ -78827,7 +78856,7 @@ __export(exports_queries_rename, {
|
|
|
78827
78856
|
queriesRename: () => queriesRename
|
|
78828
78857
|
});
|
|
78829
78858
|
import { rename, mkdir } from "fs/promises";
|
|
78830
|
-
import { dirname } from "path";
|
|
78859
|
+
import { dirname as dirname2 } from "path";
|
|
78831
78860
|
async function queriesRename(oldName, newName, options = {}) {
|
|
78832
78861
|
if (!oldName.startsWith("@") || !newName.startsWith("@")) {
|
|
78833
78862
|
throw new Error(`Both names must start with '@'`);
|
|
@@ -78848,7 +78877,7 @@ async function queriesRename(oldName, newName, options = {}) {
|
|
|
78848
78877
|
for (const v of local) {
|
|
78849
78878
|
const dst = snippetKeyToFile(cwd, newName, "local");
|
|
78850
78879
|
const dstWithSuffix = preserveEngineSuffix(v.query.file, dst);
|
|
78851
|
-
await mkdir(
|
|
78880
|
+
await mkdir(dirname2(dstWithSuffix), { recursive: true });
|
|
78852
78881
|
await rename(v.query.file, dstWithSuffix);
|
|
78853
78882
|
await rewriteFrontmatterName(dstWithSuffix, newName.slice(1));
|
|
78854
78883
|
console.log(`renamed ${v.query.file} \u2192 ${dstWithSuffix}`);
|
|
@@ -78875,7 +78904,7 @@ __export(exports_queries_copy, {
|
|
|
78875
78904
|
queriesCopy: () => queriesCopy
|
|
78876
78905
|
});
|
|
78877
78906
|
import { mkdir as mkdir2, copyFile } from "fs/promises";
|
|
78878
|
-
import { dirname as
|
|
78907
|
+
import { dirname as dirname3, basename as basename2 } from "path";
|
|
78879
78908
|
async function queriesCopy(src, dst, options = {}) {
|
|
78880
78909
|
if (!src.startsWith("@") || !dst.startsWith("@")) {
|
|
78881
78910
|
throw new Error(`Both names must start with '@'`);
|
|
@@ -78893,7 +78922,7 @@ async function queriesCopy(src, dst, options = {}) {
|
|
|
78893
78922
|
}
|
|
78894
78923
|
for (const v of variants) {
|
|
78895
78924
|
const dstFile = mapEngineSuffix(v.query.file, snippetKeyToFile(cwd, dst, "local"));
|
|
78896
|
-
await mkdir2(
|
|
78925
|
+
await mkdir2(dirname3(dstFile), { recursive: true });
|
|
78897
78926
|
await copyFile(v.query.file, dstFile);
|
|
78898
78927
|
console.log(`copied ${v.query.file} \u2192 ${dstFile}`);
|
|
78899
78928
|
}
|
|
@@ -78912,7 +78941,7 @@ __export(exports_queries_import, {
|
|
|
78912
78941
|
queriesImport: () => queriesImport
|
|
78913
78942
|
});
|
|
78914
78943
|
import { stat, mkdir as mkdir3, copyFile as copyFile2 } from "fs/promises";
|
|
78915
|
-
import { basename as basename3, join as
|
|
78944
|
+
import { basename as basename3, join as join13, extname } from "path";
|
|
78916
78945
|
async function queriesImport(filePath, options = {}) {
|
|
78917
78946
|
const cwd = options.cwd ?? process.cwd();
|
|
78918
78947
|
if (extname(filePath) !== ".sql") {
|
|
@@ -78923,9 +78952,9 @@ async function queriesImport(filePath, options = {}) {
|
|
|
78923
78952
|
const baseName = options.as ? options.as.replace(/^@/, "") : basename3(filePath, ".sql").replace(/\.(postgres|mysql)$/, "");
|
|
78924
78953
|
const key2 = "@" + baseName;
|
|
78925
78954
|
parseSavedQuery({ key: key2, file: filePath, source: "local", text: text2 });
|
|
78926
|
-
const targetDir =
|
|
78955
|
+
const targetDir = join13(cwd, ".dbcli/queries");
|
|
78927
78956
|
await mkdir3(targetDir, { recursive: true });
|
|
78928
|
-
const target =
|
|
78957
|
+
const target = join13(targetDir, basename3(filePath));
|
|
78929
78958
|
if (await Bun.file(target).exists()) {
|
|
78930
78959
|
if (!options.force) {
|
|
78931
78960
|
const ok = await esm_default4({ message: `Overwrite ${target}?`, default: false });
|
|
@@ -78995,7 +79024,7 @@ var {
|
|
|
78995
79024
|
// package.json
|
|
78996
79025
|
var package_default = {
|
|
78997
79026
|
name: "@carllee1983/dbcli",
|
|
78998
|
-
version: "1.10.
|
|
79027
|
+
version: "1.10.1",
|
|
78999
79028
|
description: "Database CLI for AI agents",
|
|
79000
79029
|
type: "module",
|
|
79001
79030
|
publishConfig: {
|
|
@@ -79046,8 +79075,8 @@ var package_default = {
|
|
|
79046
79075
|
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration/adapters; docker compose -f docker-compose.test.yml down",
|
|
79047
79076
|
typecheck: "tsc --noEmit --pretty false",
|
|
79048
79077
|
"test:perf": "bun test ./tests/perf/*.bench.ts",
|
|
79049
|
-
lint: "eslint src tests --ext .ts",
|
|
79050
|
-
"lint:fix": "eslint src tests --ext .ts --fix",
|
|
79078
|
+
lint: "eslint src tests --ext .ts --max-warnings=0",
|
|
79079
|
+
"lint:fix": "eslint src tests --ext .ts --fix --max-warnings=0",
|
|
79051
79080
|
format: 'prettier --write "src/**/*.ts" "tests/**/*.ts"'
|
|
79052
79081
|
},
|
|
79053
79082
|
dependencies: {
|
|
@@ -84767,18 +84796,22 @@ async function qCommand(name, options, command) {
|
|
|
84767
84796
|
}));
|
|
84768
84797
|
return;
|
|
84769
84798
|
}
|
|
84799
|
+
const blacklistManager = new BlacklistManager(config);
|
|
84800
|
+
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
84801
|
+
const family = engineFamily(engine);
|
|
84802
|
+
const targetName = family === "sql" ? extractTableName(prepared.rewrittenSql) ?? "" : family === "es" ? prepared.execHints?.index ?? "" : "";
|
|
84803
|
+
if (family !== "redis" && targetName) {
|
|
84804
|
+
blacklistValidator.checkTableBlacklist("SELECT", targetName);
|
|
84805
|
+
}
|
|
84770
84806
|
const adapter = AdapterFactory.createAdapter(config.connection);
|
|
84771
84807
|
await adapter.connect();
|
|
84772
84808
|
try {
|
|
84773
|
-
const blacklistManager = new BlacklistManager(config);
|
|
84774
|
-
const blacklistValidator = new BlacklistValidator(blacklistManager);
|
|
84775
|
-
const family = engineFamily(engine);
|
|
84776
84809
|
const indexParams = family === "es" && prepared.execHints?.index ? [prepared.execHints.index] : [];
|
|
84777
84810
|
const start = performance.now();
|
|
84778
84811
|
const result = await adapter.execute(prepared.driver.sql, family === "sql" ? prepared.driver.values : indexParams);
|
|
84779
84812
|
const executionTimeMs = Math.round(performance.now() - start);
|
|
84780
84813
|
const columnNames = result.rows[0] ? Object.keys(result.rows[0]) : [];
|
|
84781
|
-
const filtered = family === "redis" ? { filteredRows: result.rows, omittedColumns: [] } : blacklistValidator.filterColumns(
|
|
84814
|
+
const filtered = family === "redis" ? { filteredRows: result.rows, omittedColumns: [] } : blacklistValidator.filterColumns(targetName, result.rows, columnNames);
|
|
84782
84815
|
const formatter = new QueryResultFormatter;
|
|
84783
84816
|
const out = formatter.format({
|
|
84784
84817
|
rows: filtered.filteredRows,
|
|
@@ -84790,7 +84823,7 @@ async function qCommand(name, options, command) {
|
|
|
84790
84823
|
statement: "SELECT",
|
|
84791
84824
|
affectedRows: 0,
|
|
84792
84825
|
...filtered.omittedColumns.length > 0 ? {
|
|
84793
|
-
securityNotification: blacklistValidator.buildSecurityNotification(
|
|
84826
|
+
securityNotification: blacklistValidator.buildSecurityNotification(targetName, filtered.omittedColumns)
|
|
84794
84827
|
} : {}
|
|
84795
84828
|
}
|
|
84796
84829
|
}, { format: options.format ?? "table" });
|
|
@@ -84812,10 +84845,10 @@ function parseCliParams(list) {
|
|
|
84812
84845
|
}
|
|
84813
84846
|
return out;
|
|
84814
84847
|
}
|
|
84815
|
-
async function readParamFile(
|
|
84816
|
-
if (!
|
|
84848
|
+
async function readParamFile(path2) {
|
|
84849
|
+
if (!path2)
|
|
84817
84850
|
return {};
|
|
84818
|
-
const text2 = await Bun.file(
|
|
84851
|
+
const text2 = await Bun.file(path2).text();
|
|
84819
84852
|
const parsed = JSON.parse(text2);
|
|
84820
84853
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
84821
84854
|
throw new Error(`--param-file must be a JSON object (got ${typeof parsed})`);
|
|
@@ -84845,7 +84878,7 @@ function handleQError(error) {
|
|
|
84845
84878
|
|
|
84846
84879
|
// src/commands/queries.ts
|
|
84847
84880
|
import { mkdir as mkdir4, writeFile as writeFile2 } from "fs/promises";
|
|
84848
|
-
import { dirname as
|
|
84881
|
+
import { dirname as dirname4 } from "path";
|
|
84849
84882
|
import { spawn } from "child_process";
|
|
84850
84883
|
init_saved_queries();
|
|
84851
84884
|
async function deriveEngine() {
|
|
@@ -84975,7 +85008,7 @@ async function queriesNew(name, options) {
|
|
|
84975
85008
|
process.exit(1);
|
|
84976
85009
|
return;
|
|
84977
85010
|
}
|
|
84978
|
-
await mkdir4(
|
|
85011
|
+
await mkdir4(dirname4(file), { recursive: true });
|
|
84979
85012
|
await writeFile2(file, scaffold(name), "utf8");
|
|
84980
85013
|
console.log(`Created ${file}`);
|
|
84981
85014
|
if (source === "shared")
|
|
@@ -85102,10 +85135,10 @@ queriesCommand.command("copy <src> <dst>").description(t("queries.copy_descripti
|
|
|
85102
85135
|
process.exit(1);
|
|
85103
85136
|
}
|
|
85104
85137
|
});
|
|
85105
|
-
queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (
|
|
85138
|
+
queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (path2, options) => {
|
|
85106
85139
|
try {
|
|
85107
85140
|
const { queriesImport: queriesImport2 } = await Promise.resolve().then(() => (init_queries_import(), exports_queries_import));
|
|
85108
|
-
await queriesImport2(
|
|
85141
|
+
await queriesImport2(path2, options);
|
|
85109
85142
|
} catch (e) {
|
|
85110
85143
|
console.error(e.message);
|
|
85111
85144
|
process.exit(1);
|
|
@@ -85626,18 +85659,17 @@ function parseWhereClause(whereClause) {
|
|
|
85626
85659
|
if (valueStr === undefined || column === undefined) {
|
|
85627
85660
|
throw new Error(`Cannot parse WHERE clause: "${part}". Use format "column=value" or "col1=val1 AND col2=val2"`);
|
|
85628
85661
|
}
|
|
85629
|
-
|
|
85630
|
-
|
|
85631
|
-
|
|
85632
|
-
|
|
85633
|
-
|
|
85634
|
-
value = Number(value);
|
|
85662
|
+
const trimmed = valueStr.trim();
|
|
85663
|
+
const stripped = trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
|
|
85664
|
+
let value = stripped;
|
|
85665
|
+
if (stripped !== "" && !isNaN(Number(stripped))) {
|
|
85666
|
+
value = Number(stripped);
|
|
85635
85667
|
}
|
|
85636
|
-
if (
|
|
85668
|
+
if (stripped === "true")
|
|
85637
85669
|
value = true;
|
|
85638
|
-
if (
|
|
85670
|
+
else if (stripped === "false")
|
|
85639
85671
|
value = false;
|
|
85640
|
-
if (
|
|
85672
|
+
else if (stripped === "null")
|
|
85641
85673
|
value = null;
|
|
85642
85674
|
conditions[column] = value;
|
|
85643
85675
|
}
|
|
@@ -85845,19 +85877,17 @@ function parseWhereClause2(whereClause) {
|
|
|
85845
85877
|
throw new Error(`Cannot parse WHERE clause: "${part}". Use format "column=value" or "col1=val1 AND col2=val2"`);
|
|
85846
85878
|
}
|
|
85847
85879
|
const column = match[1];
|
|
85848
|
-
const
|
|
85849
|
-
|
|
85850
|
-
|
|
85851
|
-
|
|
85852
|
-
|
|
85853
|
-
if (!isNaN(value) && value !== "") {
|
|
85854
|
-
value = Number(value);
|
|
85880
|
+
const trimmed = match[2].trim();
|
|
85881
|
+
const stripped = trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
|
|
85882
|
+
let value = stripped;
|
|
85883
|
+
if (stripped !== "" && !isNaN(Number(stripped))) {
|
|
85884
|
+
value = Number(stripped);
|
|
85855
85885
|
}
|
|
85856
|
-
if (
|
|
85886
|
+
if (stripped === "true")
|
|
85857
85887
|
value = true;
|
|
85858
|
-
if (
|
|
85888
|
+
else if (stripped === "false")
|
|
85859
85889
|
value = false;
|
|
85860
|
-
if (
|
|
85890
|
+
else if (stripped === "null")
|
|
85861
85891
|
value = null;
|
|
85862
85892
|
conditions[column] = value;
|
|
85863
85893
|
}
|
|
@@ -86137,9 +86167,7 @@ async function redisExportBranch(command, options, config) {
|
|
|
86137
86167
|
columnNames
|
|
86138
86168
|
};
|
|
86139
86169
|
const formatter = new QueryResultFormatter;
|
|
86140
|
-
const formatted = formatter.format(queryResult, {
|
|
86141
|
-
format: options.format
|
|
86142
|
-
});
|
|
86170
|
+
const formatted = formatter.format(queryResult, { format: options.format });
|
|
86143
86171
|
if (options.output) {
|
|
86144
86172
|
const file = Bun.file(options.output);
|
|
86145
86173
|
const exists = await file.exists();
|
|
@@ -86277,20 +86305,11 @@ function escapeCsvField(value) {
|
|
|
86277
86305
|
|
|
86278
86306
|
// src/commands/skill.ts
|
|
86279
86307
|
var {$ } = globalThis.Bun;
|
|
86280
|
-
import * as
|
|
86308
|
+
import * as path2 from "path";
|
|
86281
86309
|
import { homedir as homedir2 } from "os";
|
|
86282
|
-
|
|
86283
|
-
|
|
86284
|
-
|
|
86285
|
-
if (Bun.file(path.join(dir, "package.json")).size > 0) {
|
|
86286
|
-
return dir;
|
|
86287
|
-
}
|
|
86288
|
-
dir = path.dirname(dir);
|
|
86289
|
-
}
|
|
86290
|
-
return path.resolve(import.meta.dir, "../..");
|
|
86291
|
-
}
|
|
86292
|
-
var SKILL_SOURCE_PATH = path.join(findPackageRoot(), "assets", "SKILL.md");
|
|
86293
|
-
var REFERENCE_SOURCE_PATH = path.join(findPackageRoot(), "assets", "reference.md");
|
|
86310
|
+
init_package_root();
|
|
86311
|
+
var SKILL_SOURCE_PATH = packageAssetPath("SKILL.md");
|
|
86312
|
+
var REFERENCE_SOURCE_PATH = packageAssetPath("reference.md");
|
|
86294
86313
|
var SUPPORTED_PLATFORMS = ["claude", "gemini", "copilot", "cursor"];
|
|
86295
86314
|
async function skillCommand(_program, options) {
|
|
86296
86315
|
try {
|
|
@@ -86349,28 +86368,28 @@ function getInstallPath(platform) {
|
|
|
86349
86368
|
const platformLower = platform.toLowerCase();
|
|
86350
86369
|
switch (platformLower) {
|
|
86351
86370
|
case "claude":
|
|
86352
|
-
return
|
|
86371
|
+
return path2.join(home, ".claude", "skills", "dbcli", "SKILL.md");
|
|
86353
86372
|
case "gemini":
|
|
86354
|
-
return
|
|
86373
|
+
return path2.join(home, ".gemini", "skills", "dbcli", "SKILL.md");
|
|
86355
86374
|
case "copilot":
|
|
86356
|
-
return
|
|
86375
|
+
return path2.join(process.cwd(), ".github", "skills", "dbcli", "SKILL.md");
|
|
86357
86376
|
case "cursor":
|
|
86358
|
-
return
|
|
86377
|
+
return path2.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
|
|
86359
86378
|
default:
|
|
86360
86379
|
throw new Error(`Unknown platform: ${platform}. Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`);
|
|
86361
86380
|
}
|
|
86362
86381
|
}
|
|
86363
86382
|
async function writeSkillInstall(platform, installPath, skillMarkdown, referenceMarkdown) {
|
|
86364
86383
|
const platformLower = platform.toLowerCase();
|
|
86365
|
-
await ensureDir(
|
|
86384
|
+
await ensureDir(path2.dirname(installPath));
|
|
86366
86385
|
await Bun.file(installPath).write(skillMarkdown);
|
|
86367
86386
|
if (platformLower === "cursor") {
|
|
86368
|
-
const refPath2 =
|
|
86369
|
-
await ensureDir(
|
|
86387
|
+
const refPath2 = path2.join(process.cwd(), ".cursor", "skills", "dbcli", "reference.md");
|
|
86388
|
+
await ensureDir(path2.dirname(refPath2));
|
|
86370
86389
|
await Bun.file(refPath2).write(referenceMarkdown);
|
|
86371
86390
|
return { referencePath: refPath2 };
|
|
86372
86391
|
}
|
|
86373
|
-
const refPath =
|
|
86392
|
+
const refPath = path2.join(path2.dirname(installPath), "reference.md");
|
|
86374
86393
|
await Bun.file(refPath).write(referenceMarkdown);
|
|
86375
86394
|
return { referencePath: refPath };
|
|
86376
86395
|
}
|
|
@@ -86410,15 +86429,16 @@ class AgentTaskError extends Error {
|
|
|
86410
86429
|
}
|
|
86411
86430
|
}
|
|
86412
86431
|
// src/core/agent-tasks/task-paths.ts
|
|
86413
|
-
|
|
86432
|
+
init_package_root();
|
|
86433
|
+
import { join as join15 } from "path";
|
|
86414
86434
|
function resolveBuiltinDir2() {
|
|
86415
|
-
return
|
|
86435
|
+
return packageAssetPath("tasks");
|
|
86416
86436
|
}
|
|
86417
86437
|
function resolveAgentTaskDirs(workspaceRoot) {
|
|
86418
86438
|
return {
|
|
86419
86439
|
builtinDir: resolveBuiltinDir2(),
|
|
86420
|
-
sharedDir:
|
|
86421
|
-
localDir:
|
|
86440
|
+
sharedDir: join15(workspaceRoot, ".dbcli-shared", "tasks"),
|
|
86441
|
+
localDir: join15(workspaceRoot, ".dbcli", "tasks")
|
|
86422
86442
|
};
|
|
86423
86443
|
}
|
|
86424
86444
|
// src/core/agent-tasks/parser.ts
|
|
@@ -86556,7 +86576,7 @@ function parseSteps(value, input) {
|
|
|
86556
86576
|
}
|
|
86557
86577
|
// src/core/agent-tasks/loader.ts
|
|
86558
86578
|
import { readdir as readdir2 } from "fs/promises";
|
|
86559
|
-
import { join as
|
|
86579
|
+
import { join as join16, relative as relative2, sep as sep2 } from "path";
|
|
86560
86580
|
async function loadAgentTasks(opts, flags) {
|
|
86561
86581
|
const errors3 = [];
|
|
86562
86582
|
const builtin = await walkAndParse2(opts.builtinDir, "builtin", errors3);
|
|
@@ -86613,7 +86633,7 @@ async function safeCollectMd(root) {
|
|
|
86613
86633
|
return;
|
|
86614
86634
|
}
|
|
86615
86635
|
for (const e of entries) {
|
|
86616
|
-
const full =
|
|
86636
|
+
const full = join16(dir, e.name);
|
|
86617
86637
|
if (e.isDirectory())
|
|
86618
86638
|
await walk(full);
|
|
86619
86639
|
else
|
|
@@ -87561,7 +87581,7 @@ var statusCommand = new Command("status").description("Show current configuratio
|
|
|
87561
87581
|
// src/commands/doctor.ts
|
|
87562
87582
|
init_validation();
|
|
87563
87583
|
init_schema_path();
|
|
87564
|
-
import { join as
|
|
87584
|
+
import { join as join17 } from "path";
|
|
87565
87585
|
import { resolveSrv as resolveSrv2 } from "dns/promises";
|
|
87566
87586
|
var ALLOWED_FORMATS8 = ["text", "json"];
|
|
87567
87587
|
var SENSITIVE_PATTERNS = [
|
|
@@ -87635,7 +87655,7 @@ var runDoctorChecks = {
|
|
|
87635
87655
|
}
|
|
87636
87656
|
},
|
|
87637
87657
|
async checkConfigExists(configPath, existsFn) {
|
|
87638
|
-
const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(
|
|
87658
|
+
const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join17(configPath, "config.json")).exists();
|
|
87639
87659
|
return {
|
|
87640
87660
|
group: "Configuration",
|
|
87641
87661
|
label: "Config exists",
|
|
@@ -87792,7 +87812,7 @@ var runDoctorChecks = {
|
|
|
87792
87812
|
async checkV2Config(configPath) {
|
|
87793
87813
|
const results = [];
|
|
87794
87814
|
const storagePath = await resolveConfigStoragePath(configPath);
|
|
87795
|
-
const configFile = Bun.file(
|
|
87815
|
+
const configFile = Bun.file(join17(storagePath, "config.json"));
|
|
87796
87816
|
if (!await configFile.exists())
|
|
87797
87817
|
return results;
|
|
87798
87818
|
let raw;
|
|
@@ -87832,7 +87852,7 @@ var runDoctorChecks = {
|
|
|
87832
87852
|
}
|
|
87833
87853
|
for (const [name, conn] of Object.entries(config.connections)) {
|
|
87834
87854
|
if (conn.envFile) {
|
|
87835
|
-
const envPath =
|
|
87855
|
+
const envPath = join17(storagePath, conn.envFile);
|
|
87836
87856
|
const exists = await Bun.file(envPath).exists();
|
|
87837
87857
|
results.push({
|
|
87838
87858
|
group: "Configuration",
|
|
@@ -88051,7 +88071,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
|
|
|
88051
88071
|
}
|
|
88052
88072
|
try {
|
|
88053
88073
|
const schemaConnName = await getSchemaIsolationConnectionName(configPath);
|
|
88054
|
-
const indexPath =
|
|
88074
|
+
const indexPath = join17(resolveSchemaPath(storagePath, schemaConnName), "index.json");
|
|
88055
88075
|
const indexFile = Bun.file(indexPath);
|
|
88056
88076
|
let indexParsed = null;
|
|
88057
88077
|
if (await indexFile.exists()) {
|
|
@@ -88093,7 +88113,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
|
|
|
88093
88113
|
});
|
|
88094
88114
|
|
|
88095
88115
|
// src/commands/completion.ts
|
|
88096
|
-
import { join as
|
|
88116
|
+
import { join as join18 } from "path";
|
|
88097
88117
|
import { homedir as homedir3 } from "os";
|
|
88098
88118
|
function extractCommands(program2) {
|
|
88099
88119
|
return program2.commands.map((cmd) => ({
|
|
@@ -88195,11 +88215,11 @@ function getInstallPath2(shell) {
|
|
|
88195
88215
|
const home = homedir3();
|
|
88196
88216
|
switch (shell) {
|
|
88197
88217
|
case "bash":
|
|
88198
|
-
return
|
|
88218
|
+
return join18(home, ".bashrc");
|
|
88199
88219
|
case "zsh":
|
|
88200
|
-
return
|
|
88220
|
+
return join18(home, ".zshrc");
|
|
88201
88221
|
case "fish":
|
|
88202
|
-
return
|
|
88222
|
+
return join18(home, ".config", "fish", "completions", "dbcli.fish");
|
|
88203
88223
|
default:
|
|
88204
88224
|
throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
88205
88225
|
}
|
|
@@ -88219,7 +88239,7 @@ var MARKER_END = "# <<< dbcli completion <<<";
|
|
|
88219
88239
|
async function installCompletion(shell, script) {
|
|
88220
88240
|
const targetPath = getInstallPath2(shell);
|
|
88221
88241
|
if (shell === "fish") {
|
|
88222
|
-
const dir =
|
|
88242
|
+
const dir = join18(homedir3(), ".config", "fish", "completions");
|
|
88223
88243
|
await Bun.$`mkdir -p ${dir}`.quiet();
|
|
88224
88244
|
await Bun.file(targetPath).write(script);
|
|
88225
88245
|
console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
|
|
@@ -88446,7 +88466,7 @@ ${t("upgrade.failed")}`));
|
|
|
88446
88466
|
|
|
88447
88467
|
// src/commands/shell.ts
|
|
88448
88468
|
import { createInterface as createInterface2 } from "readline";
|
|
88449
|
-
import { join as
|
|
88469
|
+
import { join as join19 } from "path";
|
|
88450
88470
|
import { homedir as homedir4 } from "os";
|
|
88451
88471
|
|
|
88452
88472
|
// src/core/repl/types.ts
|
|
@@ -88791,7 +88811,7 @@ function isKnownCommand(name) {
|
|
|
88791
88811
|
|
|
88792
88812
|
// src/core/repl/history-manager.ts
|
|
88793
88813
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
88794
|
-
import { dirname as
|
|
88814
|
+
import { dirname as dirname6 } from "path";
|
|
88795
88815
|
var DEFAULT_MAX = 1000;
|
|
88796
88816
|
|
|
88797
88817
|
class HistoryManager {
|
|
@@ -88819,7 +88839,7 @@ class HistoryManager {
|
|
|
88819
88839
|
return this.entries;
|
|
88820
88840
|
}
|
|
88821
88841
|
async save() {
|
|
88822
|
-
const dir =
|
|
88842
|
+
const dir = dirname6(this.filePath);
|
|
88823
88843
|
if (!existsSync(dir)) {
|
|
88824
88844
|
mkdirSync(dir, { recursive: true });
|
|
88825
88845
|
}
|
|
@@ -89169,7 +89189,7 @@ class MongoShellAdapter {
|
|
|
89169
89189
|
}
|
|
89170
89190
|
|
|
89171
89191
|
// src/commands/shell.ts
|
|
89172
|
-
var HISTORY_PATH =
|
|
89192
|
+
var HISTORY_PATH = join19(homedir4(), ".dbcli_history");
|
|
89173
89193
|
var MONGO_COMPLETION_EAGER_THRESHOLD = 20;
|
|
89174
89194
|
async function populateMongoColumns(mongoAdapter, collectionNames, threshold = MONGO_COMPLETION_EAGER_THRESHOLD) {
|
|
89175
89195
|
const columnsByTable = {};
|
|
@@ -90140,7 +90160,7 @@ addExecOpts(migrateCommand.command("drop-enum <name>").description(t("migrate.dr
|
|
|
90140
90160
|
});
|
|
90141
90161
|
|
|
90142
90162
|
// src/commands/use.ts
|
|
90143
|
-
import { join as
|
|
90163
|
+
import { join as join20 } from "path";
|
|
90144
90164
|
async function switchDefault(configPath, name, config) {
|
|
90145
90165
|
if (!config.connections[name]) {
|
|
90146
90166
|
const available = Object.keys(config.connections).join(", ");
|
|
@@ -90163,7 +90183,7 @@ function listConnectionsForDisplay(config) {
|
|
|
90163
90183
|
}
|
|
90164
90184
|
async function ensureV2Config(configPath) {
|
|
90165
90185
|
const storagePath = await resolveConfigStoragePath(configPath);
|
|
90166
|
-
const configFile = Bun.file(
|
|
90186
|
+
const configFile = Bun.file(join20(storagePath, "config.json"));
|
|
90167
90187
|
const legacyFile = Bun.file(configPath);
|
|
90168
90188
|
if (!await configFile.exists() && !await legacyFile.exists()) {
|
|
90169
90189
|
throw new ConfigError(t("init.config_not_found"));
|
|
@@ -90225,7 +90245,7 @@ var useCommand = new Command("use").description("Switch or display the default d
|
|
|
90225
90245
|
});
|
|
90226
90246
|
|
|
90227
90247
|
// src/cli.ts
|
|
90228
|
-
import { join as
|
|
90248
|
+
import { join as join21 } from "path";
|
|
90229
90249
|
var _bgVersionCheckResult;
|
|
90230
90250
|
function shouldSkipBackgroundChecks() {
|
|
90231
90251
|
return process.env.DBCLI_NO_UPDATE_CHECK === "1" || process.env.DBCLI_NO_UPDATE_CHECK === "true" || false;
|
|
@@ -90254,7 +90274,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
90254
90274
|
try {
|
|
90255
90275
|
let cache = null;
|
|
90256
90276
|
try {
|
|
90257
|
-
const cacheFile = Bun.file(
|
|
90277
|
+
const cacheFile = Bun.file(join21(configPath, "version-check.json"));
|
|
90258
90278
|
if (await cacheFile.exists()) {
|
|
90259
90279
|
cache = await cacheFile.json();
|
|
90260
90280
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carllee1983/dbcli",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.1",
|
|
4
4
|
"description": "Database CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration/adapters; docker compose -f docker-compose.test.yml down",
|
|
52
52
|
"typecheck": "tsc --noEmit --pretty false",
|
|
53
53
|
"test:perf": "bun test ./tests/perf/*.bench.ts",
|
|
54
|
-
"lint": "eslint src tests --ext .ts",
|
|
55
|
-
"lint:fix": "eslint src tests --ext .ts --fix",
|
|
54
|
+
"lint": "eslint src tests --ext .ts --max-warnings=0",
|
|
55
|
+
"lint:fix": "eslint src tests --ext .ts --fix --max-warnings=0",
|
|
56
56
|
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\""
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|