@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.
@@ -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.55.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
- return match?.[1] != null ? match[1].trim() : null;
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 mkdir18, readFile as readFile9, stat as stat9 } from "fs/promises";
45764
- import { dirname as dirname16 } from "path";
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 mkdir18(dirname16(this.path), { recursive: true });
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 readFile9(this.path, "utf8");
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 readFile10 } from "fs/promises";
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 readFile10(file, "utf8");
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 join39 } from "path";
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 ?? join39(".dbcli", "proxy", "events.jsonl");
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", join39(".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");
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 ?? join39(".dbcli", "proxy", "events.jsonl");
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", join39(".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) => {
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 join40 } from "path";
47672
+ import { join as join41 } from "path";
47382
47673
  function defaultDesignFile(workspaceRoot) {
47383
- return join40(workspaceRoot, DEFAULT_FILE3);
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 dirname17, resolve as resolve10 } from "path";
48215
- import { mkdir as mkdir19 } from "fs/promises";
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 mkdir19(dirname17(out), { recursive: true });
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 mkdir20, realpath as realpath2, unlink as unlink8, writeFile as writeFile9 } from "fs/promises";
48275
- import { basename as basename9, dirname as dirname18, relative as relative5, resolve as resolve11, sep as sep7 } from "path";
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 = dirname18(candidate);
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 = dirname18(requestedTarget);
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 mkdir20(parent, { recursive: true });
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 writeFile9(temp, `${JSON.stringify(pack, null, 2)}
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 join41, relative as relative6, resolve as resolve12, sep as sep8 } from "path";
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 = join41(storagePath, ".dbcli", "audit", `${connectionName}.jsonl`);
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 mkdir21, realpath as realpath5, unlink as unlink9, writeFile as writeFile10 } from "fs/promises";
50078
- import { basename as basename10, dirname as dirname19, relative as relative8, resolve as resolve14, sep as sep10 } from "path";
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 = dirname19(candidate);
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 = dirname19(target);
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 mkdir21(parent, { recursive: true });
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(dirname19(path4));
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(dirname19(path4), `.${basename10(path4)}.${process.pid}.${randomUUID7()}.tmp`);
50611
+ const temp = resolve14(dirname20(path4), `.${basename10(path4)}.${process.pid}.${randomUUID7()}.tmp`);
50321
50612
  try {
50322
- await writeFile10(temp, content, "utf8");
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 join42 } from "path";
50830
+ import { join as join43 } from "path";
50538
50831
  import { homedir as homedir4 } from "os";
50539
- import { mkdir as mkdir22 } from "fs/promises";
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 join42(home, ".bashrc");
51082
+ return join43(home, ".bashrc");
50790
51083
  case "zsh":
50791
- return join42(home, ".zshrc");
51084
+ return join43(home, ".zshrc");
50792
51085
  case "fish":
50793
- return join42(home, ".config", "fish", "completions", "dbcli.fish");
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 = join42(resolveHome(), ".config", "fish", "completions");
50812
- await mkdir22(dir, { recursive: true });
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 join43 } from "path";
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(join43(configPath, "version-check.json"));
51728
+ const cacheFile = Bun.file(join44(configPath, "version-check.json"));
51430
51729
  if (await cacheFile.exists()) {
51431
51730
  cache = await cacheFile.json();
51432
51731
  }