@carllee1983/dbcli 1.55.1 → 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 +22 -0
- package/README.dev.md +1 -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 -45
- package/dist/cli.mjs +1 -2
- package/dist/core.d.ts +144 -122
- package/dist/core.mjs +175 -37
- package/package.json +1 -2
- 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: {
|
|
@@ -61,7 +61,6 @@ var init_package = __esm(() => {
|
|
|
61
61
|
dbcli: "./dist/cli.mjs"
|
|
62
62
|
},
|
|
63
63
|
exports: {
|
|
64
|
-
".": "./dist/cli.mjs",
|
|
65
64
|
"./core": {
|
|
66
65
|
types: "./dist/core.d.ts",
|
|
67
66
|
import: "./dist/core.mjs"
|
|
@@ -6233,7 +6232,11 @@ function resolveEnvReferences(config, env, parentKey) {
|
|
|
6233
6232
|
}
|
|
6234
6233
|
function parseEnvPassword(content) {
|
|
6235
6234
|
const match = content.match(/^DBCLI_PASSWORD=(.+)$/m);
|
|
6236
|
-
|
|
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;
|
|
6237
6240
|
}
|
|
6238
6241
|
var _globalConnectionName, DEFAULT_CONFIG, configModule;
|
|
6239
6242
|
var init_config = __esm(() => {
|
|
@@ -9059,6 +9062,20 @@ Reference: {referencePath}`,
|
|
|
9059
9062
|
add_enum_description: "Create an enum type (PostgreSQL only)",
|
|
9060
9063
|
alter_enum_description: "Add a value to an enum type",
|
|
9061
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."
|
|
9062
9079
|
}
|
|
9063
9080
|
};
|
|
9064
9081
|
});
|
|
@@ -9343,6 +9360,20 @@ var init_messages2 = __esm(() => {
|
|
|
9343
9360
|
add_enum_description: "\u5EFA\u7ACB\u5217\u8209\u578B\u5225\uFF08\u50C5 PostgreSQL\uFF09",
|
|
9344
9361
|
alter_enum_description: "\u65B0\u589E\u5217\u8209\u503C",
|
|
9345
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"
|
|
9346
9377
|
}
|
|
9347
9378
|
};
|
|
9348
9379
|
});
|
|
@@ -9652,6 +9683,13 @@ async function select(message, choices) {
|
|
|
9652
9683
|
return await selectFallback(message, choices);
|
|
9653
9684
|
return await inquirer.select({ message, choices });
|
|
9654
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
|
+
}
|
|
9655
9693
|
async function confirm(message) {
|
|
9656
9694
|
if (!process.stdin.isTTY)
|
|
9657
9695
|
return await confirmFallback(message);
|
|
@@ -9665,7 +9703,8 @@ var init_prompts = __esm(() => {
|
|
|
9665
9703
|
promptUser = {
|
|
9666
9704
|
text,
|
|
9667
9705
|
select,
|
|
9668
|
-
confirm
|
|
9706
|
+
confirm,
|
|
9707
|
+
secret
|
|
9669
9708
|
};
|
|
9670
9709
|
});
|
|
9671
9710
|
|
|
@@ -45686,6 +45725,258 @@ var init_use = __esm(() => {
|
|
|
45686
45725
|
});
|
|
45687
45726
|
});
|
|
45688
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
|
+
|
|
45689
45980
|
// src/proxy/relay.ts
|
|
45690
45981
|
class TcpRelay {
|
|
45691
45982
|
clientBytes = 0;
|
|
@@ -45760,8 +46051,8 @@ var init_sql_metadata = __esm(() => {
|
|
|
45760
46051
|
});
|
|
45761
46052
|
|
|
45762
46053
|
// src/proxy/events.ts
|
|
45763
|
-
import { appendFile as appendFile2, mkdir as
|
|
45764
|
-
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";
|
|
45765
46056
|
function hasSql(e) {
|
|
45766
46057
|
return e.type === "query_observed" || e.type === "query_completed" || e.type === "query_errored";
|
|
45767
46058
|
}
|
|
@@ -45800,7 +46091,7 @@ class EventWriter {
|
|
|
45800
46091
|
}
|
|
45801
46092
|
async writeInternal(event) {
|
|
45802
46093
|
if (!this.dirEnsured) {
|
|
45803
|
-
await
|
|
46094
|
+
await mkdir19(dirname17(this.path), { recursive: true });
|
|
45804
46095
|
this.dirEnsured = true;
|
|
45805
46096
|
}
|
|
45806
46097
|
if (!this.initialized) {
|
|
@@ -45824,7 +46115,7 @@ class EventWriter {
|
|
|
45824
46115
|
try {
|
|
45825
46116
|
const s = await stat9(this.path);
|
|
45826
46117
|
this.currentSizeBytes = s.size;
|
|
45827
|
-
const raw = await
|
|
46118
|
+
const raw = await readFile10(this.path, "utf8");
|
|
45828
46119
|
this.currentEntryCount = raw.split(`
|
|
45829
46120
|
`).filter(Boolean).length;
|
|
45830
46121
|
} catch {
|
|
@@ -46421,7 +46712,7 @@ var init_server = __esm(() => {
|
|
|
46421
46712
|
});
|
|
46422
46713
|
|
|
46423
46714
|
// src/proxy/event-reader.ts
|
|
46424
|
-
import { readFile as
|
|
46715
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
46425
46716
|
async function readEvents(path4, opts) {
|
|
46426
46717
|
const candidates = opts.includeRotated ? [path4, `${path4}.1`] : [path4];
|
|
46427
46718
|
const files = [];
|
|
@@ -46430,7 +46721,7 @@ async function readEvents(path4, opts) {
|
|
|
46430
46721
|
for (const file of candidates) {
|
|
46431
46722
|
let raw;
|
|
46432
46723
|
try {
|
|
46433
|
-
raw = await
|
|
46724
|
+
raw = await readFile11(file, "utf8");
|
|
46434
46725
|
} catch (err) {
|
|
46435
46726
|
if (err.code !== "ENOENT")
|
|
46436
46727
|
throw err;
|
|
@@ -46861,7 +47152,7 @@ __export(exports_proxy, {
|
|
|
46861
47152
|
proxyCommand: () => proxyCommand,
|
|
46862
47153
|
parseHostPort: () => parseHostPort
|
|
46863
47154
|
});
|
|
46864
|
-
import { join as
|
|
47155
|
+
import { join as join40 } from "path";
|
|
46865
47156
|
function parseHostPort(value) {
|
|
46866
47157
|
const idx = value.lastIndexOf(":");
|
|
46867
47158
|
if (idx <= 0 || idx === value.length - 1) {
|
|
@@ -46924,7 +47215,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46924
47215
|
target: options.target,
|
|
46925
47216
|
connection
|
|
46926
47217
|
});
|
|
46927
|
-
const eventsPath = options.events ??
|
|
47218
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46928
47219
|
const slowMs = Number(options.slowMs ?? 1000);
|
|
46929
47220
|
if (!Number.isFinite(slowMs) || slowMs < 0) {
|
|
46930
47221
|
throw new Error(`Invalid --slow-ms "${options.slowMs}". Expected a non-negative number`);
|
|
@@ -46973,7 +47264,7 @@ async function runProxy(subcommandEngine, options, command) {
|
|
|
46973
47264
|
}
|
|
46974
47265
|
}
|
|
46975
47266
|
function addCommonOptions(cmd) {
|
|
46976
|
-
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");
|
|
46977
47268
|
}
|
|
46978
47269
|
function parseNonNegInt(value, flag, fallback) {
|
|
46979
47270
|
if (value === undefined)
|
|
@@ -46991,7 +47282,7 @@ async function runAnalyze(options) {
|
|
|
46991
47282
|
const top = parseNonNegInt(options.top, "top", 20);
|
|
46992
47283
|
const slowMs = parseNonNegInt(options.slowMs, "slow-ms", 1000);
|
|
46993
47284
|
const nPlusOne = parseNonNegInt(options.nPlusOne, "n-plus-one", 10);
|
|
46994
|
-
const eventsPath = options.events ??
|
|
47285
|
+
const eventsPath = options.events ?? join40(".dbcli", "proxy", "events.jsonl");
|
|
46995
47286
|
const { events, malformedLines, files } = await readEvents(eventsPath, {
|
|
46996
47287
|
includeRotated: options.includeRotated !== false
|
|
46997
47288
|
});
|
|
@@ -47044,7 +47335,7 @@ var init_proxy = __esm(() => {
|
|
|
47044
47335
|
});
|
|
47045
47336
|
}
|
|
47046
47337
|
ANALYZE_FORMATS = ["json", "text", "markdown"];
|
|
47047
|
-
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) => {
|
|
47048
47339
|
await runAnalyze(options);
|
|
47049
47340
|
});
|
|
47050
47341
|
addCommonOptions(proxyCommand).action(async (options, command) => {
|
|
@@ -47378,9 +47669,9 @@ var init_semantic2 = __esm(() => {
|
|
|
47378
47669
|
});
|
|
47379
47670
|
|
|
47380
47671
|
// src/core/design/index.ts
|
|
47381
|
-
import { join as
|
|
47672
|
+
import { join as join41 } from "path";
|
|
47382
47673
|
function defaultDesignFile(workspaceRoot) {
|
|
47383
|
-
return
|
|
47674
|
+
return join41(workspaceRoot, DEFAULT_FILE3);
|
|
47384
47675
|
}
|
|
47385
47676
|
function parseDesignSpec(raw, filePath = DEFAULT_FILE3) {
|
|
47386
47677
|
const parsed = specSchema.safeParse(raw);
|
|
@@ -48211,8 +48502,8 @@ var exports_backfill = {};
|
|
|
48211
48502
|
__export(exports_backfill, {
|
|
48212
48503
|
backfillCommand: () => backfillCommand
|
|
48213
48504
|
});
|
|
48214
|
-
import { dirname as
|
|
48215
|
-
import { mkdir as
|
|
48505
|
+
import { dirname as dirname18, resolve as resolve10 } from "path";
|
|
48506
|
+
import { mkdir as mkdir20 } from "fs/promises";
|
|
48216
48507
|
function identityFor(config, name) {
|
|
48217
48508
|
const connection = config.connections[name];
|
|
48218
48509
|
if (!connection) {
|
|
@@ -48262,7 +48553,7 @@ var init_backfill = __esm(() => {
|
|
|
48262
48553
|
return;
|
|
48263
48554
|
}
|
|
48264
48555
|
const out = resolve10(options.out ?? `.dbcli/backfills/${artifact2.source.sha256.slice(0, 12)}.json`);
|
|
48265
|
-
await
|
|
48556
|
+
await mkdir20(dirname18(out), { recursive: true });
|
|
48266
48557
|
await Bun.write(out, JSON.stringify(artifact2, null, 2) + `
|
|
48267
48558
|
`);
|
|
48268
48559
|
console.log(JSON.stringify({ path: out, artifact: artifact2 }, null, 2));
|
|
@@ -48271,8 +48562,8 @@ var init_backfill = __esm(() => {
|
|
|
48271
48562
|
|
|
48272
48563
|
// src/core/evidence-pack/index.ts
|
|
48273
48564
|
import { createHash as createHash8, randomUUID as randomUUID6 } from "crypto";
|
|
48274
|
-
import { link as link3, lstat as lstat5, mkdir as
|
|
48275
|
-
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";
|
|
48276
48567
|
function isRecord4(value) {
|
|
48277
48568
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48278
48569
|
}
|
|
@@ -48523,7 +48814,7 @@ async function realExistingAncestor(path4) {
|
|
|
48523
48814
|
} catch (error) {
|
|
48524
48815
|
if (error.code !== "ENOENT")
|
|
48525
48816
|
throw error;
|
|
48526
|
-
const parent =
|
|
48817
|
+
const parent = dirname19(candidate);
|
|
48527
48818
|
if (parent === candidate)
|
|
48528
48819
|
throw error;
|
|
48529
48820
|
candidate = parent;
|
|
@@ -48535,12 +48826,12 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48535
48826
|
const requestedTarget = resolve11(workspace, outputPath);
|
|
48536
48827
|
if (!isInside(workspace, requestedTarget))
|
|
48537
48828
|
throw new EvidencePackValidationError("output path must stay inside the workspace");
|
|
48538
|
-
const parent =
|
|
48829
|
+
const parent = dirname19(requestedTarget);
|
|
48539
48830
|
const existingAncestor2 = await realExistingAncestor(parent);
|
|
48540
48831
|
if (!isInside(workspace, existingAncestor2) && existingAncestor2 !== workspace) {
|
|
48541
48832
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
48542
48833
|
}
|
|
48543
|
-
await
|
|
48834
|
+
await mkdir21(parent, { recursive: true });
|
|
48544
48835
|
const realParent = await realpath2(parent);
|
|
48545
48836
|
if (!isInside(workspace, realParent) && realParent !== workspace) {
|
|
48546
48837
|
throw new EvidencePackValidationError("output path resolves outside the workspace");
|
|
@@ -48557,7 +48848,7 @@ async function writeEvidencePack(workspaceRoot, outputPath, pack) {
|
|
|
48557
48848
|
}
|
|
48558
48849
|
const temp = resolve11(realParent, `.${basename9(target)}.${process.pid}.${randomUUID6()}.tmp`);
|
|
48559
48850
|
try {
|
|
48560
|
-
await
|
|
48851
|
+
await writeFile10(temp, `${JSON.stringify(pack, null, 2)}
|
|
48561
48852
|
`, "utf8");
|
|
48562
48853
|
try {
|
|
48563
48854
|
await link3(temp, target);
|
|
@@ -48634,7 +48925,7 @@ var exports_evidence = {};
|
|
|
48634
48925
|
__export(exports_evidence, {
|
|
48635
48926
|
evidenceCommand: () => evidenceCommand
|
|
48636
48927
|
});
|
|
48637
|
-
import { join as
|
|
48928
|
+
import { join as join42, relative as relative6, resolve as resolve12, sep as sep8 } from "path";
|
|
48638
48929
|
import { realpath as realpath3 } from "fs/promises";
|
|
48639
48930
|
import { createHash as createHash9 } from "crypto";
|
|
48640
48931
|
function safeMessage(error) {
|
|
@@ -48710,7 +49001,7 @@ async function auditEntries(configPath, config) {
|
|
|
48710
49001
|
throw new EvidencePackValidationError("audit evidence is disabled");
|
|
48711
49002
|
const storagePath = await resolveConfigStoragePath(configPath);
|
|
48712
49003
|
const connectionName = config.effectiveConnectionName || getGlobalConnectionName() || "default";
|
|
48713
|
-
const file =
|
|
49004
|
+
const file = join42(storagePath, ".dbcli", "audit", `${connectionName}.jsonl`);
|
|
48714
49005
|
return { connectionName, entries: await readEntries(file, { include_rotated: true }) };
|
|
48715
49006
|
}
|
|
48716
49007
|
function auditReference(entry, connectionName) {
|
|
@@ -50074,8 +50365,8 @@ __export(exports_impact, {
|
|
|
50074
50365
|
impactCommand: () => impactCommand
|
|
50075
50366
|
});
|
|
50076
50367
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
50077
|
-
import { link as link4, lstat as lstat8, mkdir as
|
|
50078
|
-
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";
|
|
50079
50370
|
function collectOption3(value, previous) {
|
|
50080
50371
|
return [...previous, value];
|
|
50081
50372
|
}
|
|
@@ -50279,7 +50570,7 @@ async function realExistingAncestor2(path4) {
|
|
|
50279
50570
|
} catch (error) {
|
|
50280
50571
|
if (error.code !== "ENOENT")
|
|
50281
50572
|
throw error;
|
|
50282
|
-
const parent =
|
|
50573
|
+
const parent = dirname20(candidate);
|
|
50283
50574
|
if (parent === candidate)
|
|
50284
50575
|
throw error;
|
|
50285
50576
|
candidate = parent;
|
|
@@ -50291,11 +50582,11 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50291
50582
|
const target = resolve14(workspace, outputPath);
|
|
50292
50583
|
if (!isInside2(workspace, target))
|
|
50293
50584
|
throw new Error("output path must stay inside the workspace");
|
|
50294
|
-
const parent =
|
|
50585
|
+
const parent = dirname20(target);
|
|
50295
50586
|
const existingAncestor2 = await realExistingAncestor2(parent);
|
|
50296
50587
|
if (!isInside2(workspace, existingAncestor2) && existingAncestor2 !== workspace)
|
|
50297
50588
|
throw new Error("output path must stay inside the workspace");
|
|
50298
|
-
await
|
|
50589
|
+
await mkdir22(parent, { recursive: true });
|
|
50299
50590
|
const realParent = await realpath5(parent);
|
|
50300
50591
|
if (!isInside2(workspace, realParent) && realParent !== workspace)
|
|
50301
50592
|
throw new Error("output path must stay inside the workspace");
|
|
@@ -50303,7 +50594,7 @@ async function resolveOutput(workspaceRoot, outputPath) {
|
|
|
50303
50594
|
}
|
|
50304
50595
|
async function assertOutputParent(workspaceRoot, path4) {
|
|
50305
50596
|
const workspace = await realpath5(workspaceRoot);
|
|
50306
|
-
const parent = await realpath5(
|
|
50597
|
+
const parent = await realpath5(dirname20(path4));
|
|
50307
50598
|
if (!isInside2(workspace, parent) && parent !== workspace) {
|
|
50308
50599
|
throw new Error("output path must stay inside the workspace");
|
|
50309
50600
|
}
|
|
@@ -50317,9 +50608,9 @@ async function writeOutput(workspaceRoot, path4, content) {
|
|
|
50317
50608
|
if (error.code !== "ENOENT")
|
|
50318
50609
|
throw error;
|
|
50319
50610
|
}
|
|
50320
|
-
const temp = resolve14(
|
|
50611
|
+
const temp = resolve14(dirname20(path4), `.${basename10(path4)}.${process.pid}.${randomUUID7()}.tmp`);
|
|
50321
50612
|
try {
|
|
50322
|
-
await
|
|
50613
|
+
await writeFile11(temp, content, "utf8");
|
|
50323
50614
|
await assertOutputParent(workspaceRoot, path4);
|
|
50324
50615
|
await link4(temp, path4);
|
|
50325
50616
|
} catch (error) {
|
|
@@ -50458,6 +50749,7 @@ function buildProgram() {
|
|
|
50458
50749
|
program2.addCommand(shellCommand);
|
|
50459
50750
|
program2.addCommand(migrateCommand);
|
|
50460
50751
|
program2.addCommand(useCommand);
|
|
50752
|
+
program2.addCommand(passwordCommand);
|
|
50461
50753
|
program2.addCommand(queriesCommand);
|
|
50462
50754
|
program2.addCommand(explainCommand);
|
|
50463
50755
|
program2.addCommand(lintCommand);
|
|
@@ -50511,6 +50803,7 @@ var init_program = __esm(() => {
|
|
|
50511
50803
|
init_shell3();
|
|
50512
50804
|
init_migrate();
|
|
50513
50805
|
init_use();
|
|
50806
|
+
init_credential();
|
|
50514
50807
|
init_proxy();
|
|
50515
50808
|
init_semantic2();
|
|
50516
50809
|
init_design2();
|
|
@@ -50534,9 +50827,9 @@ __export(exports_completion, {
|
|
|
50534
50827
|
detectShell: () => detectShell,
|
|
50535
50828
|
completionCommand: () => completionCommand
|
|
50536
50829
|
});
|
|
50537
|
-
import { join as
|
|
50830
|
+
import { join as join43 } from "path";
|
|
50538
50831
|
import { homedir as homedir4 } from "os";
|
|
50539
|
-
import { mkdir as
|
|
50832
|
+
import { mkdir as mkdir23 } from "fs/promises";
|
|
50540
50833
|
function resolveHome() {
|
|
50541
50834
|
return process.env.HOME ?? homedir4();
|
|
50542
50835
|
}
|
|
@@ -50786,11 +51079,11 @@ function getInstallPath2(shell) {
|
|
|
50786
51079
|
const home = resolveHome();
|
|
50787
51080
|
switch (shell) {
|
|
50788
51081
|
case "bash":
|
|
50789
|
-
return
|
|
51082
|
+
return join43(home, ".bashrc");
|
|
50790
51083
|
case "zsh":
|
|
50791
|
-
return
|
|
51084
|
+
return join43(home, ".zshrc");
|
|
50792
51085
|
case "fish":
|
|
50793
|
-
return
|
|
51086
|
+
return join43(home, ".config", "fish", "completions", "dbcli.fish");
|
|
50794
51087
|
default:
|
|
50795
51088
|
throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
50796
51089
|
}
|
|
@@ -50808,8 +51101,8 @@ function detectShell() {
|
|
|
50808
51101
|
async function installCompletion(shell, script) {
|
|
50809
51102
|
const targetPath = getInstallPath2(shell);
|
|
50810
51103
|
if (shell === "fish") {
|
|
50811
|
-
const dir =
|
|
50812
|
-
await
|
|
51104
|
+
const dir = join43(resolveHome(), ".config", "fish", "completions");
|
|
51105
|
+
await mkdir23(dir, { recursive: true });
|
|
50813
51106
|
await Bun.file(targetPath).write(script);
|
|
50814
51107
|
console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
|
|
50815
51108
|
return;
|
|
@@ -51148,6 +51441,12 @@ var COMMAND_LOADERS = {
|
|
|
51148
51441
|
program2.addCommand(migrateCommand2);
|
|
51149
51442
|
};
|
|
51150
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
|
+
},
|
|
51151
51450
|
use: async () => {
|
|
51152
51451
|
const { useCommand: useCommand2 } = await Promise.resolve().then(() => (init_use(), exports_use));
|
|
51153
51452
|
return (program2) => {
|
|
@@ -51288,7 +51587,7 @@ async function buildProgramFor(argv) {
|
|
|
51288
51587
|
|
|
51289
51588
|
// src/cli-runtime.ts
|
|
51290
51589
|
init_cli_error();
|
|
51291
|
-
import { join as
|
|
51590
|
+
import { join as join44 } from "path";
|
|
51292
51591
|
import { writeSync as writeSync2 } from "fs";
|
|
51293
51592
|
import { format } from "util";
|
|
51294
51593
|
function installSynchronousRedirectedStdout() {
|
|
@@ -51426,7 +51725,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
51426
51725
|
try {
|
|
51427
51726
|
let cache = null;
|
|
51428
51727
|
try {
|
|
51429
|
-
const cacheFile = Bun.file(
|
|
51728
|
+
const cacheFile = Bun.file(join44(configPath, "version-check.json"));
|
|
51430
51729
|
if (await cacheFile.exists()) {
|
|
51431
51730
|
cache = await cacheFile.json();
|
|
51432
51731
|
}
|