@carllee1983/dbcli 1.56.0 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/dbcli.mdc +13 -0
- package/.cursor/skills/dbcli/reference.md +41 -0
- package/.github/skills/dbcli/SKILL.md +13 -0
- package/.github/skills/dbcli/reference.md +41 -0
- package/CHANGELOG.md +16 -0
- package/assets/SKILL.md +13 -0
- package/assets/SKILL.zh-TW.md +11 -0
- package/assets/reference.md +41 -0
- package/dist/cli-runtime.mjs +344 -44
- package/dist/cli.mjs +1 -1
- package/dist/core.d.ts +144 -122
- package/dist/core.mjs +175 -37
- package/package.json +1 -1
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +13 -0
- package/plugins/dbcli-agent/skills/dbcli/reference.md +41 -0
- package/skills/dbcli/SKILL.md +13 -0
- package/skills/dbcli/reference.md +41 -0
package/dist/cli-runtime.mjs
CHANGED
|
@@ -51,7 +51,7 @@ var package_default;
|
|
|
51
51
|
var init_package = __esm(() => {
|
|
52
52
|
package_default = {
|
|
53
53
|
name: "@carllee1983/dbcli",
|
|
54
|
-
version: "1.
|
|
54
|
+
version: "1.57.0",
|
|
55
55
|
description: "Database CLI for AI agents",
|
|
56
56
|
type: "module",
|
|
57
57
|
publishConfig: {
|
|
@@ -6232,7 +6232,11 @@ function resolveEnvReferences(config, env, parentKey) {
|
|
|
6232
6232
|
}
|
|
6233
6233
|
function parseEnvPassword(content) {
|
|
6234
6234
|
const match = content.match(/^DBCLI_PASSWORD=(.+)$/m);
|
|
6235
|
-
|
|
6235
|
+
if (match?.[1] == null)
|
|
6236
|
+
return null;
|
|
6237
|
+
const raw = match[1].trim();
|
|
6238
|
+
const quoted = raw.startsWith('"') && raw.endsWith('"') || raw.startsWith("'") && raw.endsWith("'");
|
|
6239
|
+
return quoted && raw.length >= 2 ? raw.slice(1, -1) : raw;
|
|
6236
6240
|
}
|
|
6237
6241
|
var _globalConnectionName, DEFAULT_CONFIG, configModule;
|
|
6238
6242
|
var init_config = __esm(() => {
|
|
@@ -9058,6 +9062,20 @@ Reference: {referencePath}`,
|
|
|
9058
9062
|
add_enum_description: "Create an enum type (PostgreSQL only)",
|
|
9059
9063
|
alter_enum_description: "Add a value to an enum type",
|
|
9060
9064
|
drop_enum_description: "Drop an enum type"
|
|
9065
|
+
},
|
|
9066
|
+
password: {
|
|
9067
|
+
description: "Change the password of a single connection, leaving every other setting untouched",
|
|
9068
|
+
arg_connection: "Connection name (defaults to the current default connection)",
|
|
9069
|
+
opt_stdin: "Read the new password from stdin (for rotation scripts)",
|
|
9070
|
+
opt_password: "New password given inline (visible in shell history and process list)",
|
|
9071
|
+
opt_skip_test: "Skip verifying the new password against the database before saving",
|
|
9072
|
+
prompt: "New password for connection '{name}'",
|
|
9073
|
+
source_conflict: "--password and --stdin cannot be used together; pick one source.",
|
|
9074
|
+
needs_source: "No terminal available for a masked prompt. Pass the value with --stdin or --password.",
|
|
9075
|
+
empty: "The new password is empty; nothing was changed.",
|
|
9076
|
+
updated: "Password for connection '{name}' updated \u2192 {envFile} ({varName})",
|
|
9077
|
+
converted: 'The literal password in config.json was replaced by a {{ "$env": "{varName}" }} reference; later rotations only touch the env file.',
|
|
9078
|
+
skipped_test: "Verification skipped (--skip-test): the new password was not checked against the database."
|
|
9061
9079
|
}
|
|
9062
9080
|
};
|
|
9063
9081
|
});
|
|
@@ -9342,6 +9360,20 @@ var init_messages2 = __esm(() => {
|
|
|
9342
9360
|
add_enum_description: "\u5EFA\u7ACB\u5217\u8209\u578B\u5225\uFF08\u50C5 PostgreSQL\uFF09",
|
|
9343
9361
|
alter_enum_description: "\u65B0\u589E\u5217\u8209\u503C",
|
|
9344
9362
|
drop_enum_description: "\u522A\u9664\u5217\u8209\u578B\u5225"
|
|
9363
|
+
},
|
|
9364
|
+
password: {
|
|
9365
|
+
description: "\u53EA\u8B8A\u66F4\u55AE\u4E00\u9023\u7DDA\u7684\u5BC6\u78BC\uFF0C\u5176\u9918\u8A2D\u5B9A\u539F\u5C01\u4E0D\u52D5",
|
|
9366
|
+
arg_connection: "\u9023\u7DDA\u540D\u7A31\uFF08\u7701\u7565\u6642\u6539\u9810\u8A2D\u9023\u7DDA\uFF09",
|
|
9367
|
+
opt_stdin: "\u5F9E stdin \u8B80\u53D6\u65B0\u5BC6\u78BC\uFF08\u4F9B\u8F2A\u66FF\u8173\u672C\u4F7F\u7528\uFF09",
|
|
9368
|
+
opt_password: "\u76F4\u63A5\u5E36\u5165\u65B0\u5BC6\u78BC\uFF08\u6703\u7559\u5728 shell \u6B77\u53F2\u8207\u884C\u7A0B\u5217\u8868\uFF09",
|
|
9369
|
+
opt_skip_test: "\u5B58\u6A94\u524D\u4E0D\u5148\u7528\u65B0\u5BC6\u78BC\u9023\u7DDA\u9A57\u8B49",
|
|
9370
|
+
prompt: "\u9023\u7DDA '{name}' \u7684\u65B0\u5BC6\u78BC",
|
|
9371
|
+
source_conflict: "--password \u8207 --stdin \u53EA\u80FD\u64C7\u4E00\u3002",
|
|
9372
|
+
needs_source: "\u6C92\u6709\u7D42\u7AEF\u6A5F\u53EF\u505A\u906E\u853D\u8F38\u5165\uFF0C\u8ACB\u6539\u7528 --stdin \u6216 --password \u50B3\u5165\u3002",
|
|
9373
|
+
empty: "\u65B0\u5BC6\u78BC\u662F\u7A7A\u7684\uFF0C\u672A\u505A\u4EFB\u4F55\u8B8A\u66F4\u3002",
|
|
9374
|
+
updated: "\u5DF2\u66F4\u65B0\u9023\u7DDA '{name}' \u7684\u5BC6\u78BC \u2192 {envFile}\uFF08{varName}\uFF09",
|
|
9375
|
+
converted: 'config.json \u88E1\u7684\u660E\u6587\u5BC6\u78BC\u5DF2\u6539\u6210 {{ "$env": "{varName}" }} \u53C3\u7167\uFF0C\u4E4B\u5F8C\u8F2A\u66FF\u53EA\u6703\u52D5 env \u6A94\u3002',
|
|
9376
|
+
skipped_test: "\u5DF2\u8DF3\u904E\u9A57\u8B49\uFF08--skip-test\uFF09\uFF1A\u65B0\u5BC6\u78BC\u672A\u7D93\u8CC7\u6599\u5EAB\u78BA\u8A8D\u3002"
|
|
9345
9377
|
}
|
|
9346
9378
|
};
|
|
9347
9379
|
});
|
|
@@ -9651,6 +9683,13 @@ async function select(message, choices) {
|
|
|
9651
9683
|
return await selectFallback(message, choices);
|
|
9652
9684
|
return await inquirer.select({ message, choices });
|
|
9653
9685
|
}
|
|
9686
|
+
async function secret(message) {
|
|
9687
|
+
const inquirer = await loadInquirer();
|
|
9688
|
+
if (!inquirer) {
|
|
9689
|
+
throw new Error("Masked input is unavailable; pass the value with --stdin or --password instead.");
|
|
9690
|
+
}
|
|
9691
|
+
return await inquirer.password({ message, mask: "*" });
|
|
9692
|
+
}
|
|
9654
9693
|
async function confirm(message) {
|
|
9655
9694
|
if (!process.stdin.isTTY)
|
|
9656
9695
|
return await confirmFallback(message);
|
|
@@ -9664,7 +9703,8 @@ var init_prompts = __esm(() => {
|
|
|
9664
9703
|
promptUser = {
|
|
9665
9704
|
text,
|
|
9666
9705
|
select,
|
|
9667
|
-
confirm
|
|
9706
|
+
confirm,
|
|
9707
|
+
secret
|
|
9668
9708
|
};
|
|
9669
9709
|
});
|
|
9670
9710
|
|
|
@@ -45685,6 +45725,258 @@ var init_use = __esm(() => {
|
|
|
45685
45725
|
});
|
|
45686
45726
|
});
|
|
45687
45727
|
|
|
45728
|
+
// src/core/config-v2-mutations.ts
|
|
45729
|
+
function envVarNameFor(connName, field) {
|
|
45730
|
+
const slug = connName.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
45731
|
+
return `DBCLI_${slug}_${field.toUpperCase()}`;
|
|
45732
|
+
}
|
|
45733
|
+
var init_config_v2_mutations = () => {};
|
|
45734
|
+
|
|
45735
|
+
// src/core/env-file-writer.ts
|
|
45736
|
+
import { chmod as chmod2, mkdir as mkdir18, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
45737
|
+
import { dirname as dirname16 } from "path";
|
|
45738
|
+
function escapeForRegExp(value) {
|
|
45739
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45740
|
+
}
|
|
45741
|
+
async function upsertEnvVar(envPath, varName, value) {
|
|
45742
|
+
let content = "";
|
|
45743
|
+
try {
|
|
45744
|
+
content = await readFile9(envPath, "utf8");
|
|
45745
|
+
} catch (cause) {
|
|
45746
|
+
const code = cause.code;
|
|
45747
|
+
if (code !== "ENOENT")
|
|
45748
|
+
throw cause;
|
|
45749
|
+
await mkdir18(dirname16(envPath), { recursive: true });
|
|
45750
|
+
}
|
|
45751
|
+
const line = `${varName}="${value}"`;
|
|
45752
|
+
const existing = new RegExp(`^${escapeForRegExp(varName)}=.*$`, "m");
|
|
45753
|
+
if (existing.test(content)) {
|
|
45754
|
+
content = content.replace(existing, () => line);
|
|
45755
|
+
} else {
|
|
45756
|
+
content = content.length && !content.endsWith(`
|
|
45757
|
+
`) ? `${content}
|
|
45758
|
+
${line}
|
|
45759
|
+
` : `${content}${line}
|
|
45760
|
+
`;
|
|
45761
|
+
}
|
|
45762
|
+
await writeFile9(envPath, content, { mode: SECRET_FILE_MODE });
|
|
45763
|
+
await chmod2(envPath, SECRET_FILE_MODE);
|
|
45764
|
+
}
|
|
45765
|
+
var SECRET_FILE_MODE = 384;
|
|
45766
|
+
var init_env_file_writer = () => {};
|
|
45767
|
+
|
|
45768
|
+
// src/core/connection-credential.ts
|
|
45769
|
+
import { join as join39 } from "path";
|
|
45770
|
+
async function readRawConfig(storagePath) {
|
|
45771
|
+
const configPath = join39(storagePath, "config.json");
|
|
45772
|
+
const file = Bun.file(configPath);
|
|
45773
|
+
if (!await file.exists()) {
|
|
45774
|
+
throw new ConfigError(`\u627E\u4E0D\u5230\u8A2D\u5B9A\u6A94\uFF1A${configPath}`);
|
|
45775
|
+
}
|
|
45776
|
+
return JSON.parse(await file.text());
|
|
45777
|
+
}
|
|
45778
|
+
function envRefName(value) {
|
|
45779
|
+
if (typeof value === "object" && value !== null && "$env" in value) {
|
|
45780
|
+
return String(value.$env);
|
|
45781
|
+
}
|
|
45782
|
+
return;
|
|
45783
|
+
}
|
|
45784
|
+
async function resolvePasswordTarget(projectPath, connectionName) {
|
|
45785
|
+
const storagePath = await resolveConfigStoragePath(projectPath);
|
|
45786
|
+
const raw = await readRawConfig(storagePath);
|
|
45787
|
+
if (detectConfigVersion(raw) !== 2) {
|
|
45788
|
+
if (connectionName && connectionName !== V1_CONNECTION_NAME) {
|
|
45789
|
+
throw new ConfigError(`\u9019\u662F v1 \u8A2D\u5B9A\uFF0C\u53EA\u6709\u4E00\u689D\u9023\u7DDA\uFF0C\u7121\u6CD5\u6307\u5B9A\u9023\u7DDA\u540D\u7A31 '${connectionName}'\u3002` + `\u9700\u8981\u591A\u9023\u7DDA\u8ACB\u5148\u7528 dbcli init --conn-name <name> \u5347\u7D1A\u5230 v2\u3002`);
|
|
45790
|
+
}
|
|
45791
|
+
const connection2 = raw.connection ?? {};
|
|
45792
|
+
const customRef = envRefName(connection2.password);
|
|
45793
|
+
if (customRef !== undefined && customRef !== V1_VAR_NAME) {
|
|
45794
|
+
throw new ConfigError(`\u9019\u500B v1 \u8A2D\u5B9A\u7684\u5BC6\u78BC\u4F86\u81EA\u74B0\u5883\u8B8A\u6578 '${customRef}'\uFF0Cdbcli \u7121\u6CD5\u4EE3\u70BA\u8F2A\u66FF\u3002` + `\u8ACB\u76F4\u63A5\u66F4\u65B0\u8A72\u74B0\u5883\u8B8A\u6578\uFF0C\u6216\u7528 dbcli init --conn-name <name> \u5347\u7D1A\u5230 v2 \u5F8C\u518D\u8F2A\u66FF\u3002`);
|
|
45795
|
+
}
|
|
45796
|
+
return {
|
|
45797
|
+
connection: V1_CONNECTION_NAME,
|
|
45798
|
+
envFile: DEFAULT_ENV_FILE,
|
|
45799
|
+
varName: V1_VAR_NAME,
|
|
45800
|
+
convertedToEnvRef: false
|
|
45801
|
+
};
|
|
45802
|
+
}
|
|
45803
|
+
const config = await readV2Config(projectPath);
|
|
45804
|
+
const targetName = connectionName ?? config.default;
|
|
45805
|
+
const connection = config.connections[targetName];
|
|
45806
|
+
if (!connection) {
|
|
45807
|
+
throw new ConfigError(connectionNotFoundMessage(targetName, Object.keys(config.connections)));
|
|
45808
|
+
}
|
|
45809
|
+
const referencedVar = envRefName(connection.password);
|
|
45810
|
+
return {
|
|
45811
|
+
connection: targetName,
|
|
45812
|
+
envFile: connection.envFile ?? DEFAULT_ENV_FILE,
|
|
45813
|
+
varName: referencedVar ?? envVarNameFor(targetName, "password"),
|
|
45814
|
+
convertedToEnvRef: referencedVar === undefined
|
|
45815
|
+
};
|
|
45816
|
+
}
|
|
45817
|
+
function assertSingleLine(value) {
|
|
45818
|
+
if (value.includes(`
|
|
45819
|
+
`) || value.includes("\r")) {
|
|
45820
|
+
throw new ConfigError("\u5BC6\u78BC\u4E0D\u53EF\u5305\u542B\u63DB\u884C\u5B57\u5143");
|
|
45821
|
+
}
|
|
45822
|
+
}
|
|
45823
|
+
async function stripV1LiteralPassword(storagePath, raw) {
|
|
45824
|
+
const connection = raw.connection ?? {};
|
|
45825
|
+
if (typeof connection.password !== "string")
|
|
45826
|
+
return;
|
|
45827
|
+
const stripped = { ...raw, connection: { ...connection } };
|
|
45828
|
+
delete stripped.connection.password;
|
|
45829
|
+
await writeConfigWithIntegrity(storagePath, JSON.stringify(stripped, null, 2));
|
|
45830
|
+
}
|
|
45831
|
+
async function setConnectionPassword(projectPath, connectionName, password) {
|
|
45832
|
+
assertConfigMutationApproved();
|
|
45833
|
+
assertSingleLine(password);
|
|
45834
|
+
const target = await resolvePasswordTarget(projectPath, connectionName);
|
|
45835
|
+
const storagePath = await resolveConfigStoragePath(projectPath);
|
|
45836
|
+
const raw = await readRawConfig(storagePath);
|
|
45837
|
+
await upsertEnvVar(join39(storagePath, target.envFile), target.varName, password);
|
|
45838
|
+
if (detectConfigVersion(raw) !== 2) {
|
|
45839
|
+
await stripV1LiteralPassword(storagePath, raw);
|
|
45840
|
+
return target;
|
|
45841
|
+
}
|
|
45842
|
+
const config = await readV2Config(projectPath);
|
|
45843
|
+
const connection = config.connections[target.connection];
|
|
45844
|
+
const needsEnvFile = connection.envFile === undefined;
|
|
45845
|
+
if (target.convertedToEnvRef || needsEnvFile) {
|
|
45846
|
+
await writeV2Config(projectPath, {
|
|
45847
|
+
...config,
|
|
45848
|
+
connections: {
|
|
45849
|
+
...config.connections,
|
|
45850
|
+
[target.connection]: {
|
|
45851
|
+
...connection,
|
|
45852
|
+
envFile: target.envFile,
|
|
45853
|
+
...target.convertedToEnvRef && { password: { $env: target.varName } }
|
|
45854
|
+
}
|
|
45855
|
+
}
|
|
45856
|
+
});
|
|
45857
|
+
}
|
|
45858
|
+
return target;
|
|
45859
|
+
}
|
|
45860
|
+
var DEFAULT_ENV_FILE = ".env.local", V1_VAR_NAME = "DBCLI_PASSWORD", V1_CONNECTION_NAME = "default";
|
|
45861
|
+
var init_connection_credential = __esm(() => {
|
|
45862
|
+
init_config_binding();
|
|
45863
|
+
init_config_v2();
|
|
45864
|
+
init_config_integrity();
|
|
45865
|
+
init_config_mutation_guard();
|
|
45866
|
+
init_config_v2_mutations();
|
|
45867
|
+
init_errors3();
|
|
45868
|
+
init_env_file_writer();
|
|
45869
|
+
});
|
|
45870
|
+
|
|
45871
|
+
// src/commands/credential.ts
|
|
45872
|
+
var exports_credential = {};
|
|
45873
|
+
__export(exports_credential, {
|
|
45874
|
+
resolveNewPassword: () => resolveNewPassword,
|
|
45875
|
+
passwordCommand: () => passwordCommand
|
|
45876
|
+
});
|
|
45877
|
+
async function readAllStdin() {
|
|
45878
|
+
return await Bun.stdin.text();
|
|
45879
|
+
}
|
|
45880
|
+
async function resolveNewPassword(options, io) {
|
|
45881
|
+
if (options.password !== undefined && options.stdin) {
|
|
45882
|
+
throw new ConfigError(t("password.source_conflict"));
|
|
45883
|
+
}
|
|
45884
|
+
let value;
|
|
45885
|
+
if (options.password !== undefined) {
|
|
45886
|
+
value = options.password;
|
|
45887
|
+
} else if (options.stdin) {
|
|
45888
|
+
value = (await io.readStdin()).replace(/\r?\n$/, "");
|
|
45889
|
+
} else if (io.isTTY) {
|
|
45890
|
+
value = await io.prompt();
|
|
45891
|
+
} else {
|
|
45892
|
+
throw new ConfigError(t("password.needs_source"));
|
|
45893
|
+
}
|
|
45894
|
+
if (value.length === 0) {
|
|
45895
|
+
throw new ConfigError(t("password.empty"));
|
|
45896
|
+
}
|
|
45897
|
+
return value;
|
|
45898
|
+
}
|
|
45899
|
+
async function verifyNewPassword(configPath, target, connectionName, password) {
|
|
45900
|
+
const storagePath = await resolveConfigStoragePath(configPath);
|
|
45901
|
+
const previous = process.env[target.varName];
|
|
45902
|
+
process.env[target.varName] = password;
|
|
45903
|
+
try {
|
|
45904
|
+
const config = await configModule.read(storagePath, connectionName);
|
|
45905
|
+
const adapter = AdapterFactory.createAdapter({
|
|
45906
|
+
...config.connection,
|
|
45907
|
+
password
|
|
45908
|
+
});
|
|
45909
|
+
try {
|
|
45910
|
+
await adapter.connect();
|
|
45911
|
+
await adapter.testConnection();
|
|
45912
|
+
} finally {
|
|
45913
|
+
await adapter.disconnect().catch(() => {
|
|
45914
|
+
return;
|
|
45915
|
+
});
|
|
45916
|
+
}
|
|
45917
|
+
} finally {
|
|
45918
|
+
if (previous === undefined)
|
|
45919
|
+
delete process.env[target.varName];
|
|
45920
|
+
else
|
|
45921
|
+
process.env[target.varName] = previous;
|
|
45922
|
+
}
|
|
45923
|
+
}
|
|
45924
|
+
var passwordCommand;
|
|
45925
|
+
var init_credential = __esm(() => {
|
|
45926
|
+
init_esm();
|
|
45927
|
+
init_adapters();
|
|
45928
|
+
init_config();
|
|
45929
|
+
init_config_mutation_guard();
|
|
45930
|
+
init_config_binding();
|
|
45931
|
+
init_connection_credential();
|
|
45932
|
+
init_config_path();
|
|
45933
|
+
init_prompts();
|
|
45934
|
+
init_errors3();
|
|
45935
|
+
init_message_loader();
|
|
45936
|
+
passwordCommand = new Command("password").description(t("password.description")).argument("[connection]", t("password.arg_connection")).option("--stdin", t("password.opt_stdin")).option("--password <value>", t("password.opt_password")).option("--skip-test", t("password.opt_skip_test")).option("--format <format>", "Output format (text, json)", "text").action(async (connection, options) => {
|
|
45937
|
+
try {
|
|
45938
|
+
assertConfigMutationApproved();
|
|
45939
|
+
const configPath = resolveConfigPath(passwordCommand);
|
|
45940
|
+
const target = await resolvePasswordTarget(configPath, connection);
|
|
45941
|
+
const value = await resolveNewPassword({
|
|
45942
|
+
password: typeof options.password === "string" ? options.password : undefined,
|
|
45943
|
+
stdin: options.stdin === true
|
|
45944
|
+
}, {
|
|
45945
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
45946
|
+
readStdin: readAllStdin,
|
|
45947
|
+
prompt: () => promptUser.secret(t_vars("password.prompt", { name: target.connection }))
|
|
45948
|
+
});
|
|
45949
|
+
if (options.skipTest !== true) {
|
|
45950
|
+
await verifyNewPassword(configPath, target, connection, value);
|
|
45951
|
+
}
|
|
45952
|
+
const written = await setConnectionPassword(configPath, connection, value);
|
|
45953
|
+
if (options.format === "json") {
|
|
45954
|
+
console.log(JSON.stringify({ success: true, ...written }, null, 2));
|
|
45955
|
+
} else {
|
|
45956
|
+
console.log(t_vars("password.updated", {
|
|
45957
|
+
name: written.connection,
|
|
45958
|
+
envFile: written.envFile,
|
|
45959
|
+
varName: written.varName
|
|
45960
|
+
}));
|
|
45961
|
+
if (written.convertedToEnvRef) {
|
|
45962
|
+
console.log(t_vars("password.converted", { varName: written.varName }));
|
|
45963
|
+
}
|
|
45964
|
+
if (options.skipTest === true) {
|
|
45965
|
+
console.log(t("password.skipped_test"));
|
|
45966
|
+
}
|
|
45967
|
+
}
|
|
45968
|
+
} catch (error) {
|
|
45969
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45970
|
+
if (options.format === "json") {
|
|
45971
|
+
console.error(JSON.stringify({ success: false, error: message }, null, 2));
|
|
45972
|
+
} else {
|
|
45973
|
+
console.error(t_vars("errors.message", { message }));
|
|
45974
|
+
}
|
|
45975
|
+
process.exit(1);
|
|
45976
|
+
}
|
|
45977
|
+
});
|
|
45978
|
+
});
|
|
45979
|
+
|
|
45688
45980
|
// src/proxy/relay.ts
|
|
45689
45981
|
class TcpRelay {
|
|
45690
45982
|
clientBytes = 0;
|
|
@@ -45759,8 +46051,8 @@ var init_sql_metadata = __esm(() => {
|
|
|
45759
46051
|
});
|
|
45760
46052
|
|
|
45761
46053
|
// src/proxy/events.ts
|
|
45762
|
-
import { appendFile as appendFile2, mkdir as
|
|
45763
|
-
import { dirname as
|
|
46054
|
+
import { appendFile as appendFile2, mkdir as mkdir19, readFile as readFile10, stat as stat9 } from "fs/promises";
|
|
46055
|
+
import { dirname as dirname17 } from "path";
|
|
45764
46056
|
function hasSql(e) {
|
|
45765
46057
|
return e.type === "query_observed" || e.type === "query_completed" || e.type === "query_errored";
|
|
45766
46058
|
}
|
|
@@ -45799,7 +46091,7 @@ class EventWriter {
|
|
|
45799
46091
|
}
|
|
45800
46092
|
async writeInternal(event) {
|
|
45801
46093
|
if (!this.dirEnsured) {
|
|
45802
|
-
await
|
|
46094
|
+
await mkdir19(dirname17(this.path), { recursive: true });
|
|
45803
46095
|
this.dirEnsured = true;
|
|
45804
46096
|
}
|
|
45805
46097
|
if (!this.initialized) {
|
|
@@ -45823,7 +46115,7 @@ class EventWriter {
|
|
|
45823
46115
|
try {
|
|
45824
46116
|
const s = await stat9(this.path);
|
|
45825
46117
|
this.currentSizeBytes = s.size;
|
|
45826
|
-
const raw = await
|
|
46118
|
+
const raw = await readFile10(this.path, "utf8");
|
|
45827
46119
|
this.currentEntryCount = raw.split(`
|
|
45828
46120
|
`).filter(Boolean).length;
|
|
45829
46121
|
} catch {
|
|
@@ -46420,7 +46712,7 @@ var init_server = __esm(() => {
|
|
|
46420
46712
|
});
|
|
46421
46713
|
|
|
46422
46714
|
// src/proxy/event-reader.ts
|
|
46423
|
-
import { readFile as
|
|
46715
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
46424
46716
|
async function readEvents(path4, opts) {
|
|
46425
46717
|
const candidates = opts.includeRotated ? [path4, `${path4}.1`] : [path4];
|
|
46426
46718
|
const files = [];
|
|
@@ -46429,7 +46721,7 @@ async function readEvents(path4, opts) {
|
|
|
46429
46721
|
for (const file of candidates) {
|
|
46430
46722
|
let raw;
|
|
46431
46723
|
try {
|
|
46432
|
-
raw = await
|
|
46724
|
+
raw = await readFile11(file, "utf8");
|
|
46433
46725
|
} catch (err) {
|
|
46434
46726
|
if (err.code !== "ENOENT")
|
|
46435
46727
|
throw err;
|
|
@@ -46860,7 +47152,7 @@ __export(exports_proxy, {
|
|
|
46860
47152
|
proxyCommand: () => proxyCommand,
|
|
46861
47153
|
parseHostPort: () => parseHostPort
|
|
46862
47154
|
});
|
|
46863
|
-
import { join as
|
|
47155
|
+
import { join as join40 } from "path";
|
|
46864
47156
|
function parseHostPort(value) {
|
|
46865
47157
|
const idx = value.lastIndexOf(":");
|
|
46866
47158
|
if (idx <= 0 || idx === value.length - 1) {
|
|
@@ -46923,7 +47215,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46923
47215
|
target: options.target,
|
|
46924
47216
|
connection
|
|
46925
47217
|
});
|
|
46926
|
-
const eventsPath = options.events ??
|
|
47218
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46927
47219
|
const slowMs = Number(options.slowMs ?? 1000);
|
|
46928
47220
|
if (!Number.isFinite(slowMs) || slowMs < 0) {
|
|
46929
47221
|
throw new Error(`Invalid --slow-ms "${options.slowMs}". Expected a non-negative number`);
|
|
@@ -46972,7 +47264,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46972
47264
|
}
|
|
46973
47265
|
}
|
|
46974
47266
|
function addCommonOptions(cmd) {
|
|
46975
|
-
return cmd.option("--listen <host:port>", "Local proxy listen address (required)").option("--target <host:port>", "Upstream DB target (optional when config provides host/port)").option("--events <path>", "Event JSONL path",
|
|
47267
|
+
return cmd.option("--listen <host:port>", "Local proxy listen address (required)").option("--target <host:port>", "Upstream DB target (optional when config provides host/port)").option("--events <path>", "Event JSONL path", join40(".dbcli", "proxy", "events.jsonl")).option("--slow-ms <number>", "Threshold (ms); queries at/above it get slow:true in the event + a terminal warning", "1000").option("--redact <mode>", "SQL redaction: none | literals", "none").option("--format <format>", "Runtime status output: text | json", "text");
|
|
46976
47268
|
}
|
|
46977
47269
|
function parseNonNegInt(value, flag, fallback) {
|
|
46978
47270
|
if (value === undefined)
|
|
@@ -46990,7 +47282,7 @@ async function runAnalyze(options) {
|
|
|
46990
47282
|
const top = parseNonNegInt(options.top, "top", 20);
|
|
46991
47283
|
const slowMs = parseNonNegInt(options.slowMs, "slow-ms", 1000);
|
|
46992
47284
|
const nPlusOne = parseNonNegInt(options.nPlusOne, "n-plus-one", 10);
|
|
46993
|
-
const eventsPath = options.events ??
|
|
47285
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46994
47286
|
const { events, malformedLines, files } = await readEvents(eventsPath, {
|
|
46995
47287
|
includeRotated: options.includeRotated !== false
|
|
46996
47288
|
});
|
|
@@ -47043,7 +47335,7 @@ var init_proxy = __esm(() => {
|
|
|
47043
47335
|
});
|
|
47044
47336
|
}
|
|
47045
47337
|
ANALYZE_FORMATS = ["json", "text", "markdown"];
|
|
47046
|
-
proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path",
|
|
47338
|
+
proxyCommand.command("analyze").description("Analyze a proxy event log offline (no DB connection)").option("--events <path>", "Event JSONL path", join40(".dbcli", "proxy", "events.jsonl")).option("--format <format>", "Output format: json | text | markdown (QueryLens)", "json").option("--top <number>", "Rows shown in text + suggestedCommands depth", "20").option("--slow-ms <number>", "Slow-query threshold (ms) for slowCount", "1000").option("--n-plus-one <number>", "Min repeats per (session,fingerprint) to flag N+1", "10").option("--no-include-rotated", "Do not merge the rotated <events>.1 segment").action(async (options) => {
|
|
47047
47339
|
await runAnalyze(options);
|
|
47048
47340
|
});
|
|
47049
47341
|
addCommonOptions(proxyCommand).action(async (options, command) => {
|
|
@@ -47377,9 +47669,9 @@ var init_semantic2 = __esm(() => {
|
|
|
47377
47669
|
});
|
|
47378
47670
|
|
|
47379
47671
|
// src/core/design/index.ts
|
|
47380
|
-
import { join as
|
|
47672
|
+
import { join as join41 } from "path";
|
|
47381
47673
|
function defaultDesignFile(workspaceRoot) {
|
|
47382
|
-
return
|
|
47674
|
+
return join41(workspaceRoot, DEFAULT_FILE3);
|
|
47383
47675
|
}
|
|
47384
47676
|
function parseDesignSpec(raw, filePath = DEFAULT_FILE3) {
|
|
47385
47677
|
const parsed = specSchema.safeParse(raw);
|
|
@@ -48210,8 +48502,8 @@ var exports_backfill = {};
|
|
|
48210
48502
|
__export(exports_backfill, {
|
|
48211
48503
|
backfillCommand: () => backfillCommand
|
|
48212
48504
|
});
|
|
48213
|
-
import { dirname as
|
|
48214
|
-
import { mkdir as
|
|
48505
|
+
import { dirname as dirname18, resolve as resolve10 } from "path";
|
|
48506
|
+
import { mkdir as mkdir20 } from "fs/promises";
|
|
48215
48507
|
function identityFor(config, name) {
|
|
48216
48508
|
const connection = config.connections[name];
|
|
48217
48509
|
if (!connection) {
|
|
@@ -48261,7 +48553,7 @@ var init_backfill = __esm(() => {
|
|
|
48261
48553
|
return;
|
|
48262
48554
|
}
|
|
48263
48555
|
const out = resolve10(options.out ?? `.dbcli/backfills/${artifact2.source.sha256.slice(0, 12)}.json`);
|
|
48264
|
-
await
|
|
48556
|
+
await mkdir20(dirname18(out), { recursive: true });
|
|
48265
48557
|
await Bun.write(out, JSON.stringify(artifact2, null, 2) + `
|
|
48266
48558
|
`);
|
|
48267
48559
|
console.log(JSON.stringify({ path: out, artifact: artifact2 }, null, 2));
|
|
@@ -48270,8 +48562,8 @@ var init_backfill = __esm(() => {
|
|
|
48270
48562
|
|
|
48271
48563
|
// src/core/evidence-pack/index.ts
|
|
48272
48564
|
import { createHash as createHash8, randomUUID as randomUUID6 } from "crypto";
|
|
48273
|
-
import { link as link3, lstat as lstat5, mkdir as
|
|
48274
|
-
import { basename as basename9, dirname as
|
|
48565
|
+
import { link as link3, lstat as lstat5, mkdir as mkdir21, realpath as realpath2, unlink as unlink8, writeFile as writeFile10 } from "fs/promises";
|
|
48566
|
+
import { basename as basename9, dirname as dirname19, relative as relative5, resolve as resolve11, sep as sep7 } from "path";
|
|
48275
48567
|
function isRecord4(value) {
|
|
48276
48568
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48277
48569
|
}
|
|
@@ -48522,7 +48814,7 @@ async function realExistingAncestor(path4) {
|
|
|
48522
48814
|
} catch (error) {
|
|
48523
48815
|
if (error.code !== "ENOENT")
|
|
48524
48816
|
throw error;
|
|
48525
|
-
const parent =
|
|
48817
|
+
const parent = dirname19(candidate);
|
|
48526
48818
|
if (parent === candidate)
|
|
48527
48819
|
throw error;
|
|
48528
48820
|
candidate = parent;
|
|
@@ -48534,12 +48826,12 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48534
48826
|
const requestedTarget = resolve11(workspace, outputPath);
|
|
48535
48827
|
if (!isInside(workspace, requestedTarget))
|
|
48536
48828
|
throw new EvidencePackValidationError("output path must stay inside the workspace");
|
|
48537
|
-
const parent =
|
|
48829
|
+
const parent = dirname19(requestedTarget);
|
|
48538
48830
|
const existingAncestor2 = await realExistingAncestor(parent);
|
|
48539
48831
|
if (!isInside(workspace, existingAncestor2) && existingAncestor2 !== workspace) {
|
|
48540
48832
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
48541
48833
|
}
|
|
48542
|
-
await
|
|
48834
|
+
await mkdir21(parent, { recursive: true });
|
|
48543
48835
|
const realParent = await realpath2(parent);
|
|
48544
48836
|
if (!isInside(workspace, realParent) && realParent !== workspace) {
|
|
48545
48837
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
@@ -48556,7 +48848,7 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48556
48848
|
}
|
|
48557
48849
|
const temp = resolve11(realParent, `.${basename9(target)}.${process.pid}.${randomUUID6()}.tmp`);
|
|
48558
48850
|
try {
|
|
48559
|
-
await
|
|
48851
|
+
await writeFile10(temp, `${JSON.stringify(pack, null, 2)}
|
|
48560
48852
|
`, "utf8");
|
|
48561
48853
|
try {
|
|
48562
48854
|
await link3(temp, target);
|
|
@@ -48633,7 +48925,7 @@ var exports_evidence = {};
|
|
|
48633
48925
|
__export(exports_evidence, {
|
|
48634
48926
|
evidenceCommand: () => evidenceCommand
|
|
48635
48927
|
});
|
|
48636
|
-
import { join as
|
|
48928
|
+
import { join as join42, relative as relative6, resolve as resolve12, sep as sep8 } from "path";
|
|
48637
48929
|
import { realpath as realpath3 } from "fs/promises";
|
|
48638
48930
|
import { createHash as createHash9 } from "crypto";
|
|
48639
48931
|
function safeMessage(error) {
|
|
@@ -48709,7 +49001,7 @@ async function auditEntries(configPath, config) {
|
|
|
48709
49001
|
throw new EvidencePackValidationError("audit evidence is disabled");
|
|
48710
49002
|
const storagePath = await resolveConfigStoragePath(configPath);
|
|
48711
49003
|
const connectionName = config.effectiveConnectionName || getGlobalConnectionName() || "default";
|
|
48712
|
-
const file =
|
|
49004
|
+
const file = join42(storagePath, ".dbcli", "audit", `${connectionName}.jsonl`);
|
|
48713
49005
|
return { connectionName, entries: await readEntries(file, { include_rotated: true }) };
|
|
48714
49006
|
}
|
|
48715
49007
|
function auditReference(entry, connectionName) {
|
|
@@ -50073,8 +50365,8 @@ __export(exports_impact, {
|
|
|
50073
50365
|
impactCommand: () => impactCommand
|
|
50074
50366
|
});
|
|
50075
50367
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
50076
|
-
import { link as link4, lstat as lstat8, mkdir as
|
|
50077
|
-
import { basename as basename10, dirname as
|
|
50368
|
+
import { link as link4, lstat as lstat8, mkdir as mkdir22, realpath as realpath5, unlink as unlink9, writeFile as writeFile11 } from "fs/promises";
|
|
50369
|
+
import { basename as basename10, dirname as dirname20, relative as relative8, resolve as resolve14, sep as sep10 } from "path";
|
|
50078
50370
|
function collectOption3(value, previous) {
|
|
50079
50371
|
return [...previous, value];
|
|
50080
50372
|
}
|
|
@@ -50278,7 +50570,7 @@ async function realExistingAncestor2(path4) {
|
|
|
50278
50570
|
} catch (error) {
|
|
50279
50571
|
if (error.code !== "ENOENT")
|
|
50280
50572
|
throw error;
|
|
50281
|
-
const parent =
|
|
50573
|
+
const parent = dirname20(candidate);
|
|
50282
50574
|
if (parent === candidate)
|
|
50283
50575
|
throw error;
|
|
50284
50576
|
candidate = parent;
|
|
@@ -50290,11 +50582,11 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50290
50582
|
const target = resolve14(workspace, outputPath);
|
|
50291
50583
|
if (!isInside2(workspace, target))
|
|
50292
50584
|
throw new Error("output path must stay inside the workspace");
|
|
50293
|
-
const parent =
|
|
50585
|
+
const parent = dirname20(target);
|
|
50294
50586
|
const existingAncestor2 = await realExistingAncestor2(parent);
|
|
50295
50587
|
if (!isInside2(workspace, existingAncestor2) && existingAncestor2 !== workspace)
|
|
50296
50588
|
throw new Error("output path must stay inside the workspace");
|
|
50297
|
-
await
|
|
50589
|
+
await mkdir22(parent, { recursive: true });
|
|
50298
50590
|
const realParent = await realpath5(parent);
|
|
50299
50591
|
if (!isInside2(workspace, realParent) && realParent !== workspace)
|
|
50300
50592
|
throw new Error("output path must stay inside the workspace");
|
|
@@ -50302,7 +50594,7 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50302
50594
|
}
|
|
50303
50595
|
async function assertOutputParent(workspaceRoot, path4) {
|
|
50304
50596
|
const workspace = await realpath5(workspaceRoot);
|
|
50305
|
-
const parent = await realpath5(
|
|
50597
|
+
const parent = await realpath5(dirname20(path4));
|
|
50306
50598
|
if (!isInside2(workspace, parent) && parent !== workspace) {
|
|
50307
50599
|
throw new Error("output path must stay inside the workspace");
|
|
50308
50600
|
}
|
|
@@ -50316,9 +50608,9 @@ async function writeOutput(workspaceRoot, path4, content) {
|
|
|
50316
50608
|
if (error.code !== "ENOENT")
|
|
50317
50609
|
throw error;
|
|
50318
50610
|
}
|
|
50319
|
-
const temp = resolve14(
|
|
50611
|
+
const temp = resolve14(dirname20(path4), `.${basename10(path4)}.${process.pid}.${randomUUID7()}.tmp`);
|
|
50320
50612
|
try {
|
|
50321
|
-
await
|
|
50613
|
+
await writeFile11(temp, content, "utf8");
|
|
50322
50614
|
await assertOutputParent(workspaceRoot, path4);
|
|
50323
50615
|
await link4(temp, path4);
|
|
50324
50616
|
} catch (error) {
|
|
@@ -50457,6 +50749,7 @@ function buildProgram() {
|
|
|
50457
50749
|
program2.addCommand(shellCommand);
|
|
50458
50750
|
program2.addCommand(migrateCommand);
|
|
50459
50751
|
program2.addCommand(useCommand);
|
|
50752
|
+
program2.addCommand(passwordCommand);
|
|
50460
50753
|
program2.addCommand(queriesCommand);
|
|
50461
50754
|
program2.addCommand(explainCommand);
|
|
50462
50755
|
program2.addCommand(lintCommand);
|
|
@@ -50510,6 +50803,7 @@ var init_program = __esm(() => {
|
|
|
50510
50803
|
init_shell3();
|
|
50511
50804
|
init_migrate();
|
|
50512
50805
|
init_use();
|
|
50806
|
+
init_credential();
|
|
50513
50807
|
init_proxy();
|
|
50514
50808
|
init_semantic2();
|
|
50515
50809
|
init_design2();
|
|
@@ -50533,9 +50827,9 @@ __export(exports_completion, {
|
|
|
50533
50827
|
detectShell: () => detectShell,
|
|
50534
50828
|
completionCommand: () => completionCommand
|
|
50535
50829
|
});
|
|
50536
|
-
import { join as
|
|
50830
|
+
import { join as join43 } from "path";
|
|
50537
50831
|
import { homedir as homedir4 } from "os";
|
|
50538
|
-
import { mkdir as
|
|
50832
|
+
import { mkdir as mkdir23 } from "fs/promises";
|
|
50539
50833
|
function resolveHome() {
|
|
50540
50834
|
return process.env.HOME ?? homedir4();
|
|
50541
50835
|
}
|
|
@@ -50785,11 +51079,11 @@ function getInstallPath2(shell) {
|
|
|
50785
51079
|
const home = resolveHome();
|
|
50786
51080
|
switch (shell) {
|
|
50787
51081
|
case "bash":
|
|
50788
|
-
return
|
|
51082
|
+
return join43(home, ".bashrc");
|
|
50789
51083
|
case "zsh":
|
|
50790
|
-
return
|
|
51084
|
+
return join43(home, ".zshrc");
|
|
50791
51085
|
case "fish":
|
|
50792
|
-
return
|
|
51086
|
+
return join43(home, ".config", "fish", "completions", "dbcli.fish");
|
|
50793
51087
|
default:
|
|
50794
51088
|
throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
50795
51089
|
}
|
|
@@ -50807,8 +51101,8 @@ function detectShell() {
|
|
|
50807
51101
|
async function installCompletion(shell, script) {
|
|
50808
51102
|
const targetPath = getInstallPath2(shell);
|
|
50809
51103
|
if (shell === "fish") {
|
|
50810
|
-
const dir =
|
|
50811
|
-
await
|
|
51104
|
+
const dir = join43(resolveHome(), ".config", "fish", "completions");
|
|
51105
|
+
await mkdir23(dir, { recursive: true });
|
|
50812
51106
|
await Bun.file(targetPath).write(script);
|
|
50813
51107
|
console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
|
|
50814
51108
|
return;
|
|
@@ -51147,6 +51441,12 @@ var COMMAND_LOADERS = {
|
|
|
51147
51441
|
program2.addCommand(migrateCommand2);
|
|
51148
51442
|
};
|
|
51149
51443
|
},
|
|
51444
|
+
password: async () => {
|
|
51445
|
+
const { passwordCommand: passwordCommand2 } = await Promise.resolve().then(() => (init_credential(), exports_credential));
|
|
51446
|
+
return (program2) => {
|
|
51447
|
+
program2.addCommand(passwordCommand2);
|
|
51448
|
+
};
|
|
51449
|
+
},
|
|
51150
51450
|
use: async () => {
|
|
51151
51451
|
const { useCommand: useCommand2 } = await Promise.resolve().then(() => (init_use(), exports_use));
|
|
51152
51452
|
return (program2) => {
|
|
@@ -51287,7 +51587,7 @@ async function buildProgramFor(argv) {
|
|
|
51287
51587
|
|
|
51288
51588
|
// src/cli-runtime.ts
|
|
51289
51589
|
init_cli_error();
|
|
51290
|
-
import { join as
|
|
51590
|
+
import { join as join44 } from "path";
|
|
51291
51591
|
import { writeSync as writeSync2 } from "fs";
|
|
51292
51592
|
import { format } from "util";
|
|
51293
51593
|
function installSynchronousRedirectedStdout() {
|
|
@@ -51425,7 +51725,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
51425
51725
|
try {
|
|
51426
51726
|
let cache = null;
|
|
51427
51727
|
try {
|
|
51428
|
-
const cacheFile = Bun.file(
|
|
51728
|
+
const cacheFile = Bun.file(join44(configPath, "version-check.json"));
|
|
51429
51729
|
if (await cacheFile.exists()) {
|
|
51430
51730
|
cache = await cacheFile.json();
|
|
51431
51731
|
}
|