@carllee1983/dbcli 1.45.1 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/dbcli.mdc +25 -5
- package/.cursor/skills/dbcli/reference.md +43 -9
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/skills/dbcli/SKILL.md +25 -5
- package/.github/skills/dbcli/reference.md +43 -9
- package/CHANGELOG.md +37 -0
- package/assets/SKILL.md +25 -5
- package/assets/SKILL.zh-TW.md +4 -2
- package/assets/reference.md +43 -9
- package/dist/cli.mjs +1424 -11784
- package/dist/core.d.ts +215 -3
- package/dist/core.mjs +200 -23
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/plugins/dbcli-agent/.codex-plugin/plugin.json +1 -1
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +25 -5
- package/plugins/dbcli-agent/skills/dbcli/reference.md +43 -9
- package/skills/dbcli/SKILL.md +25 -5
- package/skills/dbcli/reference.md +43 -9
package/dist/core.mjs
CHANGED
|
@@ -15022,12 +15022,40 @@ class MongoDBAdapter {
|
|
|
15022
15022
|
buildUri() {
|
|
15023
15023
|
if (this.options.uri)
|
|
15024
15024
|
return this.options.uri;
|
|
15025
|
-
const { user, password, host, port, database, authSource } = this.options;
|
|
15026
|
-
if (
|
|
15027
|
-
|
|
15028
|
-
|
|
15025
|
+
const { user, password, host, port, database, authSource, replicaSet, tls, srv } = this.options;
|
|
15026
|
+
if (!host) {
|
|
15027
|
+
throw new ConnectionError("UNKNOWN", "MongoDB host \u672A\u8A2D\u5B9A", [
|
|
15028
|
+
"\u8ACB\u586B\u5BEB host\uFF0C\u6216\u6539\u7528 uri \u6B04\u4F4D\u6307\u5B9A\u5B8C\u6574\u9023\u7DDA\u5B57\u4E32"
|
|
15029
|
+
]);
|
|
15030
|
+
}
|
|
15031
|
+
const isBracketedIpv6 = /^\[[0-9a-f:]+\]$/i.test(host);
|
|
15032
|
+
if (!isBracketedIpv6 && /[/@?#:\s\\]/.test(host)) {
|
|
15033
|
+
throw new ConnectionError("UNKNOWN", `MongoDB host \u542B\u6709\u975E\u6CD5\u5B57\u5143: ${host}`, [
|
|
15034
|
+
"host \u53EA\u61C9\u5305\u542B\u4E3B\u6A5F\u540D\u7A31\u6216 IP\uFF0C\u4E0D\u8981\u542B /\u3001@\u3001?\u3001#\u3001: \u6216\u7A7A\u767D",
|
|
15035
|
+
"\u57E0\u865F\u8ACB\u586B\u5728 port \u6B04\u4F4D\uFF0C\u4E0D\u8981\u4F75\u9032 host",
|
|
15036
|
+
"IPv6 \u4F4D\u5740\u8ACB\u52A0\u65B9\u62EC\u865F\uFF0C\u4F8B\u5982 [::1]",
|
|
15037
|
+
"\u82E5\u8981\u6307\u5B9A\u5B8C\u6574\u9023\u7DDA\u5B57\u4E32\uFF0C\u8ACB\u6539\u7528 uri \u6B04\u4F4D"
|
|
15038
|
+
]);
|
|
15029
15039
|
}
|
|
15030
|
-
|
|
15040
|
+
if (user && !password) {
|
|
15041
|
+
throw new ConnectionError("UNKNOWN", "\u5DF2\u6307\u5B9A user \u4F46\u672A\u63D0\u4F9B password", [
|
|
15042
|
+
'\u8ACB\u88DC\u4E0A password\uFF0C\u6216\u6539\u7528\u74B0\u5883\u8B8A\u6578\u53C3\u7167 {"$env": "..."}',
|
|
15043
|
+
"\u82E5\u78BA\u5B9A\u8981\u4EE5\u7121\u8A8D\u8B49\u65B9\u5F0F\u9023\u7DDA\uFF0C\u8ACB\u4E00\u4F75\u6E05\u7A7A user"
|
|
15044
|
+
]);
|
|
15045
|
+
}
|
|
15046
|
+
const userInfo = user ? `${encodeURIComponent(user)}:${encodeURIComponent(password)}@` : "";
|
|
15047
|
+
const scheme = srv ? "mongodb+srv://" : "mongodb://";
|
|
15048
|
+
const authority = srv ? host : `${host}:${port}`;
|
|
15049
|
+
const path = database ? `/${encodeURIComponent(database)}` : "/";
|
|
15050
|
+
const query = new URLSearchParams;
|
|
15051
|
+
if (user)
|
|
15052
|
+
query.set("authSource", authSource || "admin");
|
|
15053
|
+
if (replicaSet)
|
|
15054
|
+
query.set("replicaSet", replicaSet);
|
|
15055
|
+
if (tls !== undefined)
|
|
15056
|
+
query.set("tls", String(tls));
|
|
15057
|
+
const search = query.toString();
|
|
15058
|
+
return `${scheme}${userInfo}${authority}${path}${search ? `?${search}` : ""}`;
|
|
15031
15059
|
}
|
|
15032
15060
|
parseTxtRecords(records) {
|
|
15033
15061
|
const combined = records.flat().map((record) => record.replace(/^"|"$/g, "")).join("&");
|
|
@@ -15089,13 +15117,11 @@ class MongoDBAdapter {
|
|
|
15089
15117
|
return this.parseTxtRecords(payload.Answer.map((answer) => [answer.data]));
|
|
15090
15118
|
}
|
|
15091
15119
|
async buildResolvedUri() {
|
|
15092
|
-
|
|
15093
|
-
|
|
15120
|
+
const canonical = this.buildUri();
|
|
15121
|
+
if (!canonical.startsWith("mongodb+srv://")) {
|
|
15122
|
+
return canonical;
|
|
15094
15123
|
}
|
|
15095
|
-
|
|
15096
|
-
return this.options.uri;
|
|
15097
|
-
}
|
|
15098
|
-
const url = new URL(this.options.uri);
|
|
15124
|
+
const url = new URL(canonical);
|
|
15099
15125
|
const hosts = await this.resolveSrvHosts(url.hostname);
|
|
15100
15126
|
const txtOptions = await this.resolveTxtOptions(url.hostname);
|
|
15101
15127
|
const query = new URLSearchParams(url.searchParams);
|
|
@@ -15115,6 +15141,37 @@ class MongoDBAdapter {
|
|
|
15115
15141
|
const search = query.toString();
|
|
15116
15142
|
return `mongodb://${userInfo}${hosts.join(",")}${path}${search ? `?${search}` : ""}`;
|
|
15117
15143
|
}
|
|
15144
|
+
connectionHints(error, message) {
|
|
15145
|
+
const AUTH_HINTS = [
|
|
15146
|
+
"\u8A8D\u8B49\u5931\u6557\uFF1A\u8ACB\u78BA\u8A8D user / password \u6B63\u78BA",
|
|
15147
|
+
"\u8ACB\u78BA\u8A8D authSource \u6307\u5411\u5B58\u653E\u8A72\u5E33\u865F\u7684\u8CC7\u6599\u5EAB\uFF08Atlas \u8207\u591A\u6578\u81EA\u67B6\u74B0\u5883\u70BA admin\uFF09"
|
|
15148
|
+
];
|
|
15149
|
+
const DNS_HINTS = [
|
|
15150
|
+
"DNS/SRV \u89E3\u6790\u5931\u6557\uFF1A\u8ACB\u78BA\u8A8D host \u70BA SRV \u7DB2\u57DF\uFF0C\u4E14 srv \u8A2D\u5B9A\u8207\u5B83\u4E00\u81F4",
|
|
15151
|
+
"\u82E5\u8A72\u4E3B\u6A5F\u4E0D\u662F SRV \u7DB2\u57DF\uFF0C\u8ACB\u95DC\u9589 srv \u4E26\u6539\u586B host \u8207 port",
|
|
15152
|
+
"\u8ACB\u78BA\u8A8D\u672C\u6A5F DNS \u6216\u7DB2\u8DEF\uFF08VPN\u3001\u516C\u53F8\u7DB2\u8DEF\uFF09\u5141\u8A31 SRV \u67E5\u8A62"
|
|
15153
|
+
];
|
|
15154
|
+
const TLS_HINTS = [
|
|
15155
|
+
"TLS \u63E1\u624B\u5931\u6557\uFF1A\u8ACB\u78BA\u8A8D tls \u6B04\u4F4D\u8A2D\u5B9A\u8207\u4F3A\u670D\u5668\u4E00\u81F4",
|
|
15156
|
+
"\u81EA\u7C3D\u6191\u8B49\u74B0\u5883\u9700\u8981\u5728\u4F3A\u670D\u5668\u7AEF\u6216\u7CFB\u7D71\u4FE1\u4EFB\u93C8\u4E2D\u5B89\u88DD CA \u6191\u8B49"
|
|
15157
|
+
];
|
|
15158
|
+
const err = error;
|
|
15159
|
+
const causeCode = String(err?.cause?.code ?? err?.code ?? "");
|
|
15160
|
+
if (err?.code === 18 || err?.codeName === "AuthenticationFailed")
|
|
15161
|
+
return AUTH_HINTS;
|
|
15162
|
+
if (["ENOTFOUND", "EAI_AGAIN"].includes(causeCode))
|
|
15163
|
+
return DNS_HINTS;
|
|
15164
|
+
if (causeCode.startsWith("ERR_TLS") || causeCode.startsWith("SELF_SIGNED"))
|
|
15165
|
+
return TLS_HINTS;
|
|
15166
|
+
if (/authentication failed|not authorized|bad auth/i.test(message))
|
|
15167
|
+
return AUTH_HINTS;
|
|
15168
|
+
if (/querySrv|getaddrinfo (ENOTFOUND|EAI_AGAIN)/i.test(message))
|
|
15169
|
+
return DNS_HINTS;
|
|
15170
|
+
if (/unable to verify the first certificate|self.signed certificate|certificate has expired|ERR_TLS/i.test(message)) {
|
|
15171
|
+
return TLS_HINTS;
|
|
15172
|
+
}
|
|
15173
|
+
return ["\u8ACB\u78BA\u8A8D MongoDB \u670D\u52D9\u6B63\u5728\u57F7\u884C", "\u8ACB\u78BA\u8A8D\u9023\u7DDA\u8A2D\u5B9A\uFF08URI \u6216 host/port\uFF09\u6B63\u78BA"];
|
|
15174
|
+
}
|
|
15118
15175
|
getDatabase() {
|
|
15119
15176
|
if (!this.client) {
|
|
15120
15177
|
throw new ConnectionError("UNKNOWN", "\u5C1A\u672A\u9023\u7DDA\uFF0C\u8ACB\u5148\u547C\u53EB connect()", []);
|
|
@@ -15130,10 +15187,7 @@ class MongoDBAdapter {
|
|
|
15130
15187
|
} catch (err) {
|
|
15131
15188
|
const message = err.message ?? "Unknown error";
|
|
15132
15189
|
const code = message.includes("ECONNREFUSED") ? "ECONNREFUSED" : message.includes("ETIMEDOUT") ? "ETIMEDOUT" : "UNKNOWN";
|
|
15133
|
-
throw new ConnectionError(code, `MongoDB \u9023\u7DDA\u5931\u6557: ${message}`,
|
|
15134
|
-
"\u8ACB\u78BA\u8A8D MongoDB \u670D\u52D9\u6B63\u5728\u57F7\u884C",
|
|
15135
|
-
"\u8ACB\u78BA\u8A8D\u9023\u7DDA\u8A2D\u5B9A\uFF08URI \u6216 host/port\uFF09\u6B63\u78BA"
|
|
15136
|
-
]);
|
|
15190
|
+
throw new ConnectionError(code, `MongoDB \u9023\u7DDA\u5931\u6557: ${message}`, this.connectionHints(err, message));
|
|
15137
15191
|
}
|
|
15138
15192
|
}
|
|
15139
15193
|
async disconnect() {
|
|
@@ -16671,9 +16725,22 @@ class ElasticsearchAdapter {
|
|
|
16671
16725
|
}
|
|
16672
16726
|
}
|
|
16673
16727
|
|
|
16728
|
+
// src/utils/connection-timeout.ts
|
|
16729
|
+
var _globalConnectionTimeout;
|
|
16730
|
+
function resolveConnectionTimeout(configured) {
|
|
16731
|
+
return _globalConnectionTimeout ?? configured;
|
|
16732
|
+
}
|
|
16733
|
+
function withResolvedTimeout(options) {
|
|
16734
|
+
const timeout = resolveConnectionTimeout(options.timeout);
|
|
16735
|
+
if (timeout === options.timeout)
|
|
16736
|
+
return options;
|
|
16737
|
+
return { ...options, timeout };
|
|
16738
|
+
}
|
|
16739
|
+
|
|
16674
16740
|
// src/adapters/factory.ts
|
|
16675
16741
|
class AdapterFactory {
|
|
16676
|
-
static createSqlAdapter(
|
|
16742
|
+
static createSqlAdapter(rawOptions) {
|
|
16743
|
+
const options = withResolvedTimeout(rawOptions);
|
|
16677
16744
|
switch (options.system) {
|
|
16678
16745
|
case "postgresql":
|
|
16679
16746
|
return new PostgreSQLAdapter(options);
|
|
@@ -16684,7 +16751,8 @@ class AdapterFactory {
|
|
|
16684
16751
|
throw new Error(`createSqlAdapter requires a SQL system, got: ${options.system}`);
|
|
16685
16752
|
}
|
|
16686
16753
|
}
|
|
16687
|
-
static createQueryableAdapter(
|
|
16754
|
+
static createQueryableAdapter(rawOptions) {
|
|
16755
|
+
const options = withResolvedTimeout(rawOptions);
|
|
16688
16756
|
switch (options.system) {
|
|
16689
16757
|
case "mongodb":
|
|
16690
16758
|
return new MongoDBAdapter(options);
|
|
@@ -16716,10 +16784,11 @@ class AdapterFactory {
|
|
|
16716
16784
|
}
|
|
16717
16785
|
return AdapterFactory.createQueryableAdapter(options);
|
|
16718
16786
|
}
|
|
16719
|
-
static createRedisAdapter(
|
|
16720
|
-
if (
|
|
16787
|
+
static createRedisAdapter(rawOptions, blacklistRules = [], maskRules = []) {
|
|
16788
|
+
if (rawOptions.system !== "redis") {
|
|
16721
16789
|
throw new Error("createRedisAdapter requires system: redis");
|
|
16722
16790
|
}
|
|
16791
|
+
const options = withResolvedTimeout(rawOptions);
|
|
16723
16792
|
const adapter = new RedisAdapter(options);
|
|
16724
16793
|
adapter.setBlacklistRules(blacklistRules);
|
|
16725
16794
|
adapter.setMaskRules(maskRules);
|
|
@@ -21797,6 +21866,11 @@ var StringOrEnvRef = exports_external.union([exports_external.string().min(1), E
|
|
|
21797
21866
|
var NumberOrEnvRef = exports_external.union([exports_external.number().int().min(1).max(65535), EnvRefSchema]);
|
|
21798
21867
|
var OptStringOrEnvRef = exports_external.union([exports_external.string(), EnvRefSchema]).optional().default("");
|
|
21799
21868
|
var OptNumberOrEnvRef = exports_external.union([exports_external.number().int(), EnvRefSchema]).optional().default(27017);
|
|
21869
|
+
var MIN_CONNECTION_TIMEOUT_MS = 100;
|
|
21870
|
+
var MAX_CONNECTION_TIMEOUT_MS = 600000;
|
|
21871
|
+
var TimeoutField = {
|
|
21872
|
+
timeout: exports_external.number().int().min(MIN_CONNECTION_TIMEOUT_MS).max(MAX_CONNECTION_TIMEOUT_MS).optional()
|
|
21873
|
+
};
|
|
21800
21874
|
var MongoDBConnectionConfigSchema = exports_external.object({
|
|
21801
21875
|
system: exports_external.literal("mongodb"),
|
|
21802
21876
|
uri: exports_external.union([exports_external.string(), EnvRefSchema]).optional(),
|
|
@@ -21804,7 +21878,12 @@ var MongoDBConnectionConfigSchema = exports_external.object({
|
|
|
21804
21878
|
port: OptNumberOrEnvRef,
|
|
21805
21879
|
user: OptStringOrEnvRef,
|
|
21806
21880
|
password: OptStringOrEnvRef,
|
|
21807
|
-
database: OptStringOrEnvRef
|
|
21881
|
+
database: OptStringOrEnvRef,
|
|
21882
|
+
authSource: exports_external.union([exports_external.string(), EnvRefSchema]).optional(),
|
|
21883
|
+
replicaSet: exports_external.union([exports_external.string(), EnvRefSchema]).optional(),
|
|
21884
|
+
tls: exports_external.boolean().optional(),
|
|
21885
|
+
srv: exports_external.boolean().optional().default(false),
|
|
21886
|
+
...TimeoutField
|
|
21808
21887
|
});
|
|
21809
21888
|
var SqlConnectionConfigSchema = exports_external.object({
|
|
21810
21889
|
system: exports_external.enum(["postgresql", "mysql", "mariadb"]),
|
|
@@ -21812,7 +21891,8 @@ var SqlConnectionConfigSchema = exports_external.object({
|
|
|
21812
21891
|
port: NumberOrEnvRef,
|
|
21813
21892
|
user: StringOrEnvRef,
|
|
21814
21893
|
password: exports_external.union([exports_external.string(), EnvRefSchema]).default(""),
|
|
21815
|
-
database: StringOrEnvRef
|
|
21894
|
+
database: StringOrEnvRef,
|
|
21895
|
+
...TimeoutField
|
|
21816
21896
|
});
|
|
21817
21897
|
var RedisConnectionConfigSchema = exports_external.object({
|
|
21818
21898
|
system: exports_external.literal("redis"),
|
|
@@ -21820,7 +21900,8 @@ var RedisConnectionConfigSchema = exports_external.object({
|
|
|
21820
21900
|
port: NumberOrEnvRef,
|
|
21821
21901
|
user: OptStringOrEnvRef,
|
|
21822
21902
|
password: exports_external.union([exports_external.string(), EnvRefSchema]).optional().default(""),
|
|
21823
|
-
database: OptStringOrEnvRef
|
|
21903
|
+
database: OptStringOrEnvRef,
|
|
21904
|
+
...TimeoutField
|
|
21824
21905
|
});
|
|
21825
21906
|
var ElasticsearchConnectionConfigSchema = exports_external.object({
|
|
21826
21907
|
system: exports_external.literal("elasticsearch"),
|
|
@@ -21834,7 +21915,8 @@ var ElasticsearchConnectionConfigSchema = exports_external.object({
|
|
|
21834
21915
|
cloudId: exports_external.union([exports_external.string(), EnvRefSchema]).optional(),
|
|
21835
21916
|
apiKey: exports_external.union([exports_external.string(), EnvRefSchema]).optional(),
|
|
21836
21917
|
caPath: exports_external.string().optional(),
|
|
21837
|
-
rejectUnauthorized: exports_external.boolean().optional().default(true)
|
|
21918
|
+
rejectUnauthorized: exports_external.boolean().optional().default(true),
|
|
21919
|
+
...TimeoutField
|
|
21838
21920
|
});
|
|
21839
21921
|
var ConnectionConfigSchema = exports_external.union([
|
|
21840
21922
|
SqlConnectionConfigSchema,
|
|
@@ -21924,6 +22006,89 @@ var DbcliConfigV2Schema = exports_external.object({
|
|
|
21924
22006
|
path: ["default"]
|
|
21925
22007
|
});
|
|
21926
22008
|
|
|
22009
|
+
// src/utils/config-error-format.ts
|
|
22010
|
+
var SUPPORTED_SYSTEMS = [
|
|
22011
|
+
"postgresql",
|
|
22012
|
+
"mysql",
|
|
22013
|
+
"mariadb",
|
|
22014
|
+
"mongodb",
|
|
22015
|
+
"redis",
|
|
22016
|
+
"elasticsearch"
|
|
22017
|
+
];
|
|
22018
|
+
function isConnectionNode(raw, path) {
|
|
22019
|
+
const last = path[path.length - 1];
|
|
22020
|
+
const parent = path[path.length - 2];
|
|
22021
|
+
if (last !== "connection" && parent !== "connections")
|
|
22022
|
+
return false;
|
|
22023
|
+
const node = valueAtPath(raw, path);
|
|
22024
|
+
return typeof node === "object" && node !== null && !Array.isArray(node);
|
|
22025
|
+
}
|
|
22026
|
+
function valueAtPath(raw, path) {
|
|
22027
|
+
let current = raw;
|
|
22028
|
+
for (const key of path) {
|
|
22029
|
+
if (current == null || typeof current !== "object")
|
|
22030
|
+
return;
|
|
22031
|
+
current = current[key];
|
|
22032
|
+
}
|
|
22033
|
+
return current;
|
|
22034
|
+
}
|
|
22035
|
+
function declaredSystem(raw, path) {
|
|
22036
|
+
const node = valueAtPath(raw, path);
|
|
22037
|
+
if (node == null || typeof node !== "object")
|
|
22038
|
+
return;
|
|
22039
|
+
const system = node.system;
|
|
22040
|
+
return typeof system === "string" ? system : undefined;
|
|
22041
|
+
}
|
|
22042
|
+
function branchMatchesSystem(issues) {
|
|
22043
|
+
return !issues.some((issue) => issue.path[issue.path.length - 1] === "system" && (issue.code === "invalid_literal" || issue.code === "invalid_enum_value" || issue.code === "invalid_type"));
|
|
22044
|
+
}
|
|
22045
|
+
function formatPath(path) {
|
|
22046
|
+
return path.length > 0 ? path.join(".") : "(root)";
|
|
22047
|
+
}
|
|
22048
|
+
function flattenIssues(issues, raw, basePath = []) {
|
|
22049
|
+
const flat = [];
|
|
22050
|
+
for (const issue of issues) {
|
|
22051
|
+
const fullPath = [...basePath, ...issue.path];
|
|
22052
|
+
if (issue.code === "invalid_union") {
|
|
22053
|
+
const system = declaredSystem(raw, fullPath);
|
|
22054
|
+
const branches = issue.unionErrors.map((error) => error.issues);
|
|
22055
|
+
const candidates = system ? branches.filter((branchIssues) => branchMatchesSystem(branchIssues)) : [];
|
|
22056
|
+
if (isConnectionNode(raw, fullPath) && candidates.length === 0) {
|
|
22057
|
+
flat.push({
|
|
22058
|
+
path: formatPath([...fullPath, "system"]),
|
|
22059
|
+
message: `must be one of ${SUPPORTED_SYSTEMS.join(" | ")}` + (system === undefined ? " (missing)" : ` (received '${system}')`)
|
|
22060
|
+
});
|
|
22061
|
+
continue;
|
|
22062
|
+
}
|
|
22063
|
+
const chosen = candidates[0] ?? branches.reduce((best, current) => current.length < best.length ? current : best, branches[0] ?? []);
|
|
22064
|
+
flat.push(...flattenIssues(chosen, raw, basePath));
|
|
22065
|
+
continue;
|
|
22066
|
+
}
|
|
22067
|
+
flat.push({ path: formatPath(fullPath), message: issue.message });
|
|
22068
|
+
}
|
|
22069
|
+
return flat;
|
|
22070
|
+
}
|
|
22071
|
+
function dedupe(issues) {
|
|
22072
|
+
const seen = new Set;
|
|
22073
|
+
return issues.filter((issue) => {
|
|
22074
|
+
const key = `${issue.path}\x00${issue.message}`;
|
|
22075
|
+
if (seen.has(key))
|
|
22076
|
+
return false;
|
|
22077
|
+
seen.add(key);
|
|
22078
|
+
return true;
|
|
22079
|
+
});
|
|
22080
|
+
}
|
|
22081
|
+
function formatConfigValidationError(error, raw) {
|
|
22082
|
+
const issues = dedupe(flattenIssues(error.issues, raw));
|
|
22083
|
+
if (issues.length === 0)
|
|
22084
|
+
return error.message;
|
|
22085
|
+
return issues.map((issue) => ` - ${issue.path}: ${issue.message}`).join(`
|
|
22086
|
+
`);
|
|
22087
|
+
}
|
|
22088
|
+
function isZodError(error) {
|
|
22089
|
+
return error instanceof Error && error.name === "ZodError" && Array.isArray(error.issues);
|
|
22090
|
+
}
|
|
22091
|
+
|
|
21927
22092
|
// src/agent-core/env-loader.ts
|
|
21928
22093
|
import { readFile as readFile3 } from "fs/promises";
|
|
21929
22094
|
function parseEnvContent(content) {
|
|
@@ -22130,6 +22295,7 @@ function parseEnvPassword(content) {
|
|
|
22130
22295
|
var configModule = {
|
|
22131
22296
|
async read(path, connectionName, options = {}) {
|
|
22132
22297
|
const effectiveConnectionName = connectionName ?? _globalConnectionName;
|
|
22298
|
+
let rawForDiagnostics;
|
|
22133
22299
|
try {
|
|
22134
22300
|
const binding = await readProjectBinding(path);
|
|
22135
22301
|
const storagePath = await resolveConfigStoragePath(path);
|
|
@@ -22155,6 +22321,7 @@ var configModule = {
|
|
|
22155
22321
|
const content = await configFile.text();
|
|
22156
22322
|
await assertConfigIntegrity(storagePath, content, { requireRecord: true });
|
|
22157
22323
|
const config = JSON.parse(content);
|
|
22324
|
+
rawForDiagnostics = config;
|
|
22158
22325
|
if (detectConfigVersion(config) === 2) {
|
|
22159
22326
|
const v2Config = DbcliConfigV2Schema.parse(config);
|
|
22160
22327
|
const resolved = resolveConnection(v2Config, effectiveConnectionName);
|
|
@@ -22235,6 +22402,7 @@ var configModule = {
|
|
|
22235
22402
|
assertNoConnectionSelectorOnV1(effectiveConnectionName);
|
|
22236
22403
|
const content = await file.text();
|
|
22237
22404
|
const raw = JSON.parse(content);
|
|
22405
|
+
rawForDiagnostics = raw;
|
|
22238
22406
|
const resolved = resolveEnvReferences(raw, process.env);
|
|
22239
22407
|
return DbcliConfigSchema.parse(resolved);
|
|
22240
22408
|
}
|
|
@@ -22245,6 +22413,10 @@ var configModule = {
|
|
|
22245
22413
|
} catch (error) {
|
|
22246
22414
|
if (error instanceof ConfigError)
|
|
22247
22415
|
throw error;
|
|
22416
|
+
if (isZodError(error)) {
|
|
22417
|
+
throw new ConfigError(`Failed to read .dbcli config: \u8A2D\u5B9A\u5167\u5BB9\u4E0D\u7B26\u5408\u7D50\u69CB
|
|
22418
|
+
${formatConfigValidationError(error, rawForDiagnostics)}`);
|
|
22419
|
+
}
|
|
22248
22420
|
if (error instanceof Error && error.message.includes("JSON")) {
|
|
22249
22421
|
throw new ConfigError(`Failed to parse .dbcli file: ${error.message}`);
|
|
22250
22422
|
}
|
|
@@ -22255,6 +22427,10 @@ var configModule = {
|
|
|
22255
22427
|
try {
|
|
22256
22428
|
return DbcliConfigSchema.parse(raw);
|
|
22257
22429
|
} catch (error) {
|
|
22430
|
+
if (isZodError(error)) {
|
|
22431
|
+
throw new ConfigError(`Invalid .dbcli config structure:
|
|
22432
|
+
${formatConfigValidationError(error, raw)}`);
|
|
22433
|
+
}
|
|
22258
22434
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
22259
22435
|
throw new ConfigError(`Invalid .dbcli config structure: ${errorMessage}`);
|
|
22260
22436
|
}
|
|
@@ -22355,6 +22531,7 @@ var KEEP_VALUE_FLAGS = new Set([
|
|
|
22355
22531
|
"--min-severity",
|
|
22356
22532
|
"--output",
|
|
22357
22533
|
"--limit",
|
|
22534
|
+
"--timeout",
|
|
22358
22535
|
"--collection",
|
|
22359
22536
|
"--index",
|
|
22360
22537
|
"--fields",
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -77,7 +77,10 @@ When reporting a check's outcome use the vocabulary `verified` (evidence matched
|
|
|
77
77
|
`not_verified` (check ran and contradicted) / `indeterminate` (ran but ambiguous) /
|
|
78
78
|
`blocked` (could not run due to config, permission, schema, placeholder, or safety gate).
|
|
79
79
|
|
|
80
|
-
Prefer `--format json` for agent-friendly output.
|
|
80
|
+
Prefer `--format json` for agent-friendly output. Diagnostics (auto-limit notices,
|
|
81
|
+
warnings) go to stderr so stdout stays parseable — when piping JSON into a parser,
|
|
82
|
+
use `2>/dev/null` or leave stderr alone. **Never `2>&1`**: it merges those lines back
|
|
83
|
+
into stdout and the parse fails.
|
|
81
84
|
|
|
82
85
|
## Agent Task Packs
|
|
83
86
|
|
|
@@ -229,11 +232,13 @@ dbcli init --system postgresql --host localhost --port 5432 \
|
|
|
229
232
|
# Reuse an existing .env (DATABASE_URL=postgresql://user:pw@host:5432/db)
|
|
230
233
|
dbcli init # parses .env in cwd
|
|
231
234
|
|
|
232
|
-
# MongoDB —
|
|
235
|
+
# MongoDB — field-by-field (no auth = omit --user/--password)
|
|
236
|
+
dbcli init --system mongodb --host localhost --port 27017 --name mydb
|
|
237
|
+
dbcli init --system mongodb --host localhost --port 27017 \
|
|
238
|
+
--user admin --password '<secret>' --auth-source admin --name mydb
|
|
239
|
+
# MongoDB — full URI (advanced escape hatch: multi-host, non-standard driver options)
|
|
233
240
|
dbcli init --system mongodb \
|
|
234
241
|
--uri "mongodb+srv://user:pw@cluster.example.mongodb.net/mydb?authSource=admin"
|
|
235
|
-
# MongoDB — discrete params (no auth = omit --user/--password)
|
|
236
|
-
dbcli init --system mongodb --host localhost --port 27017 --name mydb
|
|
237
242
|
|
|
238
243
|
# Redis — `--name` is the LOGICAL DB INDEX ("0".."15"), not a database name
|
|
239
244
|
dbcli init --system redis --host localhost --port 6379 --password '<secret>' --name 0
|
|
@@ -301,10 +306,25 @@ as a `$env` ref. In a **non-interactive / CI** run you **must** pass all five `-
|
|
|
301
306
|
flags; otherwise `init` exits with an error — it never silently falls back to plaintext.
|
|
302
307
|
`--env-file <path>` is the path to the env file, independent of the `$env` key names.
|
|
303
308
|
|
|
309
|
+
**MongoDB is the exception**: only `--env-host` is required non-interactively.
|
|
310
|
+
`--env-port` / `--env-user` / `--env-password` / `--env-database` are optional — an
|
|
311
|
+
omitted one is written as a literal value (empty string for `user` / `password`, the
|
|
312
|
+
resolved value for `port` / `database`) instead of an `$env` ref, so a field the
|
|
313
|
+
connection never needed doesn't later fail closed on an undefined variable. `init`
|
|
314
|
+
also skips the connection test in this mode regardless of `--skip-test` — the `$env`
|
|
315
|
+
refs have no value to connect with yet.
|
|
316
|
+
|
|
304
317
|
### Common gotchas
|
|
305
318
|
|
|
306
319
|
- **MongoDB `mongodb+srv://`** — `dbcli doctor` reports whether SRV resolves
|
|
307
320
|
natively or via the DoH fallback; useful when the runtime restricts DNS.
|
|
321
|
+
- **MongoDB `authSource` / `replicaSet` / `tls` / `srv`** — `init` asks for
|
|
322
|
+
these interactively (`authSource` only when a user is set; `replicaSet` /
|
|
323
|
+
`tls` behind an "advanced options?" prompt); `--auth-source <db>` is the
|
|
324
|
+
only one with a dedicated non-interactive flag, so set `replicaSet` / `tls`
|
|
325
|
+
interactively or edit `.dbcli` afterward. If a config has both `uri` and
|
|
326
|
+
per-field values, `uri` wins silently — `dbcli doctor` flags this and also
|
|
327
|
+
warns when `srv: true` is combined with a non-default `port`.
|
|
308
328
|
- **MySQL/Postgres password with `@` `:` `/`** — when using `DATABASE_URL`,
|
|
309
329
|
percent-encode (`@` → `%40`); discrete `--password` flags do not need encoding.
|
|
310
330
|
- **Redis `--name`** — accepts only the logical DB index string; non-numeric
|
|
@@ -536,4 +556,4 @@ schema. Raw `query` / `export` invocations render a sortable table only.
|
|
|
536
556
|
- Blacklisted tables and columns are redacted from query output.
|
|
537
557
|
- `schema` reports `estimatedRowCount` and `sizeCategory` (small / medium / large / huge). For large/huge tables add `WHERE` or `LIMIT` — bands in reference.md.
|
|
538
558
|
- `doctor` on `mongodb+srv://` reports whether SRV resolves natively or through the DoH fallback — useful when the runtime restricts DNS.
|
|
539
|
-
- **Global flags:** `--version`, `--config <path>`, `--global`, `--use <name>`, `-v` / `--verbose` / `-vv`, `-q` / `--quiet`, `--no-color` (also honours `NO_COLOR`). Root-level flags must precede the command unless the command explicitly declares a command-level option.
|
|
559
|
+
- **Global flags:** `--version`, `--config <path>`, `--global`, `--use <name>`, `--timeout <ms>`, `-v` / `--verbose` / `-vv`, `-q` / `--quiet`, `--no-color` (also honours `NO_COLOR`). Root-level flags must precede the command unless the command explicitly declares a command-level option.
|
|
@@ -19,6 +19,28 @@ command-level option is only valid after the command that declares it.
|
|
|
19
19
|
| `--config <path>` | Select the `.dbcli` configuration path. |
|
|
20
20
|
| `--global` | Select the user-global registry at `~/.config/dbcli/config.json` instead of the current project's `.dbcli` config. Place it before the command path. |
|
|
21
21
|
| `--use <connection>` | Select a named connection for this invocation; place it before the command path unless that command explicitly lists a command-level `--use`. |
|
|
22
|
+
| `--timeout <ms>` | Connection timeout in milliseconds (integer, 100–600000), overriding the connection config's `timeout` field for this invocation. Applies to every engine adapter. Without either the flag or the config field, adapters fall back to their built-in 5000ms default. |
|
|
23
|
+
|
|
24
|
+
`--timeout` is applied only when the adapter is constructed for this invocation — it is
|
|
25
|
+
never written back to `config.json`. Set the connection's `timeout` field instead for a
|
|
26
|
+
value that persists across runs. On PostgreSQL, the same value is also used as the
|
|
27
|
+
session's `statement_timeout` (not just the connection timeout), so a low value can cut
|
|
28
|
+
off a long-running query with an error that looks like a connection timeout; the 100ms
|
|
29
|
+
floor exists specifically to keep that failure mode from being too easy to trigger.
|
|
30
|
+
Elasticsearch applies its timeout per request rather than once for the whole connection.
|
|
31
|
+
The `timeout` field itself always takes a literal number — unlike other connection
|
|
32
|
+
fields, it does not accept an `{"$env": "..."}` reference.
|
|
33
|
+
|
|
34
|
+
### Redirecting output
|
|
35
|
+
|
|
36
|
+
Results go to stdout; diagnostics (auto-limit notices, warnings, update hints) go to
|
|
37
|
+
stderr. That split is what keeps `--format json` machine-parseable, so do not collapse
|
|
38
|
+
it with `2>&1` — the diagnostic lines land in front of the JSON document and the parse
|
|
39
|
+
fails. Pipe stdout alone, or add `2>/dev/null` when the diagnostics are not wanted:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
dbcli query '{}' --collection events --format json 2>/dev/null | jq '.rows | length'
|
|
43
|
+
```
|
|
22
44
|
|
|
23
45
|
## Commands
|
|
24
46
|
|
|
@@ -32,10 +54,11 @@ dbcli init --system mysql --host localhost --port 3306 --user root --name mydb
|
|
|
32
54
|
dbcli init --use-env-refs # Store env var references
|
|
33
55
|
dbcli init --no-interactive --force # Non-interactive mode
|
|
34
56
|
|
|
35
|
-
# MongoDB
|
|
36
|
-
dbcli init --system mongodb --
|
|
37
|
-
dbcli init --system mongodb --host localhost --port 27017 --user admin --password secret --name mydb
|
|
57
|
+
# MongoDB — field-by-field (primary path, same shape as SQL)
|
|
58
|
+
dbcli init --system mongodb --host localhost --port 27017 --user admin --password secret --auth-source admin --name mydb
|
|
38
59
|
dbcli init --system mongodb --host localhost --port 27017 --name mydb # No auth
|
|
60
|
+
# MongoDB — full URI (advanced fallback: multi-host, non-standard driver options)
|
|
61
|
+
dbcli init --system mongodb --uri "mongodb://user:pass@host:27017/mydb?authSource=admin"
|
|
39
62
|
|
|
40
63
|
# Redis (database = logical DB index)
|
|
41
64
|
dbcli init --system redis --host localhost --port 6379
|
|
@@ -62,7 +85,7 @@ dbcli --global use --list
|
|
|
62
85
|
|
|
63
86
|
**Environment-reference options:** `--env-host <var>`, `--env-port <var>`, `--env-user <var>`, `--env-password <var>`, `--env-database <var>`
|
|
64
87
|
|
|
65
|
-
**MongoDB-specific options:** `--uri <uri>` (full connection URI), `--auth-source <db>` (auth database, default: `admin` when user/password set)
|
|
88
|
+
**MongoDB-specific options:** `--uri <uri>` (full connection URI — advanced fallback), `--auth-source <db>` (auth database, default: `admin` when user/password set). Interactive `init` also asks for `replicaSet` and `tls` under an "advanced options?" prompt; there is no dedicated non-interactive flag for either yet — set them interactively or edit `.dbcli` afterward. `srv` (boolean, builds `mongodb+srv://` and resolves hosts via DNS SRV, ignoring `port`) is asked right after `host`, before `port`, since it decides whether `port` is even relevant.
|
|
66
89
|
|
|
67
90
|
**Elasticsearch-specific options:** `--cloud-id <id>` (Elastic Cloud), `--api-key <key>` (ApiKey auth). Other ES fields (`nodes[]`, `protocol`, `caPath`, `rejectUnauthorized`) can be edited directly in `.dbcli`.
|
|
68
91
|
|
|
@@ -117,6 +140,10 @@ agent mode disabled. A host that needs protection from a same-user hostile
|
|
|
117
140
|
process can set `DBCLI_CONFIG_INTEGRITY_ANCHOR_DIR` to a protected or read-only
|
|
118
141
|
directory; trusted writes publish detached digests there.
|
|
119
142
|
|
|
143
|
+
When a connection's config fails schema validation, dbcli reports the specific field
|
|
144
|
+
path(s) that are wrong for that connection's declared `system` — not the raw Zod union
|
|
145
|
+
error tree — so a broken `.dbcli` can be fixed without guessing which branch applies.
|
|
146
|
+
|
|
120
147
|
### list
|
|
121
148
|
|
|
122
149
|
List all tables (SQL), collections (MongoDB), keys (Redis), or indices (Elasticsearch).
|
|
@@ -2025,7 +2052,9 @@ dbcli doctor --format json # JSON output for AI agents
|
|
|
2025
2052
|
- Configuration: config file exists/valid, permission level, blacklist completeness (detects unprotected sensitive columns)
|
|
2026
2053
|
- Connection & Data: database connectivity, schema cache freshness (warns if > 7 days), large table warnings (> 1M rows)
|
|
2027
2054
|
|
|
2028
|
-
> **MongoDB SRV diagnostics:** When the active connection uses `mongodb+srv
|
|
2055
|
+
> **MongoDB SRV diagnostics:** When the active connection uses `mongodb+srv://` (via a full `uri` or the per-field `srv: true`), `doctor` reports whether the current runtime can resolve SRV records directly or only through the DNS-over-HTTPS fallback used by dbcli. This helps spot execution-environment DNS restrictions even when Compass can connect.
|
|
2056
|
+
|
|
2057
|
+
> **MongoDB connection-field warnings:** `doctor` also warns when a config has both `uri` and per-field values (`host` / `user`) present — `uri` silently wins and the per-field values are ignored — and when `srv: true` is combined with a non-default `port`, since SRV records carry their own ports.
|
|
2029
2058
|
|
|
2030
2059
|
**Exit code:** 0 if all pass or warnings only, 1 if any error
|
|
2031
2060
|
**Options:** `--format <text|json>`, `--remediation`
|
|
@@ -2600,7 +2629,9 @@ Parser behaviour (`src/core/saved-queries/parser.ts::normaliseVisual`):
|
|
|
2600
2629
|
|
|
2601
2630
|
MongoDB connections use a JSON-based query model instead of SQL. Treat MongoDB support as a narrower document-database path, not as a full SQL feature equivalent.
|
|
2602
2631
|
|
|
2603
|
-
Atlas-style `mongodb+srv://` URIs are supported. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
|
|
2632
|
+
`init --system mongodb` defaults to a field-by-field wizard (`host`, `srv`, `port`, `user`, `password` + `authSource`, then optional `replicaSet` / `tls`); a full `uri` is an explicit advanced choice in the interactive flow and the unchanged non-interactive path via `--uri`. Optional fields `authSource`, `replicaSet`, `tls`, and `srv` express what previously required embedding options in the `uri` query string. Atlas-style `mongodb+srv://` URIs are supported both as a full `uri` and via the per-field `srv: true` option. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
|
|
2633
|
+
|
|
2634
|
+
The 5000ms default server-selection timeout is often too tight for a connection over a VPN or to Atlas. Set a `timeout` field (ms) in the connection config, or override it per invocation with root-level `--timeout`, e.g. `dbcli --timeout 20000 --use <conn> list`.
|
|
2604
2635
|
|
|
2605
2636
|
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `q`, `insert`, `update`, `delete`, `export`, `status`, `shell`, `doctor`, `upgrade`, `completion`
|
|
2606
2637
|
|
|
@@ -2623,7 +2654,10 @@ Atlas-style `mongodb+srv://` URIs are supported. `list` and `query` run against
|
|
|
2623
2654
|
### MongoDB-specific workflow
|
|
2624
2655
|
|
|
2625
2656
|
```bash
|
|
2626
|
-
# 1. Initialize
|
|
2657
|
+
# 1. Initialize — field-by-field (primary path)
|
|
2658
|
+
dbcli init --system mongodb --host localhost --port 27017 \
|
|
2659
|
+
--user admin --password '<secret>' --auth-source admin --name mydb
|
|
2660
|
+
# ...or a full URI (advanced fallback, e.g. Atlas SRV clusters)
|
|
2627
2661
|
dbcli init --system mongodb --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
|
|
2628
2662
|
|
|
2629
2663
|
# 2. List collections
|
|
@@ -2663,7 +2697,7 @@ Redis connections speak Redis commands rather than SQL. The adapter uses Bun's n
|
|
|
2663
2697
|
|
|
2664
2698
|
- Required fields: `system: redis`, `host`, `port`. `password` and `database` are optional.
|
|
2665
2699
|
- `database` is the **logical DB index** (`"0"` … `"15"`), kept as a string to play nicely with env-ref bindings. `list` and the connection metadata both label it as the active DB.
|
|
2666
|
-
- `connection.timeout` (ms, default 5000) maps to the client's `connectionTimeout
|
|
2700
|
+
- `connection.timeout` (ms, default 5000) maps to the client's `connectionTimeout`; root-level `--timeout <ms>` overrides it for a single invocation.
|
|
2667
2701
|
|
|
2668
2702
|
### Permission classification
|
|
2669
2703
|
|
|
@@ -2793,7 +2827,7 @@ Elasticsearch connections speak the REST API. The adapter is fetch-based (no SDK
|
|
|
2793
2827
|
- Either `host` + `port` (default `https://localhost:9200`) or `nodes: [...]` (first node is used) or `cloudId`.
|
|
2794
2828
|
- Auth precedence: `apiKey` → `user`/`password` (HTTP Basic). Leave both unset for an open cluster.
|
|
2795
2829
|
- `protocol` defaults to `https`. For TLS quirks: `caPath` (path to a PEM bundle) and `rejectUnauthorized: false` (last resort).
|
|
2796
|
-
- `connection.timeout` (ms, default 5000) is wired to `AbortController` on every request.
|
|
2830
|
+
- `connection.timeout` (ms, default 5000) is wired to `AbortController` on every request; root-level `--timeout <ms>` overrides it for a single invocation.
|
|
2797
2831
|
|
|
2798
2832
|
### Permission classification
|
|
2799
2833
|
|
package/skills/dbcli/SKILL.md
CHANGED
|
@@ -77,7 +77,10 @@ When reporting a check's outcome use the vocabulary `verified` (evidence matched
|
|
|
77
77
|
`not_verified` (check ran and contradicted) / `indeterminate` (ran but ambiguous) /
|
|
78
78
|
`blocked` (could not run due to config, permission, schema, placeholder, or safety gate).
|
|
79
79
|
|
|
80
|
-
Prefer `--format json` for agent-friendly output.
|
|
80
|
+
Prefer `--format json` for agent-friendly output. Diagnostics (auto-limit notices,
|
|
81
|
+
warnings) go to stderr so stdout stays parseable — when piping JSON into a parser,
|
|
82
|
+
use `2>/dev/null` or leave stderr alone. **Never `2>&1`**: it merges those lines back
|
|
83
|
+
into stdout and the parse fails.
|
|
81
84
|
|
|
82
85
|
## Agent Task Packs
|
|
83
86
|
|
|
@@ -229,11 +232,13 @@ dbcli init --system postgresql --host localhost --port 5432 \
|
|
|
229
232
|
# Reuse an existing .env (DATABASE_URL=postgresql://user:pw@host:5432/db)
|
|
230
233
|
dbcli init # parses .env in cwd
|
|
231
234
|
|
|
232
|
-
# MongoDB —
|
|
235
|
+
# MongoDB — field-by-field (no auth = omit --user/--password)
|
|
236
|
+
dbcli init --system mongodb --host localhost --port 27017 --name mydb
|
|
237
|
+
dbcli init --system mongodb --host localhost --port 27017 \
|
|
238
|
+
--user admin --password '<secret>' --auth-source admin --name mydb
|
|
239
|
+
# MongoDB — full URI (advanced escape hatch: multi-host, non-standard driver options)
|
|
233
240
|
dbcli init --system mongodb \
|
|
234
241
|
--uri "mongodb+srv://user:pw@cluster.example.mongodb.net/mydb?authSource=admin"
|
|
235
|
-
# MongoDB — discrete params (no auth = omit --user/--password)
|
|
236
|
-
dbcli init --system mongodb --host localhost --port 27017 --name mydb
|
|
237
242
|
|
|
238
243
|
# Redis — `--name` is the LOGICAL DB INDEX ("0".."15"), not a database name
|
|
239
244
|
dbcli init --system redis --host localhost --port 6379 --password '<secret>' --name 0
|
|
@@ -301,10 +306,25 @@ as a `$env` ref. In a **non-interactive / CI** run you **must** pass all five `-
|
|
|
301
306
|
flags; otherwise `init` exits with an error — it never silently falls back to plaintext.
|
|
302
307
|
`--env-file <path>` is the path to the env file, independent of the `$env` key names.
|
|
303
308
|
|
|
309
|
+
**MongoDB is the exception**: only `--env-host` is required non-interactively.
|
|
310
|
+
`--env-port` / `--env-user` / `--env-password` / `--env-database` are optional — an
|
|
311
|
+
omitted one is written as a literal value (empty string for `user` / `password`, the
|
|
312
|
+
resolved value for `port` / `database`) instead of an `$env` ref, so a field the
|
|
313
|
+
connection never needed doesn't later fail closed on an undefined variable. `init`
|
|
314
|
+
also skips the connection test in this mode regardless of `--skip-test` — the `$env`
|
|
315
|
+
refs have no value to connect with yet.
|
|
316
|
+
|
|
304
317
|
### Common gotchas
|
|
305
318
|
|
|
306
319
|
- **MongoDB `mongodb+srv://`** — `dbcli doctor` reports whether SRV resolves
|
|
307
320
|
natively or via the DoH fallback; useful when the runtime restricts DNS.
|
|
321
|
+
- **MongoDB `authSource` / `replicaSet` / `tls` / `srv`** — `init` asks for
|
|
322
|
+
these interactively (`authSource` only when a user is set; `replicaSet` /
|
|
323
|
+
`tls` behind an "advanced options?" prompt); `--auth-source <db>` is the
|
|
324
|
+
only one with a dedicated non-interactive flag, so set `replicaSet` / `tls`
|
|
325
|
+
interactively or edit `.dbcli` afterward. If a config has both `uri` and
|
|
326
|
+
per-field values, `uri` wins silently — `dbcli doctor` flags this and also
|
|
327
|
+
warns when `srv: true` is combined with a non-default `port`.
|
|
308
328
|
- **MySQL/Postgres password with `@` `:` `/`** — when using `DATABASE_URL`,
|
|
309
329
|
percent-encode (`@` → `%40`); discrete `--password` flags do not need encoding.
|
|
310
330
|
- **Redis `--name`** — accepts only the logical DB index string; non-numeric
|
|
@@ -536,4 +556,4 @@ schema. Raw `query` / `export` invocations render a sortable table only.
|
|
|
536
556
|
- Blacklisted tables and columns are redacted from query output.
|
|
537
557
|
- `schema` reports `estimatedRowCount` and `sizeCategory` (small / medium / large / huge). For large/huge tables add `WHERE` or `LIMIT` — bands in reference.md.
|
|
538
558
|
- `doctor` on `mongodb+srv://` reports whether SRV resolves natively or through the DoH fallback — useful when the runtime restricts DNS.
|
|
539
|
-
- **Global flags:** `--version`, `--config <path>`, `--global`, `--use <name>`, `-v` / `--verbose` / `-vv`, `-q` / `--quiet`, `--no-color` (also honours `NO_COLOR`). Root-level flags must precede the command unless the command explicitly declares a command-level option.
|
|
559
|
+
- **Global flags:** `--version`, `--config <path>`, `--global`, `--use <name>`, `--timeout <ms>`, `-v` / `--verbose` / `-vv`, `-q` / `--quiet`, `--no-color` (also honours `NO_COLOR`). Root-level flags must precede the command unless the command explicitly declares a command-level option.
|