@carllee1983/dbcli 0.3.2-beta → 0.5.0-beta
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 +25 -0
- package/README.md +66 -0
- package/README.zh-TW.md +66 -4
- package/assets/SKILL.md +376 -0
- package/dist/cli.mjs +1005 -189
- package/package.json +3 -1
package/dist/cli.mjs
CHANGED
|
@@ -2064,6 +2064,76 @@ var require_commander = __commonJS((exports) => {
|
|
|
2064
2064
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2065
2065
|
});
|
|
2066
2066
|
|
|
2067
|
+
// node_modules/picocolors/picocolors.js
|
|
2068
|
+
var require_picocolors = __commonJS((exports, module) => {
|
|
2069
|
+
var p = process || {};
|
|
2070
|
+
var argv = p.argv || [];
|
|
2071
|
+
var env = p.env || {};
|
|
2072
|
+
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
2073
|
+
var formatter = (open, close, replace = open) => (input) => {
|
|
2074
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
2075
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
2076
|
+
};
|
|
2077
|
+
var replaceClose = (string, close, replace, index) => {
|
|
2078
|
+
let result = "", cursor = 0;
|
|
2079
|
+
do {
|
|
2080
|
+
result += string.substring(cursor, index) + replace;
|
|
2081
|
+
cursor = index + close.length;
|
|
2082
|
+
index = string.indexOf(close, cursor);
|
|
2083
|
+
} while (~index);
|
|
2084
|
+
return result + string.substring(cursor);
|
|
2085
|
+
};
|
|
2086
|
+
var createColors = (enabled = isColorSupported) => {
|
|
2087
|
+
let f = enabled ? formatter : () => String;
|
|
2088
|
+
return {
|
|
2089
|
+
isColorSupported: enabled,
|
|
2090
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
2091
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
2092
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
2093
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
2094
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
2095
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
2096
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
2097
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
2098
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
2099
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
2100
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
2101
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
2102
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
2103
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
2104
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
2105
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
2106
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
2107
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
2108
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
2109
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
2110
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
2111
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
2112
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
2113
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
2114
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
2115
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
2116
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
2117
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
2118
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
2119
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
2120
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
2121
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
2122
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
2123
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
2124
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
2125
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
2126
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
2127
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
2128
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
2129
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
2130
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
2131
|
+
};
|
|
2132
|
+
};
|
|
2133
|
+
module.exports = createColors();
|
|
2134
|
+
module.exports.createColors = createColors;
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2067
2137
|
// node_modules/@inquirer/core/dist/esm/lib/key.mjs
|
|
2068
2138
|
var isUpKey = (key) => key.name === "up" || key.name === "k" || key.ctrl && key.name === "p", isDownKey = (key) => key.name === "down" || key.name === "j" || key.ctrl && key.name === "n", isSpaceKey = (key) => key.name === "space", isBackspaceKey = (key) => key.name === "backspace", isNumberKey = (key) => "123456789".includes(key.name), isEnterKey = (key) => key.name === "enter" || key.name === "return";
|
|
2069
2139
|
|
|
@@ -2267,49 +2337,49 @@ var require_yoctocolors_cjs = __commonJS((exports, module) => {
|
|
|
2267
2337
|
return result;
|
|
2268
2338
|
};
|
|
2269
2339
|
};
|
|
2270
|
-
var
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
module.exports =
|
|
2340
|
+
var colors2 = {};
|
|
2341
|
+
colors2.reset = format(0, 0);
|
|
2342
|
+
colors2.bold = format(1, 22);
|
|
2343
|
+
colors2.dim = format(2, 22);
|
|
2344
|
+
colors2.italic = format(3, 23);
|
|
2345
|
+
colors2.underline = format(4, 24);
|
|
2346
|
+
colors2.overline = format(53, 55);
|
|
2347
|
+
colors2.inverse = format(7, 27);
|
|
2348
|
+
colors2.hidden = format(8, 28);
|
|
2349
|
+
colors2.strikethrough = format(9, 29);
|
|
2350
|
+
colors2.black = format(30, 39);
|
|
2351
|
+
colors2.red = format(31, 39);
|
|
2352
|
+
colors2.green = format(32, 39);
|
|
2353
|
+
colors2.yellow = format(33, 39);
|
|
2354
|
+
colors2.blue = format(34, 39);
|
|
2355
|
+
colors2.magenta = format(35, 39);
|
|
2356
|
+
colors2.cyan = format(36, 39);
|
|
2357
|
+
colors2.white = format(37, 39);
|
|
2358
|
+
colors2.gray = format(90, 39);
|
|
2359
|
+
colors2.bgBlack = format(40, 49);
|
|
2360
|
+
colors2.bgRed = format(41, 49);
|
|
2361
|
+
colors2.bgGreen = format(42, 49);
|
|
2362
|
+
colors2.bgYellow = format(43, 49);
|
|
2363
|
+
colors2.bgBlue = format(44, 49);
|
|
2364
|
+
colors2.bgMagenta = format(45, 49);
|
|
2365
|
+
colors2.bgCyan = format(46, 49);
|
|
2366
|
+
colors2.bgWhite = format(47, 49);
|
|
2367
|
+
colors2.bgGray = format(100, 49);
|
|
2368
|
+
colors2.redBright = format(91, 39);
|
|
2369
|
+
colors2.greenBright = format(92, 39);
|
|
2370
|
+
colors2.yellowBright = format(93, 39);
|
|
2371
|
+
colors2.blueBright = format(94, 39);
|
|
2372
|
+
colors2.magentaBright = format(95, 39);
|
|
2373
|
+
colors2.cyanBright = format(96, 39);
|
|
2374
|
+
colors2.whiteBright = format(97, 39);
|
|
2375
|
+
colors2.bgRedBright = format(101, 49);
|
|
2376
|
+
colors2.bgGreenBright = format(102, 49);
|
|
2377
|
+
colors2.bgYellowBright = format(103, 49);
|
|
2378
|
+
colors2.bgBlueBright = format(104, 49);
|
|
2379
|
+
colors2.bgMagentaBright = format(105, 49);
|
|
2380
|
+
colors2.bgCyanBright = format(106, 49);
|
|
2381
|
+
colors2.bgWhiteBright = format(107, 49);
|
|
2382
|
+
module.exports = colors2;
|
|
2313
2383
|
});
|
|
2314
2384
|
|
|
2315
2385
|
// node_modules/@inquirer/figures/dist/esm/index.js
|
|
@@ -41827,17 +41897,17 @@ var require_zalgo = __commonJS((exports, module) => {
|
|
|
41827
41897
|
|
|
41828
41898
|
// node_modules/@colors/colors/lib/maps/america.js
|
|
41829
41899
|
var require_america = __commonJS((exports, module) => {
|
|
41830
|
-
module["exports"] = function(
|
|
41900
|
+
module["exports"] = function(colors9) {
|
|
41831
41901
|
return function(letter, i, exploded) {
|
|
41832
41902
|
if (letter === " ")
|
|
41833
41903
|
return letter;
|
|
41834
41904
|
switch (i % 3) {
|
|
41835
41905
|
case 0:
|
|
41836
|
-
return
|
|
41906
|
+
return colors9.red(letter);
|
|
41837
41907
|
case 1:
|
|
41838
|
-
return
|
|
41908
|
+
return colors9.white(letter);
|
|
41839
41909
|
case 2:
|
|
41840
|
-
return
|
|
41910
|
+
return colors9.blue(letter);
|
|
41841
41911
|
}
|
|
41842
41912
|
};
|
|
41843
41913
|
};
|
|
@@ -41845,22 +41915,22 @@ var require_america = __commonJS((exports, module) => {
|
|
|
41845
41915
|
|
|
41846
41916
|
// node_modules/@colors/colors/lib/maps/zebra.js
|
|
41847
41917
|
var require_zebra = __commonJS((exports, module) => {
|
|
41848
|
-
module["exports"] = function(
|
|
41918
|
+
module["exports"] = function(colors9) {
|
|
41849
41919
|
return function(letter, i, exploded) {
|
|
41850
|
-
return i % 2 === 0 ? letter :
|
|
41920
|
+
return i % 2 === 0 ? letter : colors9.inverse(letter);
|
|
41851
41921
|
};
|
|
41852
41922
|
};
|
|
41853
41923
|
});
|
|
41854
41924
|
|
|
41855
41925
|
// node_modules/@colors/colors/lib/maps/rainbow.js
|
|
41856
41926
|
var require_rainbow = __commonJS((exports, module) => {
|
|
41857
|
-
module["exports"] = function(
|
|
41927
|
+
module["exports"] = function(colors9) {
|
|
41858
41928
|
var rainbowColors = ["red", "yellow", "green", "blue", "magenta"];
|
|
41859
41929
|
return function(letter, i, exploded) {
|
|
41860
41930
|
if (letter === " ") {
|
|
41861
41931
|
return letter;
|
|
41862
41932
|
} else {
|
|
41863
|
-
return
|
|
41933
|
+
return colors9[rainbowColors[i++ % rainbowColors.length]](letter);
|
|
41864
41934
|
}
|
|
41865
41935
|
};
|
|
41866
41936
|
};
|
|
@@ -41868,7 +41938,7 @@ var require_rainbow = __commonJS((exports, module) => {
|
|
|
41868
41938
|
|
|
41869
41939
|
// node_modules/@colors/colors/lib/maps/random.js
|
|
41870
41940
|
var require_random = __commonJS((exports, module) => {
|
|
41871
|
-
module["exports"] = function(
|
|
41941
|
+
module["exports"] = function(colors9) {
|
|
41872
41942
|
var available = [
|
|
41873
41943
|
"underline",
|
|
41874
41944
|
"inverse",
|
|
@@ -41889,40 +41959,40 @@ var require_random = __commonJS((exports, module) => {
|
|
|
41889
41959
|
"brightMagenta"
|
|
41890
41960
|
];
|
|
41891
41961
|
return function(letter, i, exploded) {
|
|
41892
|
-
return letter === " " ? letter :
|
|
41962
|
+
return letter === " " ? letter : colors9[available[Math.round(Math.random() * (available.length - 2))]](letter);
|
|
41893
41963
|
};
|
|
41894
41964
|
};
|
|
41895
41965
|
});
|
|
41896
41966
|
|
|
41897
41967
|
// node_modules/@colors/colors/lib/colors.js
|
|
41898
41968
|
var require_colors = __commonJS((exports, module) => {
|
|
41899
|
-
var
|
|
41900
|
-
module["exports"] =
|
|
41901
|
-
|
|
41969
|
+
var colors9 = {};
|
|
41970
|
+
module["exports"] = colors9;
|
|
41971
|
+
colors9.themes = {};
|
|
41902
41972
|
var util3 = __require("util");
|
|
41903
|
-
var ansiStyles =
|
|
41973
|
+
var ansiStyles = colors9.styles = require_styles();
|
|
41904
41974
|
var defineProps = Object.defineProperties;
|
|
41905
41975
|
var newLineRegex = new RegExp(/[\r\n]+/g);
|
|
41906
|
-
|
|
41907
|
-
if (typeof
|
|
41908
|
-
|
|
41976
|
+
colors9.supportsColor = require_supports_colors().supportsColor;
|
|
41977
|
+
if (typeof colors9.enabled === "undefined") {
|
|
41978
|
+
colors9.enabled = colors9.supportsColor() !== false;
|
|
41909
41979
|
}
|
|
41910
|
-
|
|
41911
|
-
|
|
41980
|
+
colors9.enable = function() {
|
|
41981
|
+
colors9.enabled = true;
|
|
41912
41982
|
};
|
|
41913
|
-
|
|
41914
|
-
|
|
41983
|
+
colors9.disable = function() {
|
|
41984
|
+
colors9.enabled = false;
|
|
41915
41985
|
};
|
|
41916
|
-
|
|
41986
|
+
colors9.stripColors = colors9.strip = function(str) {
|
|
41917
41987
|
return ("" + str).replace(/\x1B\[\d+m/g, "");
|
|
41918
41988
|
};
|
|
41919
|
-
var stylize =
|
|
41920
|
-
if (!
|
|
41989
|
+
var stylize = colors9.stylize = function stylize2(str, style) {
|
|
41990
|
+
if (!colors9.enabled) {
|
|
41921
41991
|
return str + "";
|
|
41922
41992
|
}
|
|
41923
41993
|
var styleMap = ansiStyles[style];
|
|
41924
|
-
if (!styleMap && style in
|
|
41925
|
-
return
|
|
41994
|
+
if (!styleMap && style in colors9) {
|
|
41995
|
+
return colors9[style](str);
|
|
41926
41996
|
}
|
|
41927
41997
|
return styleMap.open + str + styleMap.close;
|
|
41928
41998
|
};
|
|
@@ -41954,7 +42024,7 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
41954
42024
|
});
|
|
41955
42025
|
return ret;
|
|
41956
42026
|
}();
|
|
41957
|
-
var proto = defineProps(function
|
|
42027
|
+
var proto = defineProps(function colors10() {}, styles);
|
|
41958
42028
|
function applyStyle() {
|
|
41959
42029
|
var args = Array.prototype.slice.call(arguments);
|
|
41960
42030
|
var str = args.map(function(arg) {
|
|
@@ -41964,7 +42034,7 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
41964
42034
|
return util3.inspect(arg);
|
|
41965
42035
|
}
|
|
41966
42036
|
}).join(" ");
|
|
41967
|
-
if (!
|
|
42037
|
+
if (!colors9.enabled || !str) {
|
|
41968
42038
|
return str;
|
|
41969
42039
|
}
|
|
41970
42040
|
var newLinesPresent = str.indexOf(`
|
|
@@ -41982,22 +42052,22 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
41982
42052
|
}
|
|
41983
42053
|
return str;
|
|
41984
42054
|
}
|
|
41985
|
-
|
|
42055
|
+
colors9.setTheme = function(theme) {
|
|
41986
42056
|
if (typeof theme === "string") {
|
|
41987
42057
|
console.log("colors.setTheme now only accepts an object, not a string. " + "If you are trying to set a theme from a file, it is now your (the " + "caller's) responsibility to require the file. The old syntax " + "looked like colors.setTheme(__dirname + " + "'/../themes/generic-logging.js'); The new syntax looks like " + "colors.setTheme(require(__dirname + " + "'/../themes/generic-logging.js'));");
|
|
41988
42058
|
return;
|
|
41989
42059
|
}
|
|
41990
42060
|
for (var style in theme) {
|
|
41991
42061
|
(function(style2) {
|
|
41992
|
-
|
|
42062
|
+
colors9[style2] = function(str) {
|
|
41993
42063
|
if (typeof theme[style2] === "object") {
|
|
41994
42064
|
var out = str;
|
|
41995
42065
|
for (var i in theme[style2]) {
|
|
41996
|
-
out =
|
|
42066
|
+
out = colors9[theme[style2][i]](out);
|
|
41997
42067
|
}
|
|
41998
42068
|
return out;
|
|
41999
42069
|
}
|
|
42000
|
-
return
|
|
42070
|
+
return colors9[theme[style2]](str);
|
|
42001
42071
|
};
|
|
42002
42072
|
})(style);
|
|
42003
42073
|
}
|
|
@@ -42018,28 +42088,28 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
42018
42088
|
exploded = exploded.map(map2);
|
|
42019
42089
|
return exploded.join("");
|
|
42020
42090
|
};
|
|
42021
|
-
|
|
42022
|
-
|
|
42023
|
-
|
|
42024
|
-
|
|
42025
|
-
|
|
42026
|
-
|
|
42027
|
-
|
|
42028
|
-
for (map in
|
|
42091
|
+
colors9.trap = require_trap();
|
|
42092
|
+
colors9.zalgo = require_zalgo();
|
|
42093
|
+
colors9.maps = {};
|
|
42094
|
+
colors9.maps.america = require_america()(colors9);
|
|
42095
|
+
colors9.maps.zebra = require_zebra()(colors9);
|
|
42096
|
+
colors9.maps.rainbow = require_rainbow()(colors9);
|
|
42097
|
+
colors9.maps.random = require_random()(colors9);
|
|
42098
|
+
for (map in colors9.maps) {
|
|
42029
42099
|
(function(map2) {
|
|
42030
|
-
|
|
42031
|
-
return sequencer(
|
|
42100
|
+
colors9[map2] = function(str) {
|
|
42101
|
+
return sequencer(colors9.maps[map2], str);
|
|
42032
42102
|
};
|
|
42033
42103
|
})(map);
|
|
42034
42104
|
}
|
|
42035
42105
|
var map;
|
|
42036
|
-
defineProps(
|
|
42106
|
+
defineProps(colors9, init());
|
|
42037
42107
|
});
|
|
42038
42108
|
|
|
42039
42109
|
// node_modules/@colors/colors/safe.js
|
|
42040
42110
|
var require_safe = __commonJS((exports, module) => {
|
|
42041
|
-
var
|
|
42042
|
-
module["exports"] =
|
|
42111
|
+
var colors9 = require_colors();
|
|
42112
|
+
module["exports"] = colors9;
|
|
42043
42113
|
});
|
|
42044
42114
|
|
|
42045
42115
|
// node_modules/cli-table3/src/cell.js
|
|
@@ -42209,11 +42279,11 @@ var require_cell = __commonJS((exports, module) => {
|
|
|
42209
42279
|
wrapWithStyleColors(styleProperty, content) {
|
|
42210
42280
|
if (this[styleProperty] && this[styleProperty].length) {
|
|
42211
42281
|
try {
|
|
42212
|
-
let
|
|
42282
|
+
let colors9 = require_safe();
|
|
42213
42283
|
for (let i = this[styleProperty].length - 1;i >= 0; i--) {
|
|
42214
|
-
|
|
42284
|
+
colors9 = colors9[this[styleProperty][i]];
|
|
42215
42285
|
}
|
|
42216
|
-
return
|
|
42286
|
+
return colors9(content);
|
|
42217
42287
|
} catch (e) {
|
|
42218
42288
|
return content;
|
|
42219
42289
|
}
|
|
@@ -42758,7 +42828,7 @@ var {
|
|
|
42758
42828
|
// package.json
|
|
42759
42829
|
var package_default = {
|
|
42760
42830
|
name: "@carllee1983/dbcli",
|
|
42761
|
-
version: "0.
|
|
42831
|
+
version: "0.5.0-beta",
|
|
42762
42832
|
description: "Database CLI for AI agents",
|
|
42763
42833
|
type: "module",
|
|
42764
42834
|
publishConfig: {
|
|
@@ -42773,6 +42843,7 @@ var package_default = {
|
|
|
42773
42843
|
},
|
|
42774
42844
|
files: [
|
|
42775
42845
|
"dist/",
|
|
42846
|
+
"assets/",
|
|
42776
42847
|
"README.md",
|
|
42777
42848
|
"CHANGELOG.md",
|
|
42778
42849
|
"LICENSE"
|
|
@@ -42795,6 +42866,7 @@ var package_default = {
|
|
|
42795
42866
|
dotenv: "^16.3.1",
|
|
42796
42867
|
mysql2: "^3.20.0",
|
|
42797
42868
|
pg: "^8.20.0",
|
|
42869
|
+
picocolors: "^1.1.1",
|
|
42798
42870
|
zod: "^3.22.4"
|
|
42799
42871
|
},
|
|
42800
42872
|
devDependencies: {
|
|
@@ -42823,7 +42895,7 @@ var messages_default = {
|
|
|
42823
42895
|
prompt_user: "Database user: ",
|
|
42824
42896
|
prompt_password: "Database password: ",
|
|
42825
42897
|
prompt_name: "Database name: ",
|
|
42826
|
-
prompt_permission: "Permission level (query-only, read-write, admin): ",
|
|
42898
|
+
prompt_permission: "Permission level (query-only, read-write, data-admin, admin): ",
|
|
42827
42899
|
env_parse_note: "Note: Unable to parse .env configuration, using interactive prompts",
|
|
42828
42900
|
connection_testing: "Testing database connection...",
|
|
42829
42901
|
connection_success: "\u2713 Database connection successful",
|
|
@@ -42892,7 +42964,7 @@ var messages_default = {
|
|
|
42892
42964
|
description: "Delete data from a table",
|
|
42893
42965
|
prompt_where: "Enter WHERE clause (e.g., id=1): ",
|
|
42894
42966
|
confirm: "Delete rows from {table}? (y/n): ",
|
|
42895
|
-
admin_only: "Delete command requires admin permission"
|
|
42967
|
+
admin_only: "Delete command requires data-admin or admin permission"
|
|
42896
42968
|
},
|
|
42897
42969
|
export: {
|
|
42898
42970
|
description: "Export query results",
|
|
@@ -42932,7 +43004,7 @@ var messages_default2 = {
|
|
|
42932
43004
|
prompt_user: "\u8CC7\u6599\u5EAB\u4F7F\u7528\u8005\uFF1A",
|
|
42933
43005
|
prompt_password: "\u8CC7\u6599\u5EAB\u5BC6\u78BC\uFF1A",
|
|
42934
43006
|
prompt_name: "\u8CC7\u6599\u5EAB\u540D\u7A31\uFF1A",
|
|
42935
|
-
prompt_permission: "\u6B0A\u9650\u7B49\u7D1A\uFF08query-only, read-write, admin\uFF09\uFF1A",
|
|
43007
|
+
prompt_permission: "\u6B0A\u9650\u7B49\u7D1A\uFF08query-only, read-write, data-admin, admin\uFF09\uFF1A",
|
|
42936
43008
|
env_parse_note: "\u63D0\u793A\uFF1A\u7121\u6CD5\u89E3\u6790 .env \u914D\u7F6E\uFF0C\u4F7F\u7528\u4E92\u52D5\u5F0F\u63D0\u793A",
|
|
42937
43009
|
connection_testing: "\u6B63\u5728\u6E2C\u8A66\u8CC7\u6599\u5EAB\u9023\u63A5...",
|
|
42938
43010
|
connection_success: "\u2713 \u8CC7\u6599\u5EAB\u9023\u63A5\u6210\u529F",
|
|
@@ -43001,7 +43073,7 @@ var messages_default2 = {
|
|
|
43001
43073
|
description: "\u522A\u9664\u8868\u683C\u4E2D\u7684\u8CC7\u6599",
|
|
43002
43074
|
prompt_where: "\u8F38\u5165 WHERE \u5B50\u53E5 (\u4F8B\u5982\uFF1Aid=1)\uFF1A",
|
|
43003
43075
|
confirm: "\u522A\u9664 {table} \u4E2D\u7684\u5217\uFF1F (y/n)\uFF1A",
|
|
43004
|
-
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981\
|
|
43076
|
+
admin_only: "\u522A\u9664\u547D\u4EE4\u9700\u8981 data-admin \u6216 admin \u6B0A\u9650"
|
|
43005
43077
|
},
|
|
43006
43078
|
export: {
|
|
43007
43079
|
description: "\u532F\u51FA\u67E5\u8A62\u7D50\u679C",
|
|
@@ -43100,6 +43172,47 @@ var messageLoader = MessageLoader.getInstance();
|
|
|
43100
43172
|
var t = (key) => messageLoader.t(key);
|
|
43101
43173
|
var t_vars = (key, vars) => messageLoader.interpolate(key, vars);
|
|
43102
43174
|
|
|
43175
|
+
// src/utils/colors.ts
|
|
43176
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
43177
|
+
var colors = {
|
|
43178
|
+
success: (text) => import_picocolors.default.green(text),
|
|
43179
|
+
error: (text) => import_picocolors.default.red(text),
|
|
43180
|
+
warn: (text) => import_picocolors.default.yellow(text),
|
|
43181
|
+
info: (text) => import_picocolors.default.blue(text),
|
|
43182
|
+
dim: (text) => import_picocolors.default.dim(text),
|
|
43183
|
+
bold: (text) => import_picocolors.default.bold(text),
|
|
43184
|
+
keyword: (text) => import_picocolors.default.blue(import_picocolors.default.bold(text))
|
|
43185
|
+
};
|
|
43186
|
+
|
|
43187
|
+
// src/utils/logger.ts
|
|
43188
|
+
function formatArgs(args) {
|
|
43189
|
+
return args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
43190
|
+
}
|
|
43191
|
+
function createLogger(level = 1 /* NORMAL */) {
|
|
43192
|
+
const write = (minLevel, prefix, args) => {
|
|
43193
|
+
if (level < minLevel)
|
|
43194
|
+
return;
|
|
43195
|
+
const message = `${prefix} ${formatArgs(args)}
|
|
43196
|
+
`;
|
|
43197
|
+
process.stderr.write(message);
|
|
43198
|
+
};
|
|
43199
|
+
return {
|
|
43200
|
+
error: (...args) => write(0 /* QUIET */, colors.error("[ERROR]"), args),
|
|
43201
|
+
warn: (...args) => write(1 /* NORMAL */, colors.warn("[WARN]"), args),
|
|
43202
|
+
info: (...args) => write(1 /* NORMAL */, colors.info("[INFO]"), args),
|
|
43203
|
+
verbose: (...args) => write(2 /* VERBOSE */, colors.dim("[VERBOSE]"), args),
|
|
43204
|
+
debug: (...args) => write(3 /* DEBUG */, colors.dim("[DEBUG]"), args),
|
|
43205
|
+
level
|
|
43206
|
+
};
|
|
43207
|
+
}
|
|
43208
|
+
var globalLogger = createLogger(1 /* NORMAL */);
|
|
43209
|
+
function setGlobalLogger(logger) {
|
|
43210
|
+
globalLogger = logger;
|
|
43211
|
+
}
|
|
43212
|
+
function getLogger() {
|
|
43213
|
+
return globalLogger;
|
|
43214
|
+
}
|
|
43215
|
+
|
|
43103
43216
|
// src/utils/errors.ts
|
|
43104
43217
|
class EnvParseError extends Error {
|
|
43105
43218
|
constructor(message) {
|
|
@@ -43156,7 +43269,7 @@ function parseConnectionUrl(url) {
|
|
|
43156
43269
|
} else if (protocol === "mariadb") {
|
|
43157
43270
|
system = "mariadb";
|
|
43158
43271
|
} else {
|
|
43159
|
-
throw new Error(
|
|
43272
|
+
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
43160
43273
|
}
|
|
43161
43274
|
const host = parsed.hostname || "localhost";
|
|
43162
43275
|
const port = parsed.port !== "" ? parseInt(parsed.port, 10) : getDefaultsForSystem(system).port || 5432;
|
|
@@ -43165,7 +43278,7 @@ function parseConnectionUrl(url) {
|
|
|
43165
43278
|
const database = parsed.pathname.slice(1);
|
|
43166
43279
|
return { system, host, port, user, password, database };
|
|
43167
43280
|
} catch (error) {
|
|
43168
|
-
throw new EnvParseError(
|
|
43281
|
+
throw new EnvParseError(`Failed to parse DATABASE_URL: ${error instanceof Error ? error.message : String(error)}`);
|
|
43169
43282
|
}
|
|
43170
43283
|
}
|
|
43171
43284
|
function parseEnvDatabase(env) {
|
|
@@ -43177,15 +43290,15 @@ function parseEnvDatabase(env) {
|
|
|
43177
43290
|
const user = env.DB_USER;
|
|
43178
43291
|
const database = env.DB_NAME;
|
|
43179
43292
|
if (!user) {
|
|
43180
|
-
throw new EnvParseError("
|
|
43293
|
+
throw new EnvParseError("DB_USER is required when using component format");
|
|
43181
43294
|
}
|
|
43182
43295
|
if (!database) {
|
|
43183
|
-
throw new EnvParseError("
|
|
43296
|
+
throw new EnvParseError("DB_NAME is required when using component format");
|
|
43184
43297
|
}
|
|
43185
43298
|
const defaults = getDefaultsForSystem(system);
|
|
43186
43299
|
const port = env.DB_PORT ? parseInt(env.DB_PORT, 10) : defaults.port || 5432;
|
|
43187
43300
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
43188
|
-
throw new EnvParseError(`DB_PORT
|
|
43301
|
+
throw new EnvParseError(`DB_PORT must be between 1 and 65535, got: ${env.DB_PORT}`);
|
|
43189
43302
|
}
|
|
43190
43303
|
return {
|
|
43191
43304
|
system,
|
|
@@ -47192,7 +47305,7 @@ var ConnectionConfigSchema = exports_external.object({
|
|
|
47192
47305
|
password: exports_external.union([exports_external.string(), EnvRefSchema]).default(""),
|
|
47193
47306
|
database: StringOrEnvRef
|
|
47194
47307
|
});
|
|
47195
|
-
var PermissionSchema = exports_external.enum(["query-only", "read-write", "admin"]).default("query-only");
|
|
47308
|
+
var PermissionSchema = exports_external.enum(["query-only", "read-write", "data-admin", "admin"]).default("query-only");
|
|
47196
47309
|
var MetadataSchema = exports_external.object({
|
|
47197
47310
|
createdAt: exports_external.string().datetime().optional(),
|
|
47198
47311
|
version: exports_external.string().default("1.0")
|
|
@@ -47237,14 +47350,14 @@ function resolveEnvReferences(config, env, parentKey, strict = false) {
|
|
|
47237
47350
|
if (!strict) {
|
|
47238
47351
|
return config;
|
|
47239
47352
|
}
|
|
47240
|
-
throw new ConfigError(
|
|
47241
|
-
` +
|
|
47242
|
-
` +
|
|
47353
|
+
throw new ConfigError(`Environment variable not defined: ${envKey}
|
|
47354
|
+
` + `Please set ${envKey} in .env or your environment.
|
|
47355
|
+
` + `Hint: check your .env file or run 'export ${envKey}=<value>'`);
|
|
47243
47356
|
}
|
|
47244
47357
|
if (parentKey === "port") {
|
|
47245
47358
|
const portNum = parseInt(value, 10);
|
|
47246
47359
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
47247
|
-
throw new ConfigError(`${envKey}
|
|
47360
|
+
throw new ConfigError(`${envKey} must be a valid port number (1-65535), got: ${value}`);
|
|
47248
47361
|
}
|
|
47249
47362
|
return portNum;
|
|
47250
47363
|
}
|
|
@@ -47307,9 +47420,9 @@ var configModule = {
|
|
|
47307
47420
|
return { ...DEFAULT_CONFIG };
|
|
47308
47421
|
} catch (error) {
|
|
47309
47422
|
if (error instanceof Error && error.message.includes("JSON")) {
|
|
47310
|
-
throw new ConfigError(
|
|
47423
|
+
throw new ConfigError(`Failed to parse .dbcli file: ${error.message}`);
|
|
47311
47424
|
}
|
|
47312
|
-
throw new ConfigError(
|
|
47425
|
+
throw new ConfigError(`Failed to read .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
47313
47426
|
}
|
|
47314
47427
|
},
|
|
47315
47428
|
validate(raw) {
|
|
@@ -47317,7 +47430,7 @@ var configModule = {
|
|
|
47317
47430
|
return DbcliConfigSchema.parse(raw);
|
|
47318
47431
|
} catch (error) {
|
|
47319
47432
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
47320
|
-
throw new ConfigError(
|
|
47433
|
+
throw new ConfigError(`Invalid .dbcli config structure: ${errorMessage}`);
|
|
47321
47434
|
}
|
|
47322
47435
|
},
|
|
47323
47436
|
merge(existing, updates) {
|
|
@@ -47367,8 +47480,7 @@ var configModule = {
|
|
|
47367
47480
|
await Bun.file(configPath).write(configJson);
|
|
47368
47481
|
if (password) {
|
|
47369
47482
|
const envPath = join(path, ".env.local");
|
|
47370
|
-
const envContent = `#
|
|
47371
|
-
# Database Credentials - DO NOT commit to git
|
|
47483
|
+
const envContent = `# Database Credentials - DO NOT commit to git
|
|
47372
47484
|
|
|
47373
47485
|
DBCLI_PASSWORD=${password}
|
|
47374
47486
|
`;
|
|
@@ -47383,7 +47495,7 @@ DBCLI_PASSWORD=${password}
|
|
|
47383
47495
|
if (error instanceof ConfigError) {
|
|
47384
47496
|
throw error;
|
|
47385
47497
|
}
|
|
47386
|
-
throw new ConfigError(
|
|
47498
|
+
throw new ConfigError(`Failed to write .dbcli config: ${error instanceof Error ? error.message : String(error)}`);
|
|
47387
47499
|
}
|
|
47388
47500
|
}
|
|
47389
47501
|
};
|
|
@@ -47498,37 +47610,37 @@ function mapError(error, system, options) {
|
|
|
47498
47610
|
const errMsg = String(err?.message || String(error));
|
|
47499
47611
|
const errCode = String(err?.code || "");
|
|
47500
47612
|
if (errCode === "ECONNREFUSED" || errMsg.includes("refused")) {
|
|
47501
|
-
return new ConnectionError("ECONNREFUSED",
|
|
47502
|
-
|
|
47503
|
-
|
|
47504
|
-
|
|
47613
|
+
return new ConnectionError("ECONNREFUSED", `Cannot connect to ${options.host}:${options.port} \u2014 server is not running or not listening on this port`, [
|
|
47614
|
+
`Check that the ${system} service is running: ${system === "postgresql" ? "systemctl status postgresql" : "systemctl status mysql"}`,
|
|
47615
|
+
`Verify the port is correct: ${system === "postgresql" ? "default 5432" : "default 3306"}`,
|
|
47616
|
+
`Check that ${options.host} is reachable: ping ${options.host} or telnet ${options.host} ${options.port}`
|
|
47505
47617
|
]);
|
|
47506
47618
|
}
|
|
47507
47619
|
if (errCode === "ETIMEDOUT" || errMsg.includes("timeout") || errMsg.includes("timed out")) {
|
|
47508
|
-
return new ConnectionError("ETIMEDOUT",
|
|
47509
|
-
|
|
47510
|
-
|
|
47511
|
-
|
|
47620
|
+
return new ConnectionError("ETIMEDOUT", `Connection timed out (${options.timeout || 5000}ms) \u2014 may be blocked by a firewall or slow network`, [
|
|
47621
|
+
`Check firewall rules: ${system === "postgresql" ? "allow TCP 5432" : "allow TCP 3306"}`,
|
|
47622
|
+
`Increase timeout: edit .dbcli and add "timeout": 15000`,
|
|
47623
|
+
`Verify network connectivity: ping ${options.host} -c 3`
|
|
47512
47624
|
]);
|
|
47513
47625
|
}
|
|
47514
47626
|
if (errMsg.includes("authentication") || errMsg.includes("auth") || errMsg.includes("password") || errMsg.includes("access denied") || errMsg.includes("FATAL")) {
|
|
47515
|
-
return new ConnectionError("AUTH_FAILED",
|
|
47516
|
-
|
|
47517
|
-
|
|
47518
|
-
|
|
47627
|
+
return new ConnectionError("AUTH_FAILED", `Authentication failed \u2014 check your username or password`, [
|
|
47628
|
+
`Verify credentials: ${system === "postgresql" ? `psql -U ${options.user} -h ${options.host}` : `mysql -u ${options.user} -h ${options.host}`}`,
|
|
47629
|
+
`Check pg_hba.conf (PostgreSQL) or user privileges (MySQL)`,
|
|
47630
|
+
`Re-run dbcli init to update credentials`
|
|
47519
47631
|
]);
|
|
47520
47632
|
}
|
|
47521
47633
|
if (errCode === "ENOTFOUND" || errMsg.includes("not found") || errMsg.includes("getaddrinfo")) {
|
|
47522
|
-
return new ConnectionError("ENOTFOUND",
|
|
47523
|
-
|
|
47524
|
-
|
|
47525
|
-
|
|
47634
|
+
return new ConnectionError("ENOTFOUND", `Host not found: ${options.host}`, [
|
|
47635
|
+
`Check the hostname spelling: ${options.host}`,
|
|
47636
|
+
`Verify DNS resolution: nslookup ${options.host}`,
|
|
47637
|
+
`If using localhost, try 127.0.0.1 (IPv4 vs IPv6 issue)`
|
|
47526
47638
|
]);
|
|
47527
47639
|
}
|
|
47528
|
-
return new ConnectionError("UNKNOWN",
|
|
47529
|
-
|
|
47530
|
-
|
|
47531
|
-
|
|
47640
|
+
return new ConnectionError("UNKNOWN", `Connection failed: ${errMsg}`, [
|
|
47641
|
+
`Check connection parameters: host=${options.host}, port=${options.port}, user=${options.user}`,
|
|
47642
|
+
`View server logs: ${system === "postgresql" ? "postgresql.log" : "mysql.log"}`,
|
|
47643
|
+
`Try connecting directly with the ${system === "postgresql" ? "psql" : "mysql"} command-line tool`
|
|
47532
47644
|
]);
|
|
47533
47645
|
}
|
|
47534
47646
|
|
|
@@ -47554,7 +47666,7 @@ class PostgreSQLAdapter {
|
|
|
47554
47666
|
constructor(options) {
|
|
47555
47667
|
this.options = options;
|
|
47556
47668
|
if (options.port < 1 || options.port > 65535) {
|
|
47557
|
-
throw new Error(
|
|
47669
|
+
throw new Error(`Invalid port number: ${options.port}`);
|
|
47558
47670
|
}
|
|
47559
47671
|
}
|
|
47560
47672
|
async connect() {
|
|
@@ -47587,7 +47699,7 @@ class PostgreSQLAdapter {
|
|
|
47587
47699
|
}
|
|
47588
47700
|
async testConnection() {
|
|
47589
47701
|
if (!this.pool) {
|
|
47590
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47702
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47591
47703
|
}
|
|
47592
47704
|
try {
|
|
47593
47705
|
const result = await this.execute("SELECT 1 as count");
|
|
@@ -47598,7 +47710,7 @@ class PostgreSQLAdapter {
|
|
|
47598
47710
|
}
|
|
47599
47711
|
async execute(sql, params) {
|
|
47600
47712
|
if (!this.pool) {
|
|
47601
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47713
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47602
47714
|
}
|
|
47603
47715
|
try {
|
|
47604
47716
|
const result = params ? await this.pool.query(sql, params) : await this.pool.query(sql);
|
|
@@ -47609,7 +47721,7 @@ class PostgreSQLAdapter {
|
|
|
47609
47721
|
}
|
|
47610
47722
|
async listTables() {
|
|
47611
47723
|
if (!this.pool) {
|
|
47612
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47724
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47613
47725
|
}
|
|
47614
47726
|
try {
|
|
47615
47727
|
const query = `
|
|
@@ -47621,7 +47733,9 @@ class PostgreSQLAdapter {
|
|
|
47621
47733
|
WHEN 'v' THEN 'view'
|
|
47622
47734
|
WHEN 'm' THEN 'view'
|
|
47623
47735
|
ELSE 'table'
|
|
47624
|
-
END as table_type
|
|
47736
|
+
END as table_type,
|
|
47737
|
+
(SELECT COUNT(*) FROM information_schema.columns ic
|
|
47738
|
+
WHERE ic.table_schema = 'public' AND ic.table_name = c.relname)::int as column_count
|
|
47625
47739
|
FROM pg_class c
|
|
47626
47740
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
47627
47741
|
WHERE n.nspname = 'public'
|
|
@@ -47632,6 +47746,7 @@ class PostgreSQLAdapter {
|
|
|
47632
47746
|
return results.map((row) => ({
|
|
47633
47747
|
name: row.table_name,
|
|
47634
47748
|
columns: [],
|
|
47749
|
+
columnCount: row.column_count,
|
|
47635
47750
|
rowCount: Math.max(0, row.estimated_rows || 0),
|
|
47636
47751
|
engine: "PostgreSQL",
|
|
47637
47752
|
estimatedRowCount: Math.max(0, row.estimated_rows || 0),
|
|
@@ -47643,7 +47758,7 @@ class PostgreSQLAdapter {
|
|
|
47643
47758
|
}
|
|
47644
47759
|
async getTableSchema(tableName) {
|
|
47645
47760
|
if (!this.pool) {
|
|
47646
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47761
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47647
47762
|
}
|
|
47648
47763
|
try {
|
|
47649
47764
|
const columnQuery = `
|
|
@@ -47801,7 +47916,7 @@ class MySQLAdapter {
|
|
|
47801
47916
|
this.options = options;
|
|
47802
47917
|
this.system = options.system === "mariadb" ? "mariadb" : "mysql";
|
|
47803
47918
|
if (options.port < 1 || options.port > 65535) {
|
|
47804
|
-
throw new Error(
|
|
47919
|
+
throw new Error(`Invalid port number: ${options.port}`);
|
|
47805
47920
|
}
|
|
47806
47921
|
}
|
|
47807
47922
|
async connect() {
|
|
@@ -47828,7 +47943,7 @@ class MySQLAdapter {
|
|
|
47828
47943
|
}
|
|
47829
47944
|
async testConnection() {
|
|
47830
47945
|
if (!this.db) {
|
|
47831
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47946
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47832
47947
|
}
|
|
47833
47948
|
try {
|
|
47834
47949
|
const result = await this.execute("SELECT 1 as count");
|
|
@@ -47839,7 +47954,7 @@ class MySQLAdapter {
|
|
|
47839
47954
|
}
|
|
47840
47955
|
async execute(sql, params) {
|
|
47841
47956
|
if (!this.db) {
|
|
47842
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47957
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47843
47958
|
}
|
|
47844
47959
|
try {
|
|
47845
47960
|
const [rows] = params ? await this.db.execute(sql, params) : await this.db.execute(sql);
|
|
@@ -47850,7 +47965,7 @@ class MySQLAdapter {
|
|
|
47850
47965
|
}
|
|
47851
47966
|
async listTables() {
|
|
47852
47967
|
if (!this.db) {
|
|
47853
|
-
throw new ConnectionError("UNKNOWN", "
|
|
47968
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47854
47969
|
}
|
|
47855
47970
|
try {
|
|
47856
47971
|
const query = `
|
|
@@ -47870,7 +47985,8 @@ class MySQLAdapter {
|
|
|
47870
47985
|
const results = await this.execute(query);
|
|
47871
47986
|
return results.map((row) => ({
|
|
47872
47987
|
name: row.table_name,
|
|
47873
|
-
columns:
|
|
47988
|
+
columns: [],
|
|
47989
|
+
columnCount: row.column_count,
|
|
47874
47990
|
rowCount: row.row_count || 0,
|
|
47875
47991
|
engine: row.engine,
|
|
47876
47992
|
estimatedRowCount: row.row_count || 0,
|
|
@@ -47882,7 +47998,7 @@ class MySQLAdapter {
|
|
|
47882
47998
|
}
|
|
47883
47999
|
async getTableSchema(tableName) {
|
|
47884
48000
|
if (!this.db) {
|
|
47885
|
-
throw new ConnectionError("UNKNOWN", "
|
|
48001
|
+
throw new ConnectionError("UNKNOWN", "Database connection not established", ["Call connect() to establish a connection"]);
|
|
47886
48002
|
}
|
|
47887
48003
|
try {
|
|
47888
48004
|
const columnQuery = `
|
|
@@ -47993,12 +48109,12 @@ class AdapterFactory {
|
|
|
47993
48109
|
case "mariadb":
|
|
47994
48110
|
return new MySQLAdapter(options);
|
|
47995
48111
|
default:
|
|
47996
|
-
throw new Error(
|
|
48112
|
+
throw new Error(`Unsupported database system: ${options.system}`);
|
|
47997
48113
|
}
|
|
47998
48114
|
}
|
|
47999
48115
|
}
|
|
48000
48116
|
// src/commands/init.ts
|
|
48001
|
-
var initCommand = new Command("init").description("Initialize dbcli configuration with .env parsing and interactive prompts").option("--host <host>", "Database host").option("--port <port>", "Database port").option("--user <user>", "Database user").option("--password <password>", "Database password").option("--name <name>", "Database name").option("--system <system>", "Database system (postgresql, mysql, mariadb)").option("--permission <permission>", "Permission level (query-only, read-write, admin)", "query-only").option("--use-env-refs", "Generate config with environment variable references (for .env)", false).option("--env-host <var>", "Environment variable name for database host (when using --use-env-refs)").option("--env-port <var>", "Environment variable name for database port (when using --use-env-refs)").option("--env-user <var>", "Environment variable name for database user (when using --use-env-refs)").option("--env-password <var>", "Environment variable name for database password (when using --use-env-refs)").option("--env-database <var>", "Environment variable name for database name (when using --use-env-refs)").option("--skip-test", "Skip database connection test").option("--no-interactive", "Non-interactive mode (requires all values via flags)").option("--force", "Skip overwrite confirmation if .dbcli exists").action(async (options) => {
|
|
48117
|
+
var initCommand = new Command("init").description("Initialize dbcli configuration with .env parsing and interactive prompts").option("--host <host>", "Database host").option("--port <port>", "Database port").option("--user <user>", "Database user").option("--password <password>", "Database password").option("--name <name>", "Database name").option("--system <system>", "Database system (postgresql, mysql, mariadb)").option("--permission <permission>", "Permission level (query-only, read-write, data-admin, admin)", "query-only").option("--use-env-refs", "Generate config with environment variable references (for .env)", false).option("--env-host <var>", "Environment variable name for database host (when using --use-env-refs)").option("--env-port <var>", "Environment variable name for database port (when using --use-env-refs)").option("--env-user <var>", "Environment variable name for database user (when using --use-env-refs)").option("--env-password <var>", "Environment variable name for database password (when using --use-env-refs)").option("--env-database <var>", "Environment variable name for database name (when using --use-env-refs)").option("--skip-test", "Skip database connection test").option("--no-interactive", "Non-interactive mode (requires all values via flags)").option("--force", "Skip overwrite confirmation if .dbcli exists").action(async (options) => {
|
|
48002
48118
|
try {
|
|
48003
48119
|
await initCommandHandler(options);
|
|
48004
48120
|
} catch (error) {
|
|
@@ -48061,7 +48177,7 @@ async function initCommandHandler(options) {
|
|
|
48061
48177
|
"admin"
|
|
48062
48178
|
]);
|
|
48063
48179
|
}
|
|
48064
|
-
if (!["query-only", "read-write", "admin"].includes(permission2)) {
|
|
48180
|
+
if (!["query-only", "read-write", "data-admin", "admin"].includes(permission2)) {
|
|
48065
48181
|
throw new Error(`Invalid permission level: ${permission2}`);
|
|
48066
48182
|
}
|
|
48067
48183
|
const newConfig2 = configModule.merge(existingConfig, {
|
|
@@ -48107,10 +48223,11 @@ async function initCommandHandler(options) {
|
|
|
48107
48223
|
permission = await promptUser.select(t("init.prompt_permission"), [
|
|
48108
48224
|
"query-only",
|
|
48109
48225
|
"read-write",
|
|
48226
|
+
"data-admin",
|
|
48110
48227
|
"admin"
|
|
48111
48228
|
]);
|
|
48112
48229
|
}
|
|
48113
|
-
if (!["query-only", "read-write", "admin"].includes(permission)) {
|
|
48230
|
+
if (!["query-only", "read-write", "data-admin", "admin"].includes(permission)) {
|
|
48114
48231
|
throw new Error(`Invalid permission level: ${permission}`);
|
|
48115
48232
|
}
|
|
48116
48233
|
configForWrite = connection;
|
|
@@ -48205,7 +48322,7 @@ var Table = require_table();
|
|
|
48205
48322
|
class TableFormatter {
|
|
48206
48323
|
format(columns) {
|
|
48207
48324
|
const table = new Table({
|
|
48208
|
-
head: ["Column", "Type", "Nullable", "Default", "Key"],
|
|
48325
|
+
head: ["Column", "Type", "Nullable", "Default", "Key"].map((h) => colors.bold(h)),
|
|
48209
48326
|
style: { compact: false, "padding-left": 1, "padding-right": 1 },
|
|
48210
48327
|
colWidths: [25, 25, 10, 25, 20]
|
|
48211
48328
|
});
|
|
@@ -48231,14 +48348,14 @@ class TableFormatter {
|
|
|
48231
48348
|
class TableListFormatter {
|
|
48232
48349
|
format(tables) {
|
|
48233
48350
|
const table = new Table({
|
|
48234
|
-
head: ["Table", "Columns", "Rows", "Engine"],
|
|
48351
|
+
head: ["Table", "Columns", "Rows", "Engine"].map((h) => colors.bold(h)),
|
|
48235
48352
|
style: { compact: false, "padding-left": 1, "padding-right": 1 },
|
|
48236
48353
|
colWidths: [30, 12, 15, 15]
|
|
48237
48354
|
});
|
|
48238
48355
|
tables.forEach((t2) => {
|
|
48239
48356
|
table.push([
|
|
48240
48357
|
t2.name,
|
|
48241
|
-
t2.columns.length.toString(),
|
|
48358
|
+
(t2.columnCount ?? t2.columns.length).toString(),
|
|
48242
48359
|
(t2.rowCount ?? "?").toString(),
|
|
48243
48360
|
t2.engine || "N/A"
|
|
48244
48361
|
]);
|
|
@@ -48248,14 +48365,6 @@ class TableListFormatter {
|
|
|
48248
48365
|
}
|
|
48249
48366
|
// src/formatters/json-formatter.ts
|
|
48250
48367
|
init_size_category();
|
|
48251
|
-
|
|
48252
|
-
class JSONFormatter {
|
|
48253
|
-
format(data, options) {
|
|
48254
|
-
const spacing = options?.compact ? undefined : 2;
|
|
48255
|
-
return JSON.stringify(data, null, spacing);
|
|
48256
|
-
}
|
|
48257
|
-
}
|
|
48258
|
-
|
|
48259
48368
|
class TableSchemaJSONFormatter {
|
|
48260
48369
|
format(table, options) {
|
|
48261
48370
|
const spacing = options?.compact ? undefined : 2;
|
|
@@ -48413,8 +48522,15 @@ async function listAction(options) {
|
|
|
48413
48522
|
return;
|
|
48414
48523
|
}
|
|
48415
48524
|
if (options.format === "json") {
|
|
48416
|
-
const
|
|
48417
|
-
|
|
48525
|
+
const listOutput = tables.map((t2) => ({
|
|
48526
|
+
name: t2.name,
|
|
48527
|
+
columnCount: t2.columnCount ?? t2.columns.length,
|
|
48528
|
+
rowCount: t2.rowCount ?? 0,
|
|
48529
|
+
engine: t2.engine,
|
|
48530
|
+
estimatedRowCount: t2.estimatedRowCount ?? t2.rowCount ?? 0,
|
|
48531
|
+
tableType: t2.tableType ?? "table"
|
|
48532
|
+
}));
|
|
48533
|
+
console.log(JSON.stringify(listOutput, null, 2));
|
|
48418
48534
|
} else {
|
|
48419
48535
|
const formatter = new TableListFormatter;
|
|
48420
48536
|
console.log(formatter.format(tables));
|
|
@@ -48709,9 +48825,9 @@ async function handleFullDatabaseScan(adapter, config, options) {
|
|
|
48709
48825
|
};
|
|
48710
48826
|
await configModule.write(options.config, updatedConfig);
|
|
48711
48827
|
console.log(`
|
|
48712
|
-
\u2705
|
|
48713
|
-
console.log(` ${tables.length}
|
|
48714
|
-
console.log(`
|
|
48828
|
+
\u2705 Schema updated in .dbcli`);
|
|
48829
|
+
console.log(` ${tables.length} tables with full column details and relationships`);
|
|
48830
|
+
console.log(` Timestamp: ${updatedConfig.metadata.schemaLastUpdated}`);
|
|
48715
48831
|
}
|
|
48716
48832
|
|
|
48717
48833
|
// src/core/permission-guard.ts
|
|
@@ -48934,6 +49050,29 @@ function checkPermission(sql, permission) {
|
|
|
48934
49050
|
classification
|
|
48935
49051
|
};
|
|
48936
49052
|
}
|
|
49053
|
+
if (permission === "data-admin") {
|
|
49054
|
+
const allowedTypes = [
|
|
49055
|
+
"SELECT",
|
|
49056
|
+
"INSERT",
|
|
49057
|
+
"UPDATE",
|
|
49058
|
+
"DELETE",
|
|
49059
|
+
"SHOW",
|
|
49060
|
+
"DESCRIBE",
|
|
49061
|
+
"EXPLAIN"
|
|
49062
|
+
];
|
|
49063
|
+
if (allowedTypes.includes(classification.type)) {
|
|
49064
|
+
return {
|
|
49065
|
+
allowed: true,
|
|
49066
|
+
reason: `${classification.type} operation allowed in data-admin mode`,
|
|
49067
|
+
classification
|
|
49068
|
+
};
|
|
49069
|
+
}
|
|
49070
|
+
return {
|
|
49071
|
+
allowed: false,
|
|
49072
|
+
reason: `${classification.type} operation requires admin permission`,
|
|
49073
|
+
classification
|
|
49074
|
+
};
|
|
49075
|
+
}
|
|
48937
49076
|
if (permission === "read-write") {
|
|
48938
49077
|
const allowedTypes = [
|
|
48939
49078
|
"SELECT",
|
|
@@ -48952,7 +49091,7 @@ function checkPermission(sql, permission) {
|
|
|
48952
49091
|
}
|
|
48953
49092
|
return {
|
|
48954
49093
|
allowed: false,
|
|
48955
|
-
reason: `${classification.type} operation requires admin permission`,
|
|
49094
|
+
reason: `${classification.type} operation requires data-admin or admin permission`,
|
|
48956
49095
|
classification
|
|
48957
49096
|
};
|
|
48958
49097
|
}
|
|
@@ -49438,7 +49577,7 @@ class DataExecutor {
|
|
|
49438
49577
|
const dataKeys = Object.keys(data);
|
|
49439
49578
|
for (const key2 of dataKeys) {
|
|
49440
49579
|
if (!columnNames.includes(key2)) {
|
|
49441
|
-
throw new Error(
|
|
49580
|
+
throw new Error(`Column "${key2}" not found in table "${tableName}". Valid columns: ${columnNames.join(", ")}`);
|
|
49442
49581
|
}
|
|
49443
49582
|
}
|
|
49444
49583
|
const columns = dataKeys;
|
|
@@ -49471,12 +49610,12 @@ class DataExecutor {
|
|
|
49471
49610
|
}
|
|
49472
49611
|
if (!options?.force) {
|
|
49473
49612
|
console.log(`
|
|
49474
|
-
|
|
49613
|
+
Generated SQL:`);
|
|
49475
49614
|
console.log(` ${sql}`);
|
|
49476
49615
|
console.log(`
|
|
49477
|
-
|
|
49616
|
+
Parameters:`);
|
|
49478
49617
|
console.log(` ${JSON.stringify(params, null, 2)}`);
|
|
49479
|
-
const confirmed = await promptUser.confirm("
|
|
49618
|
+
const confirmed = await promptUser.confirm("Proceed with this operation?");
|
|
49480
49619
|
if (!confirmed) {
|
|
49481
49620
|
return {
|
|
49482
49621
|
status: "success",
|
|
@@ -49507,7 +49646,7 @@ class DataExecutor {
|
|
|
49507
49646
|
operation: "insert",
|
|
49508
49647
|
rows_affected: 0,
|
|
49509
49648
|
timestamp,
|
|
49510
|
-
error: "
|
|
49649
|
+
error: "Permission denied: Query-only mode only allows SELECT. Use Read-Write or Admin mode to execute INSERT."
|
|
49511
49650
|
};
|
|
49512
49651
|
}
|
|
49513
49652
|
return {
|
|
@@ -49515,7 +49654,7 @@ class DataExecutor {
|
|
|
49515
49654
|
operation: "insert",
|
|
49516
49655
|
rows_affected: 0,
|
|
49517
49656
|
timestamp,
|
|
49518
|
-
error: `INSERT
|
|
49657
|
+
error: `INSERT failed: ${errorMessage}`
|
|
49519
49658
|
};
|
|
49520
49659
|
}
|
|
49521
49660
|
}
|
|
@@ -49538,12 +49677,12 @@ class DataExecutor {
|
|
|
49538
49677
|
}
|
|
49539
49678
|
if (!options?.force) {
|
|
49540
49679
|
console.log(`
|
|
49541
|
-
|
|
49680
|
+
Generated SQL:`);
|
|
49542
49681
|
console.log(` ${sql}`);
|
|
49543
49682
|
console.log(`
|
|
49544
|
-
|
|
49683
|
+
Parameters:`);
|
|
49545
49684
|
console.log(` ${JSON.stringify(params, null, 2)}`);
|
|
49546
|
-
const confirmed = await promptUser.confirm("
|
|
49685
|
+
const confirmed = await promptUser.confirm("Proceed with this operation?");
|
|
49547
49686
|
if (!confirmed) {
|
|
49548
49687
|
return {
|
|
49549
49688
|
status: "success",
|
|
@@ -49574,7 +49713,7 @@ class DataExecutor {
|
|
|
49574
49713
|
operation: "update",
|
|
49575
49714
|
rows_affected: 0,
|
|
49576
49715
|
timestamp,
|
|
49577
|
-
error: "
|
|
49716
|
+
error: "Permission denied: Query-only mode only allows SELECT. Use Read-Write or Admin mode to execute UPDATE."
|
|
49578
49717
|
};
|
|
49579
49718
|
}
|
|
49580
49719
|
return {
|
|
@@ -49582,7 +49721,7 @@ class DataExecutor {
|
|
|
49582
49721
|
operation: "update",
|
|
49583
49722
|
rows_affected: 0,
|
|
49584
49723
|
timestamp,
|
|
49585
|
-
error: `UPDATE
|
|
49724
|
+
error: `UPDATE failed: ${errorMessage}`
|
|
49586
49725
|
};
|
|
49587
49726
|
}
|
|
49588
49727
|
}
|
|
@@ -49592,13 +49731,13 @@ class DataExecutor {
|
|
|
49592
49731
|
if (this.blacklistValidator) {
|
|
49593
49732
|
this.blacklistValidator.checkTableBlacklist("DELETE", tableName, []);
|
|
49594
49733
|
}
|
|
49595
|
-
if (this.permission !== "admin") {
|
|
49734
|
+
if (this.permission !== "data-admin" && this.permission !== "admin") {
|
|
49596
49735
|
return {
|
|
49597
49736
|
status: "error",
|
|
49598
49737
|
operation: "delete",
|
|
49599
49738
|
rows_affected: 0,
|
|
49600
49739
|
timestamp,
|
|
49601
|
-
error: "
|
|
49740
|
+
error: "Permission denied: DELETE operation requires Data-Admin or Admin permission."
|
|
49602
49741
|
};
|
|
49603
49742
|
}
|
|
49604
49743
|
const { sql, params } = this.buildDeleteSql(tableName, where, schema);
|
|
@@ -49613,14 +49752,14 @@ class DataExecutor {
|
|
|
49613
49752
|
}
|
|
49614
49753
|
if (!options?.force) {
|
|
49615
49754
|
console.log(`
|
|
49616
|
-
\u26A0\uFE0F
|
|
49755
|
+
\u26A0\uFE0F Warning: DELETE operation is destructive and cannot be undone!`);
|
|
49617
49756
|
console.log(`
|
|
49618
|
-
|
|
49757
|
+
Generated SQL:`);
|
|
49619
49758
|
console.log(` ${sql}`);
|
|
49620
49759
|
console.log(`
|
|
49621
|
-
|
|
49760
|
+
Parameters:`);
|
|
49622
49761
|
console.log(` ${JSON.stringify(params, null, 2)}`);
|
|
49623
|
-
const confirmed = await promptUser.confirm("
|
|
49762
|
+
const confirmed = await promptUser.confirm("Are you sure you want to execute this DELETE operation? This cannot be undone.");
|
|
49624
49763
|
if (!confirmed) {
|
|
49625
49764
|
return {
|
|
49626
49765
|
status: "success",
|
|
@@ -49650,7 +49789,7 @@ class DataExecutor {
|
|
|
49650
49789
|
operation: "delete",
|
|
49651
49790
|
rows_affected: 0,
|
|
49652
49791
|
timestamp,
|
|
49653
|
-
error: `DELETE
|
|
49792
|
+
error: `DELETE failed: ${errorMessage}`
|
|
49654
49793
|
};
|
|
49655
49794
|
}
|
|
49656
49795
|
}
|
|
@@ -49666,7 +49805,7 @@ class DataExecutor {
|
|
|
49666
49805
|
const columnNames = schema.columns.map((col) => col.name);
|
|
49667
49806
|
for (const key2 of [...dataKeys, ...whereKeys]) {
|
|
49668
49807
|
if (!columnNames.includes(key2)) {
|
|
49669
|
-
throw new Error(
|
|
49808
|
+
throw new Error(`Column "${key2}" not found in table "${tableName}"`);
|
|
49670
49809
|
}
|
|
49671
49810
|
}
|
|
49672
49811
|
const systemType = this.getSystemType();
|
|
@@ -49689,7 +49828,7 @@ class DataExecutor {
|
|
|
49689
49828
|
const columnNames = schema.columns.map((col) => col.name);
|
|
49690
49829
|
for (const key2 of whereKeys) {
|
|
49691
49830
|
if (!columnNames.includes(key2)) {
|
|
49692
|
-
throw new Error(
|
|
49831
|
+
throw new Error(`Column "${key2}" not found in table "${tableName}"`);
|
|
49693
49832
|
}
|
|
49694
49833
|
}
|
|
49695
49834
|
const systemType = this.getSystemType();
|
|
@@ -49983,7 +50122,7 @@ async function deleteCommand(table, options) {
|
|
|
49983
50122
|
if (!config.connection) {
|
|
49984
50123
|
throw new Error('Run "dbcli init" to configure database connection');
|
|
49985
50124
|
}
|
|
49986
|
-
if (config.permission !== "admin") {
|
|
50125
|
+
if (config.permission !== "data-admin" && config.permission !== "admin") {
|
|
49987
50126
|
throw new PermissionError(t("delete.admin_only"), { type: "DELETE", isDangerous: true, keywords: ["DELETE"], isComposite: false, confidence: "HIGH" }, config.permission);
|
|
49988
50127
|
}
|
|
49989
50128
|
const adapter = AdapterFactory.createAdapter(config.connection);
|
|
@@ -50025,7 +50164,7 @@ async function deleteCommand(table, options) {
|
|
|
50025
50164
|
process.exit(1);
|
|
50026
50165
|
}
|
|
50027
50166
|
if (error instanceof PermissionError) {
|
|
50028
|
-
console.error(t_vars("errors.permission_denied", { required: "admin" }));
|
|
50167
|
+
console.error(t_vars("errors.permission_denied", { required: "data-admin" }));
|
|
50029
50168
|
console.error(` Operation: ${error.classification.type}`);
|
|
50030
50169
|
console.error(` Message: ${error.message}`);
|
|
50031
50170
|
process.exit(1);
|
|
@@ -50099,7 +50238,17 @@ async function exportCommand(sql, options) {
|
|
|
50099
50238
|
// src/commands/skill.ts
|
|
50100
50239
|
import * as path from "path";
|
|
50101
50240
|
import { homedir } from "os";
|
|
50102
|
-
|
|
50241
|
+
function findPackageRoot() {
|
|
50242
|
+
let dir = import.meta.dir;
|
|
50243
|
+
for (let i = 0;i < 5; i++) {
|
|
50244
|
+
if (Bun.file(path.join(dir, "package.json")).size > 0) {
|
|
50245
|
+
return dir;
|
|
50246
|
+
}
|
|
50247
|
+
dir = path.dirname(dir);
|
|
50248
|
+
}
|
|
50249
|
+
return path.resolve(import.meta.dir, "../..");
|
|
50250
|
+
}
|
|
50251
|
+
var SKILL_SOURCE_PATH = path.join(findPackageRoot(), "assets", "SKILL.md");
|
|
50103
50252
|
async function skillCommand(_program, options) {
|
|
50104
50253
|
try {
|
|
50105
50254
|
const skillFile = Bun.file(SKILL_SOURCE_PATH);
|
|
@@ -50753,8 +50902,671 @@ Schema diff (${beforeSnapshot.createdAt} -> ${currentSnapshot.createdAt}):`);
|
|
|
50753
50902
|
}
|
|
50754
50903
|
}
|
|
50755
50904
|
|
|
50905
|
+
// src/commands/status.ts
|
|
50906
|
+
var statusCommand = new Command("status").description("Show current configuration status (safe for AI agents, no credentials exposed)").option("--format <type>", "Output format: text, json", "json").action(async (options) => {
|
|
50907
|
+
try {
|
|
50908
|
+
const config = await configModule.read(".dbcli");
|
|
50909
|
+
if (!config.connection) {
|
|
50910
|
+
console.error('No configuration found. Run "dbcli init" first.');
|
|
50911
|
+
process.exit(1);
|
|
50912
|
+
}
|
|
50913
|
+
const blacklistTables = config.blacklist?.tables ?? [];
|
|
50914
|
+
const blacklistColumns = config.blacklist?.columns ?? {};
|
|
50915
|
+
const columnCount = Object.values(blacklistColumns).reduce((sum, cols) => sum + cols.length, 0);
|
|
50916
|
+
const status = {
|
|
50917
|
+
permission: config.permission,
|
|
50918
|
+
system: config.connection.system,
|
|
50919
|
+
blacklist: {
|
|
50920
|
+
tables: blacklistTables.length,
|
|
50921
|
+
columns: columnCount
|
|
50922
|
+
},
|
|
50923
|
+
version: config.metadata?.version ?? "unknown"
|
|
50924
|
+
};
|
|
50925
|
+
if (options.format === "text") {
|
|
50926
|
+
console.log(`Permission: ${status.permission}`);
|
|
50927
|
+
console.log(`System: ${status.system}`);
|
|
50928
|
+
console.log(`Blacklist: ${status.blacklist.tables} table(s), ${status.blacklist.columns} column(s)`);
|
|
50929
|
+
console.log(`Version: ${status.version}`);
|
|
50930
|
+
} else {
|
|
50931
|
+
console.log(JSON.stringify(status, null, 2));
|
|
50932
|
+
}
|
|
50933
|
+
} catch (error) {
|
|
50934
|
+
console.error(`Error: ${error.message}`);
|
|
50935
|
+
process.exit(1);
|
|
50936
|
+
}
|
|
50937
|
+
});
|
|
50938
|
+
|
|
50939
|
+
// src/commands/doctor.ts
|
|
50940
|
+
import { join as join3 } from "path";
|
|
50941
|
+
var SENSITIVE_PATTERNS = [
|
|
50942
|
+
"password",
|
|
50943
|
+
"passwd",
|
|
50944
|
+
"secret",
|
|
50945
|
+
"token",
|
|
50946
|
+
"api_key",
|
|
50947
|
+
"apikey",
|
|
50948
|
+
"access_key",
|
|
50949
|
+
"private_key",
|
|
50950
|
+
"credential",
|
|
50951
|
+
"auth_token",
|
|
50952
|
+
"refresh_token",
|
|
50953
|
+
"session_token",
|
|
50954
|
+
"ssn",
|
|
50955
|
+
"credit_card"
|
|
50956
|
+
];
|
|
50957
|
+
function compareSemver(a, b) {
|
|
50958
|
+
const pa = a.split(".").map(Number);
|
|
50959
|
+
const pb = b.split(".").map(Number);
|
|
50960
|
+
for (let i = 0;i < 3; i++) {
|
|
50961
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
50962
|
+
if (diff !== 0)
|
|
50963
|
+
return diff;
|
|
50964
|
+
}
|
|
50965
|
+
return 0;
|
|
50966
|
+
}
|
|
50967
|
+
var runDoctorChecks = {
|
|
50968
|
+
checkBunVersion(current, required) {
|
|
50969
|
+
const passes = compareSemver(current, required) >= 0;
|
|
50970
|
+
return {
|
|
50971
|
+
group: "Environment",
|
|
50972
|
+
label: "Bun version",
|
|
50973
|
+
status: passes ? "pass" : "error",
|
|
50974
|
+
message: passes ? `Bun v${current} (meets >= ${required})` : `Bun v${current} is below required >= ${required}`
|
|
50975
|
+
};
|
|
50976
|
+
},
|
|
50977
|
+
async checkLatestVersion(currentVersion) {
|
|
50978
|
+
try {
|
|
50979
|
+
const response = await fetch("https://registry.npmjs.org/@carllee1983/dbcli/latest", { signal: AbortSignal.timeout(5000) });
|
|
50980
|
+
if (!response.ok)
|
|
50981
|
+
throw new Error(`HTTP ${response.status}`);
|
|
50982
|
+
const data = await response.json();
|
|
50983
|
+
const latest = data.version;
|
|
50984
|
+
const isLatest = currentVersion === latest;
|
|
50985
|
+
return {
|
|
50986
|
+
group: "Environment",
|
|
50987
|
+
label: "dbcli version",
|
|
50988
|
+
status: isLatest ? "pass" : "warn",
|
|
50989
|
+
message: isLatest ? `dbcli v${currentVersion} (latest)` : `dbcli v${currentVersion} (latest: ${latest})`
|
|
50990
|
+
};
|
|
50991
|
+
} catch {
|
|
50992
|
+
return {
|
|
50993
|
+
group: "Environment",
|
|
50994
|
+
label: "dbcli version",
|
|
50995
|
+
status: "pass",
|
|
50996
|
+
message: `dbcli v${currentVersion} (version check skipped)`
|
|
50997
|
+
};
|
|
50998
|
+
}
|
|
50999
|
+
},
|
|
51000
|
+
async checkConfigExists(configPath, existsFn) {
|
|
51001
|
+
const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join3(configPath, "config.json")).exists();
|
|
51002
|
+
return {
|
|
51003
|
+
group: "Configuration",
|
|
51004
|
+
label: "Config exists",
|
|
51005
|
+
status: exists ? "pass" : "error",
|
|
51006
|
+
message: exists ? `Config found: ${configPath}` : `No config found at ${configPath}. Run "dbcli init" first.`
|
|
51007
|
+
};
|
|
51008
|
+
},
|
|
51009
|
+
checkBlacklistCompleteness(tableColumns, blacklistedColumns) {
|
|
51010
|
+
const unprotected = [];
|
|
51011
|
+
for (const [table, columns] of tableColumns) {
|
|
51012
|
+
const blacklisted = blacklistedColumns.get(table) ?? new Set;
|
|
51013
|
+
for (const col of columns) {
|
|
51014
|
+
const colLower = col.toLowerCase();
|
|
51015
|
+
const isSensitive = SENSITIVE_PATTERNS.some((p) => colLower.includes(p));
|
|
51016
|
+
if (isSensitive && !blacklisted.has(col)) {
|
|
51017
|
+
unprotected.push(`${table}.${col}`);
|
|
51018
|
+
}
|
|
51019
|
+
}
|
|
51020
|
+
}
|
|
51021
|
+
if (unprotected.length === 0) {
|
|
51022
|
+
return {
|
|
51023
|
+
group: "Configuration",
|
|
51024
|
+
label: "Blacklist completeness",
|
|
51025
|
+
status: "pass",
|
|
51026
|
+
message: "All detected sensitive columns are protected"
|
|
51027
|
+
};
|
|
51028
|
+
}
|
|
51029
|
+
return {
|
|
51030
|
+
group: "Configuration",
|
|
51031
|
+
label: "Blacklist completeness",
|
|
51032
|
+
status: "warn",
|
|
51033
|
+
message: `Consider protecting: ${unprotected.join(", ")}`
|
|
51034
|
+
};
|
|
51035
|
+
},
|
|
51036
|
+
checkSchemaCacheFreshness(lastUpdated) {
|
|
51037
|
+
if (!lastUpdated) {
|
|
51038
|
+
return {
|
|
51039
|
+
group: "Connection & Data",
|
|
51040
|
+
label: "Schema cache",
|
|
51041
|
+
status: "warn",
|
|
51042
|
+
message: 'No schema cache found \u2014 run "dbcli schema --refresh"'
|
|
51043
|
+
};
|
|
51044
|
+
}
|
|
51045
|
+
const ageMs = Date.now() - new Date(lastUpdated).getTime();
|
|
51046
|
+
const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
|
|
51047
|
+
if (ageDays > 7) {
|
|
51048
|
+
return {
|
|
51049
|
+
group: "Connection & Data",
|
|
51050
|
+
label: "Schema cache",
|
|
51051
|
+
status: "warn",
|
|
51052
|
+
message: `Schema cache is ${ageDays} days old \u2014 run "dbcli schema --refresh"`
|
|
51053
|
+
};
|
|
51054
|
+
}
|
|
51055
|
+
return {
|
|
51056
|
+
group: "Connection & Data",
|
|
51057
|
+
label: "Schema cache",
|
|
51058
|
+
status: "pass",
|
|
51059
|
+
message: `Schema cache is ${ageDays} day(s) old`
|
|
51060
|
+
};
|
|
51061
|
+
},
|
|
51062
|
+
checkLargeTables(tables) {
|
|
51063
|
+
const large = tables.filter((t7) => (t7.estimatedRowCount ?? 0) > 1e6);
|
|
51064
|
+
if (large.length === 0) {
|
|
51065
|
+
return {
|
|
51066
|
+
group: "Connection & Data",
|
|
51067
|
+
label: "Large tables",
|
|
51068
|
+
status: "pass",
|
|
51069
|
+
message: "No tables exceed 1M rows"
|
|
51070
|
+
};
|
|
51071
|
+
}
|
|
51072
|
+
const list = large.map((t7) => `${t7.name} (${((t7.estimatedRowCount ?? 0) / 1e6).toFixed(1)}M rows)`).join(", ");
|
|
51073
|
+
return {
|
|
51074
|
+
group: "Connection & Data",
|
|
51075
|
+
label: "Large tables",
|
|
51076
|
+
status: "warn",
|
|
51077
|
+
message: `Large tables: ${list}`
|
|
51078
|
+
};
|
|
51079
|
+
},
|
|
51080
|
+
formatTextOutput(results, version) {
|
|
51081
|
+
const lines2 = [`dbcli doctor v${version}`, ""];
|
|
51082
|
+
const groups = ["Environment", "Configuration", "Connection & Data"];
|
|
51083
|
+
for (const group of groups) {
|
|
51084
|
+
const groupResults = results.filter((r) => r.group === group);
|
|
51085
|
+
if (groupResults.length === 0)
|
|
51086
|
+
continue;
|
|
51087
|
+
lines2.push(group);
|
|
51088
|
+
for (const r of groupResults) {
|
|
51089
|
+
const icon = r.status === "pass" ? colors.success("\u2713") : r.status === "warn" ? colors.warn("\u26A0") : colors.error("\u2717");
|
|
51090
|
+
lines2.push(` ${icon} ${r.message}`);
|
|
51091
|
+
}
|
|
51092
|
+
lines2.push("");
|
|
51093
|
+
}
|
|
51094
|
+
const passed = results.filter((r) => r.status === "pass").length;
|
|
51095
|
+
const warnings = results.filter((r) => r.status === "warn").length;
|
|
51096
|
+
const errors3 = results.filter((r) => r.status === "error").length;
|
|
51097
|
+
lines2.push(`Summary: ${passed} passed, ${warnings} warning(s), ${errors3} error(s)`);
|
|
51098
|
+
return lines2.join(`
|
|
51099
|
+
`);
|
|
51100
|
+
}
|
|
51101
|
+
};
|
|
51102
|
+
var doctorCommand = new Command("doctor").description("Run diagnostic checks on dbcli configuration, environment, and connection").option("--format <type>", "Output format: text, json", "text").action(async (options) => {
|
|
51103
|
+
const logger = getLogger();
|
|
51104
|
+
const results = [];
|
|
51105
|
+
const configPath = doctorCommand.parent?.opts().config ?? ".dbcli";
|
|
51106
|
+
const bunVersion = process.versions.bun ?? "unknown";
|
|
51107
|
+
const requiredBun = package_default.engines?.bun?.replace(">=", "") ?? "1.3.3";
|
|
51108
|
+
results.push(runDoctorChecks.checkBunVersion(bunVersion, requiredBun));
|
|
51109
|
+
results.push(await runDoctorChecks.checkLatestVersion(package_default.version));
|
|
51110
|
+
const configExists = await runDoctorChecks.checkConfigExists(configPath);
|
|
51111
|
+
results.push(configExists);
|
|
51112
|
+
if (configExists.status !== "error") {
|
|
51113
|
+
try {
|
|
51114
|
+
const config = await configModule.read(configPath);
|
|
51115
|
+
results.push({
|
|
51116
|
+
group: "Configuration",
|
|
51117
|
+
label: "Config valid",
|
|
51118
|
+
status: "pass",
|
|
51119
|
+
message: "Config valid"
|
|
51120
|
+
});
|
|
51121
|
+
results.push({
|
|
51122
|
+
group: "Configuration",
|
|
51123
|
+
label: "Permission",
|
|
51124
|
+
status: "pass",
|
|
51125
|
+
message: `Permission: ${config.permission}`
|
|
51126
|
+
});
|
|
51127
|
+
const blacklistedColumns = new Map;
|
|
51128
|
+
if (config.blacklist?.columns) {
|
|
51129
|
+
for (const [table, cols] of Object.entries(config.blacklist.columns)) {
|
|
51130
|
+
blacklistedColumns.set(table, new Set(cols));
|
|
51131
|
+
}
|
|
51132
|
+
}
|
|
51133
|
+
try {
|
|
51134
|
+
const adapter = AdapterFactory.createAdapter(config.connection);
|
|
51135
|
+
await adapter.connect();
|
|
51136
|
+
results.push({
|
|
51137
|
+
group: "Connection & Data",
|
|
51138
|
+
label: "Connection",
|
|
51139
|
+
status: "pass",
|
|
51140
|
+
message: `Connected to ${config.connection.system} ${config.connection.database}@${config.connection.host}:${config.connection.port}`
|
|
51141
|
+
});
|
|
51142
|
+
try {
|
|
51143
|
+
const tables = await adapter.listTables();
|
|
51144
|
+
const tableColumns = new Map;
|
|
51145
|
+
for (const t7 of tables) {
|
|
51146
|
+
tableColumns.set(t7.name, t7.columns.map((c) => c.name));
|
|
51147
|
+
}
|
|
51148
|
+
results.push(runDoctorChecks.checkBlacklistCompleteness(tableColumns, blacklistedColumns));
|
|
51149
|
+
results.push(runDoctorChecks.checkLargeTables(tables));
|
|
51150
|
+
} catch {
|
|
51151
|
+
logger.debug("Could not list tables for blacklist/large table check");
|
|
51152
|
+
}
|
|
51153
|
+
try {
|
|
51154
|
+
const indexPath = join3(configPath, "schemas", "index.json");
|
|
51155
|
+
const indexFile = Bun.file(indexPath);
|
|
51156
|
+
if (await indexFile.exists()) {
|
|
51157
|
+
const indexContent = await indexFile.text();
|
|
51158
|
+
const index = JSON.parse(indexContent);
|
|
51159
|
+
results.push(runDoctorChecks.checkSchemaCacheFreshness(index.updatedAt ?? null));
|
|
51160
|
+
} else {
|
|
51161
|
+
results.push(runDoctorChecks.checkSchemaCacheFreshness(null));
|
|
51162
|
+
}
|
|
51163
|
+
} catch {
|
|
51164
|
+
results.push(runDoctorChecks.checkSchemaCacheFreshness(null));
|
|
51165
|
+
}
|
|
51166
|
+
await adapter.disconnect();
|
|
51167
|
+
} catch (error) {
|
|
51168
|
+
results.push({
|
|
51169
|
+
group: "Connection & Data",
|
|
51170
|
+
label: "Connection",
|
|
51171
|
+
status: "error",
|
|
51172
|
+
message: `Connection failed: ${error.message}`
|
|
51173
|
+
});
|
|
51174
|
+
}
|
|
51175
|
+
} catch (error) {
|
|
51176
|
+
results.push({
|
|
51177
|
+
group: "Configuration",
|
|
51178
|
+
label: "Config valid",
|
|
51179
|
+
status: "error",
|
|
51180
|
+
message: `Config invalid: ${error.message}`
|
|
51181
|
+
});
|
|
51182
|
+
}
|
|
51183
|
+
}
|
|
51184
|
+
const hasError = results.some((r) => r.status === "error");
|
|
51185
|
+
if (options.format === "json") {
|
|
51186
|
+
console.log(JSON.stringify({ results, hasError }, null, 2));
|
|
51187
|
+
} else {
|
|
51188
|
+
console.log(runDoctorChecks.formatTextOutput(results, package_default.version));
|
|
51189
|
+
}
|
|
51190
|
+
if (hasError) {
|
|
51191
|
+
process.exit(1);
|
|
51192
|
+
}
|
|
51193
|
+
});
|
|
51194
|
+
|
|
51195
|
+
// src/commands/completion.ts
|
|
51196
|
+
import { join as join4 } from "path";
|
|
51197
|
+
import { homedir as homedir2 } from "os";
|
|
51198
|
+
function extractCommands(program2) {
|
|
51199
|
+
return program2.commands.map((cmd) => ({
|
|
51200
|
+
name: cmd.name(),
|
|
51201
|
+
options: cmd.options.map((o) => o.long ?? o.short ?? "").filter(Boolean)
|
|
51202
|
+
}));
|
|
51203
|
+
}
|
|
51204
|
+
function extractGlobalOptions(program2) {
|
|
51205
|
+
return program2.options.map((o) => o.long ?? o.short ?? "").filter(Boolean);
|
|
51206
|
+
}
|
|
51207
|
+
function generateBashCompletion(commands, globalOptions) {
|
|
51208
|
+
const cmdNames = commands.map((c) => c.name).join(" ");
|
|
51209
|
+
const globalOpts = globalOptions.join(" ");
|
|
51210
|
+
const caseEntries = commands.map((c) => ` ${c.name})
|
|
51211
|
+
COMPREPLY=( $(compgen -W "${c.options.join(" ")}" -- "\${cur}") )
|
|
51212
|
+
;;`).join(`
|
|
51213
|
+
`);
|
|
51214
|
+
return `#!/bin/bash
|
|
51215
|
+
# dbcli bash completion \u2014 auto-generated, do not edit
|
|
51216
|
+
_dbcli_completions() {
|
|
51217
|
+
local cur prev commands
|
|
51218
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
51219
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
51220
|
+
commands="${cmdNames}"
|
|
51221
|
+
|
|
51222
|
+
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
51223
|
+
COMPREPLY=( $(compgen -W "\${commands} ${globalOpts}" -- "\${cur}") )
|
|
51224
|
+
return 0
|
|
51225
|
+
fi
|
|
51226
|
+
|
|
51227
|
+
case "\${COMP_WORDS[1]}" in
|
|
51228
|
+
${caseEntries}
|
|
51229
|
+
*)
|
|
51230
|
+
COMPREPLY=( $(compgen -W "${globalOpts}" -- "\${cur}") )
|
|
51231
|
+
;;
|
|
51232
|
+
esac
|
|
51233
|
+
}
|
|
51234
|
+
complete -F _dbcli_completions dbcli
|
|
51235
|
+
`;
|
|
51236
|
+
}
|
|
51237
|
+
function generateZshCompletion(commands, globalOptions) {
|
|
51238
|
+
const cmdLines = commands.map((c) => ` '${c.name}:${c.name} command'`).join(`
|
|
51239
|
+
`);
|
|
51240
|
+
const subcmdCases = commands.map((c) => {
|
|
51241
|
+
const opts = c.options.map((o) => `'${o}[${o}]'`).join(" ");
|
|
51242
|
+
return ` ${c.name})
|
|
51243
|
+
_arguments ${opts}
|
|
51244
|
+
;;`;
|
|
51245
|
+
}).join(`
|
|
51246
|
+
`);
|
|
51247
|
+
const globalOpts = globalOptions.map((o) => `'${o}[${o}]'`).join(" ");
|
|
51248
|
+
return `#compdef dbcli
|
|
51249
|
+
# dbcli zsh completion \u2014 auto-generated, do not edit
|
|
51250
|
+
_dbcli() {
|
|
51251
|
+
local -a commands
|
|
51252
|
+
commands=(
|
|
51253
|
+
${cmdLines}
|
|
51254
|
+
)
|
|
51255
|
+
|
|
51256
|
+
_arguments -C \\
|
|
51257
|
+
${globalOpts} \\
|
|
51258
|
+
'1:command:->cmd' \\
|
|
51259
|
+
'*::arg:->args'
|
|
51260
|
+
|
|
51261
|
+
case "$state" in
|
|
51262
|
+
cmd)
|
|
51263
|
+
_describe 'command' commands
|
|
51264
|
+
;;
|
|
51265
|
+
args)
|
|
51266
|
+
case "$words[1]" in
|
|
51267
|
+
${subcmdCases}
|
|
51268
|
+
esac
|
|
51269
|
+
;;
|
|
51270
|
+
esac
|
|
51271
|
+
}
|
|
51272
|
+
_dbcli
|
|
51273
|
+
`;
|
|
51274
|
+
}
|
|
51275
|
+
function generateFishCompletion(commands, globalOptions) {
|
|
51276
|
+
const lines2 = [
|
|
51277
|
+
"# dbcli fish completion \u2014 auto-generated, do not edit",
|
|
51278
|
+
""
|
|
51279
|
+
];
|
|
51280
|
+
for (const opt of globalOptions) {
|
|
51281
|
+
const longName = opt.replace(/^--/, "");
|
|
51282
|
+
lines2.push(`complete -c dbcli -n '__fish_use_subcommand' -l ${longName} -d '${opt}'`);
|
|
51283
|
+
}
|
|
51284
|
+
for (const cmd of commands) {
|
|
51285
|
+
lines2.push(`complete -c dbcli -n '__fish_use_subcommand' -a ${cmd.name} -d '${cmd.name} command'`);
|
|
51286
|
+
}
|
|
51287
|
+
for (const cmd of commands) {
|
|
51288
|
+
for (const opt of cmd.options) {
|
|
51289
|
+
const longName = opt.replace(/^--/, "");
|
|
51290
|
+
lines2.push(`complete -c dbcli -n '__fish_seen_subcommand_from ${cmd.name}' -l ${longName} -d '${opt}'`);
|
|
51291
|
+
}
|
|
51292
|
+
}
|
|
51293
|
+
return lines2.join(`
|
|
51294
|
+
`) + `
|
|
51295
|
+
`;
|
|
51296
|
+
}
|
|
51297
|
+
function getInstallPath2(shell) {
|
|
51298
|
+
const home = homedir2();
|
|
51299
|
+
switch (shell) {
|
|
51300
|
+
case "bash":
|
|
51301
|
+
return join4(home, ".bashrc");
|
|
51302
|
+
case "zsh":
|
|
51303
|
+
return join4(home, ".zshrc");
|
|
51304
|
+
case "fish":
|
|
51305
|
+
return join4(home, ".config", "fish", "completions", "dbcli.fish");
|
|
51306
|
+
default:
|
|
51307
|
+
throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
51308
|
+
}
|
|
51309
|
+
}
|
|
51310
|
+
function detectShell() {
|
|
51311
|
+
const shellEnv = process.env.SHELL ?? "";
|
|
51312
|
+
if (shellEnv.includes("zsh"))
|
|
51313
|
+
return "zsh";
|
|
51314
|
+
if (shellEnv.includes("bash"))
|
|
51315
|
+
return "bash";
|
|
51316
|
+
if (shellEnv.includes("fish"))
|
|
51317
|
+
return "fish";
|
|
51318
|
+
return "bash";
|
|
51319
|
+
}
|
|
51320
|
+
var MARKER_START = "# >>> dbcli completion >>>";
|
|
51321
|
+
var MARKER_END = "# <<< dbcli completion <<<";
|
|
51322
|
+
async function installCompletion(shell, script) {
|
|
51323
|
+
const targetPath = getInstallPath2(shell);
|
|
51324
|
+
if (shell === "fish") {
|
|
51325
|
+
const dir = join4(homedir2(), ".config", "fish", "completions");
|
|
51326
|
+
await Bun.$`mkdir -p ${dir}`.quiet();
|
|
51327
|
+
await Bun.file(targetPath).write(script);
|
|
51328
|
+
console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
|
|
51329
|
+
return;
|
|
51330
|
+
}
|
|
51331
|
+
const file = Bun.file(targetPath);
|
|
51332
|
+
let content = "";
|
|
51333
|
+
if (await file.exists()) {
|
|
51334
|
+
content = await file.text();
|
|
51335
|
+
}
|
|
51336
|
+
const markerRegex = new RegExp(`${MARKER_START}[\\s\\S]*?${MARKER_END}\\n?`, "g");
|
|
51337
|
+
content = content.replace(markerRegex, "");
|
|
51338
|
+
const block = `
|
|
51339
|
+
${MARKER_START}
|
|
51340
|
+
eval "$(dbcli completion ${shell})"
|
|
51341
|
+
${MARKER_END}
|
|
51342
|
+
`;
|
|
51343
|
+
content = content.trimEnd() + `
|
|
51344
|
+
` + block;
|
|
51345
|
+
await Bun.file(targetPath).write(content);
|
|
51346
|
+
console.log(colors.success(`\u2713 Completion installed to ${targetPath}`));
|
|
51347
|
+
console.log(colors.info(` Run: source ${targetPath}`));
|
|
51348
|
+
}
|
|
51349
|
+
var completionCommand = new Command("completion").description("Generate shell completion scripts (bash, zsh, fish)").argument("[shell]", "Shell type: bash, zsh, fish").option("--install [shell]", "Auto-install completion to shell rc file").action(async (shellArg, options) => {
|
|
51350
|
+
const parentProgram = completionCommand.parent;
|
|
51351
|
+
if (!parentProgram) {
|
|
51352
|
+
console.error(colors.error("Error: completion command must be registered to a program"));
|
|
51353
|
+
process.exit(1);
|
|
51354
|
+
}
|
|
51355
|
+
const commands = extractCommands(parentProgram);
|
|
51356
|
+
const globalOptions = extractGlobalOptions(parentProgram);
|
|
51357
|
+
if (options.install !== undefined) {
|
|
51358
|
+
const shell2 = typeof options.install === "string" ? options.install : shellArg ?? detectShell();
|
|
51359
|
+
const generators2 = {
|
|
51360
|
+
bash: generateBashCompletion,
|
|
51361
|
+
zsh: generateZshCompletion,
|
|
51362
|
+
fish: generateFishCompletion
|
|
51363
|
+
};
|
|
51364
|
+
const generate2 = generators2[shell2];
|
|
51365
|
+
if (!generate2) {
|
|
51366
|
+
console.error(colors.error(`Unsupported shell: ${shell2}. Supported: bash, zsh, fish`));
|
|
51367
|
+
process.exit(1);
|
|
51368
|
+
}
|
|
51369
|
+
const script = generate2(commands, globalOptions);
|
|
51370
|
+
await installCompletion(shell2, script);
|
|
51371
|
+
return;
|
|
51372
|
+
}
|
|
51373
|
+
const shell = shellArg ?? detectShell();
|
|
51374
|
+
const generators = {
|
|
51375
|
+
bash: generateBashCompletion,
|
|
51376
|
+
zsh: generateZshCompletion,
|
|
51377
|
+
fish: generateFishCompletion
|
|
51378
|
+
};
|
|
51379
|
+
const generate = generators[shell];
|
|
51380
|
+
if (!generate) {
|
|
51381
|
+
console.error(colors.error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`));
|
|
51382
|
+
process.exit(1);
|
|
51383
|
+
}
|
|
51384
|
+
process.stdout.write(generate(commands, globalOptions));
|
|
51385
|
+
});
|
|
51386
|
+
|
|
51387
|
+
// src/utils/version-check.ts
|
|
51388
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@carllee1983/dbcli/latest";
|
|
51389
|
+
var STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
|
|
51390
|
+
var FETCH_TIMEOUT_MS = 3000;
|
|
51391
|
+
function compareVersions(a, b) {
|
|
51392
|
+
const stripSuffix = (v) => v.replace(/-.*$/, "");
|
|
51393
|
+
const pa = stripSuffix(a).split(".").map(Number);
|
|
51394
|
+
const pb = stripSuffix(b).split(".").map(Number);
|
|
51395
|
+
for (let i = 0;i < 3; i++) {
|
|
51396
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
51397
|
+
if (diff !== 0)
|
|
51398
|
+
return diff;
|
|
51399
|
+
}
|
|
51400
|
+
if (a === b)
|
|
51401
|
+
return 0;
|
|
51402
|
+
const aHasSuffix = a.includes("-");
|
|
51403
|
+
const bHasSuffix = b.includes("-");
|
|
51404
|
+
if (!aHasSuffix && bHasSuffix)
|
|
51405
|
+
return 1;
|
|
51406
|
+
if (aHasSuffix && !bHasSuffix)
|
|
51407
|
+
return -1;
|
|
51408
|
+
return a.localeCompare(b);
|
|
51409
|
+
}
|
|
51410
|
+
function isStale(checkedAt) {
|
|
51411
|
+
const checked = new Date(checkedAt).getTime();
|
|
51412
|
+
if (isNaN(checked))
|
|
51413
|
+
return true;
|
|
51414
|
+
return Date.now() - checked >= STALE_THRESHOLD_MS;
|
|
51415
|
+
}
|
|
51416
|
+
async function fetchLatestVersion() {
|
|
51417
|
+
try {
|
|
51418
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
51419
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
51420
|
+
});
|
|
51421
|
+
if (!response.ok)
|
|
51422
|
+
return null;
|
|
51423
|
+
const data = await response.json();
|
|
51424
|
+
return data.version ?? null;
|
|
51425
|
+
} catch {
|
|
51426
|
+
return null;
|
|
51427
|
+
}
|
|
51428
|
+
}
|
|
51429
|
+
async function checkForUpdate(currentVersion, cachePath, existingCache) {
|
|
51430
|
+
let cache = existingCache ?? null;
|
|
51431
|
+
if (cache === undefined && cachePath) {
|
|
51432
|
+
try {
|
|
51433
|
+
const cacheFile = Bun.file(`${cachePath}/version-check.json`);
|
|
51434
|
+
if (await cacheFile.exists()) {
|
|
51435
|
+
cache = await cacheFile.json();
|
|
51436
|
+
}
|
|
51437
|
+
} catch {
|
|
51438
|
+
cache = null;
|
|
51439
|
+
}
|
|
51440
|
+
}
|
|
51441
|
+
if (cache && !isStale(cache.checkedAt)) {
|
|
51442
|
+
return {
|
|
51443
|
+
hasUpdate: compareVersions(cache.latestVersion, currentVersion) > 0,
|
|
51444
|
+
latestVersion: cache.latestVersion
|
|
51445
|
+
};
|
|
51446
|
+
}
|
|
51447
|
+
const latestVersion = await fetchLatestVersion();
|
|
51448
|
+
if (!latestVersion)
|
|
51449
|
+
return null;
|
|
51450
|
+
if (cachePath) {
|
|
51451
|
+
try {
|
|
51452
|
+
const newCache = {
|
|
51453
|
+
latestVersion,
|
|
51454
|
+
checkedAt: new Date().toISOString()
|
|
51455
|
+
};
|
|
51456
|
+
await Bun.write(`${cachePath}/version-check.json`, JSON.stringify(newCache, null, 2));
|
|
51457
|
+
} catch {}
|
|
51458
|
+
}
|
|
51459
|
+
return {
|
|
51460
|
+
hasUpdate: compareVersions(latestVersion, currentVersion) > 0,
|
|
51461
|
+
latestVersion
|
|
51462
|
+
};
|
|
51463
|
+
}
|
|
51464
|
+
|
|
51465
|
+
// src/commands/upgrade.ts
|
|
51466
|
+
function formatAlreadyUpToDate(version) {
|
|
51467
|
+
return colors.success(`\u2713 Already up to date (v${version})`);
|
|
51468
|
+
}
|
|
51469
|
+
function formatUpgradeMessage(currentVersion, latestVersion) {
|
|
51470
|
+
return [
|
|
51471
|
+
colors.info(` Current version : v${currentVersion}`),
|
|
51472
|
+
colors.success(` Latest version : v${latestVersion}`)
|
|
51473
|
+
].join(`
|
|
51474
|
+
`);
|
|
51475
|
+
}
|
|
51476
|
+
function formatUpdateHint(latestVersion) {
|
|
51477
|
+
return colors.warn(`[INFO] dbcli v${latestVersion} available. Run "dbcli upgrade" to upgrade.`);
|
|
51478
|
+
}
|
|
51479
|
+
var upgradeCommand = new Command("upgrade").description("Check for updates and upgrade dbcli to the latest version").option("--check", "Only check for updates, do not upgrade").action(async (options) => {
|
|
51480
|
+
const configPath = upgradeCommand.parent?.opts().config ?? ".dbcli";
|
|
51481
|
+
const currentVersion = package_default.version;
|
|
51482
|
+
console.log(colors.bold("Checking for updates..."));
|
|
51483
|
+
let cachePath = null;
|
|
51484
|
+
try {
|
|
51485
|
+
const file = Bun.file(configPath);
|
|
51486
|
+
const isDir = !configPath.includes(".") || await file.exists() === false;
|
|
51487
|
+
if (isDir) {
|
|
51488
|
+
cachePath = configPath;
|
|
51489
|
+
}
|
|
51490
|
+
} catch {
|
|
51491
|
+
cachePath = null;
|
|
51492
|
+
}
|
|
51493
|
+
const result = await checkForUpdate(currentVersion, cachePath);
|
|
51494
|
+
if (!result) {
|
|
51495
|
+
console.error(colors.warn("\u26A0 Could not check for updates (network error or registry unavailable)"));
|
|
51496
|
+
process.exit(0);
|
|
51497
|
+
}
|
|
51498
|
+
if (!result.hasUpdate) {
|
|
51499
|
+
console.log(formatAlreadyUpToDate(currentVersion));
|
|
51500
|
+
process.exit(0);
|
|
51501
|
+
}
|
|
51502
|
+
console.log(colors.warn(`
|
|
51503
|
+
New version available: v${result.latestVersion}`));
|
|
51504
|
+
console.log(formatUpgradeMessage(currentVersion, result.latestVersion));
|
|
51505
|
+
console.log();
|
|
51506
|
+
if (options.check) {
|
|
51507
|
+
console.log(colors.dim(' Run "dbcli upgrade" to install the update.'));
|
|
51508
|
+
process.exit(0);
|
|
51509
|
+
}
|
|
51510
|
+
console.log(colors.bold("Upgrading..."));
|
|
51511
|
+
console.log(colors.dim(` bun add -g @carllee1983/dbcli@latest`));
|
|
51512
|
+
console.log();
|
|
51513
|
+
const proc = Bun.$`bun add -g @carllee1983/dbcli@latest`.nothrow();
|
|
51514
|
+
const result2 = await proc;
|
|
51515
|
+
if (result2.exitCode === 0) {
|
|
51516
|
+
console.log(colors.success(`
|
|
51517
|
+
\u2713 Successfully upgraded to v${result.latestVersion}`));
|
|
51518
|
+
} else {
|
|
51519
|
+
console.error(colors.error(`
|
|
51520
|
+
\u2717 Upgrade failed. Try running manually:`));
|
|
51521
|
+
console.error(colors.dim(" bun add -g @carllee1983/dbcli@latest"));
|
|
51522
|
+
process.exit(1);
|
|
51523
|
+
}
|
|
51524
|
+
});
|
|
51525
|
+
|
|
50756
51526
|
// src/cli.ts
|
|
50757
|
-
|
|
51527
|
+
import { join as join5 } from "path";
|
|
51528
|
+
var _bgVersionCheckResult;
|
|
51529
|
+
var program2 = new Command().name("dbcli").description("Database CLI for AI agents").version(package_default.version).option("--no-color", "Disable colored output").option("-v, --verbose", "Increase verbosity (-v verbose, -vv debug)", (_, prev) => prev + 1, 0).option("-q, --quiet", "Suppress non-essential output").option("--config <path>", "Path to .dbcli config file", ".dbcli");
|
|
51530
|
+
program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
51531
|
+
const opts = thisCommand.opts();
|
|
51532
|
+
if (opts.color === false) {
|
|
51533
|
+
process.env.NO_COLOR = "1";
|
|
51534
|
+
}
|
|
51535
|
+
let level = 1 /* NORMAL */;
|
|
51536
|
+
if (opts.quiet) {
|
|
51537
|
+
level = 0 /* QUIET */;
|
|
51538
|
+
} else if (opts.verbose >= 2) {
|
|
51539
|
+
level = 3 /* DEBUG */;
|
|
51540
|
+
} else if (opts.verbose >= 1) {
|
|
51541
|
+
level = 2 /* VERBOSE */;
|
|
51542
|
+
}
|
|
51543
|
+
setGlobalLogger(createLogger(level));
|
|
51544
|
+
const isUpgradeCommand = actionCommand.name() === "upgrade";
|
|
51545
|
+
if (!opts.quiet && !isUpgradeCommand) {
|
|
51546
|
+
const configPath = opts.config ?? ".dbcli";
|
|
51547
|
+
(async () => {
|
|
51548
|
+
try {
|
|
51549
|
+
let cache = null;
|
|
51550
|
+
try {
|
|
51551
|
+
const cacheFile = Bun.file(join5(configPath, "version-check.json"));
|
|
51552
|
+
if (await cacheFile.exists()) {
|
|
51553
|
+
cache = await cacheFile.json();
|
|
51554
|
+
}
|
|
51555
|
+
} catch {}
|
|
51556
|
+
const result = await checkForUpdate(package_default.version, configPath, cache);
|
|
51557
|
+
_bgVersionCheckResult = result;
|
|
51558
|
+
} catch {
|
|
51559
|
+
_bgVersionCheckResult = null;
|
|
51560
|
+
}
|
|
51561
|
+
})();
|
|
51562
|
+
}
|
|
51563
|
+
});
|
|
51564
|
+
program2.hook("postAction", () => {
|
|
51565
|
+
if (_bgVersionCheckResult?.hasUpdate) {
|
|
51566
|
+
process.stderr.write(formatUpdateHint(_bgVersionCheckResult.latestVersion) + `
|
|
51567
|
+
`);
|
|
51568
|
+
}
|
|
51569
|
+
});
|
|
50758
51570
|
program2.addCommand(initCommand);
|
|
50759
51571
|
program2.addCommand(listCommand);
|
|
50760
51572
|
program2.addCommand(schemaCommand);
|
|
@@ -50813,6 +51625,10 @@ program2.command("skill").description(t("skill.description")).option("--install
|
|
|
50813
51625
|
program2.addCommand(blacklistCommand);
|
|
50814
51626
|
program2.addCommand(checkCommand);
|
|
50815
51627
|
program2.addCommand(diffCommand);
|
|
51628
|
+
program2.addCommand(statusCommand);
|
|
51629
|
+
program2.addCommand(doctorCommand);
|
|
51630
|
+
program2.addCommand(completionCommand);
|
|
51631
|
+
program2.addCommand(upgradeCommand);
|
|
50816
51632
|
if (!process.argv.slice(2).length) {
|
|
50817
51633
|
program2.outputHelp();
|
|
50818
51634
|
}
|