@carllee1983/dbcli 1.17.0 → 1.18.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/dist/cli.mjs CHANGED
@@ -77413,6 +77413,7 @@ function renderStep2(r) {
77413
77413
  var NEXT_SCHEMA_VERSION = 1, STEP_RESULT_SUMMARY_FIELD_CAP = 4096;
77414
77414
 
77415
77415
  // src/core/recovery/next-step-schema.ts
77416
+ import { stat as stat2 } from "fs/promises";
77416
77417
  import { resolve as resolve2 } from "path";
77417
77418
  function summarizeZodError(err) {
77418
77419
  return err.issues.map((iss) => {
@@ -77433,6 +77434,11 @@ async function loadStepResultSummary(arg, cwd) {
77433
77434
  let raw;
77434
77435
  if (arg.startsWith("@")) {
77435
77436
  const path3 = resolve2(cwd, arg.slice(1));
77437
+ try {
77438
+ await stat2(path3);
77439
+ } catch {
77440
+ return { ok: false, reason: `--result @<file>: ${path3} not readable.` };
77441
+ }
77436
77442
  let buf;
77437
77443
  try {
77438
77444
  buf = new Uint8Array(await Bun.file(path3).arrayBuffer());
@@ -77572,6 +77578,33 @@ var init_recovery = __esm(() => {
77572
77578
  init_next_step_schema();
77573
77579
  });
77574
77580
 
77581
+ // src/utils/package-root.ts
77582
+ import * as path3 from "path";
77583
+ function findPackageRoot() {
77584
+ if (cached)
77585
+ return cached;
77586
+ let dir = HERE;
77587
+ for (let i = 0;i < 6; i++) {
77588
+ if (Bun.file(path3.join(dir, "package.json")).size > 0) {
77589
+ cached = dir;
77590
+ return dir;
77591
+ }
77592
+ const parent = path3.dirname(dir);
77593
+ if (parent === dir)
77594
+ break;
77595
+ dir = parent;
77596
+ }
77597
+ cached = path3.resolve(HERE, "..", "..");
77598
+ return cached;
77599
+ }
77600
+ function packageAssetPath(...segments) {
77601
+ return path3.join(findPackageRoot(), "assets", ...segments);
77602
+ }
77603
+ var cached = null, HERE;
77604
+ var init_package_root = __esm(() => {
77605
+ HERE = import.meta.dir;
77606
+ });
77607
+
77575
77608
  // src/utils/levenshtein-distance.ts
77576
77609
  function levenshteinDistance(a, b) {
77577
77610
  const lenA = a.length;
@@ -78313,6 +78346,7 @@ function parseFrontmatter(yaml, input) {
78313
78346
  const tags = Array.isArray(raw.tags) ? raw.tags.map(String) : [];
78314
78347
  const index = typeof raw.index === "string" ? raw.index : undefined;
78315
78348
  const intent = normaliseIntent(raw.intent, input);
78349
+ const visual = normaliseVisual(raw.visual);
78316
78350
  return {
78317
78351
  meta: {
78318
78352
  name: typeof raw.name === "string" ? raw.name : "",
@@ -78322,11 +78356,54 @@ function parseFrontmatter(yaml, input) {
78322
78356
  index,
78323
78357
  params,
78324
78358
  tags,
78325
- intent
78359
+ intent,
78360
+ visual
78326
78361
  },
78327
78362
  warnings
78328
78363
  };
78329
78364
  }
78365
+ function normaliseVisual(value) {
78366
+ if (value === undefined || value === null || typeof value !== "object")
78367
+ return;
78368
+ const raw = value;
78369
+ const title = typeof raw.title === "string" ? raw.title : undefined;
78370
+ const kpis = [];
78371
+ if (Array.isArray(raw.kpis)) {
78372
+ for (const item of raw.kpis) {
78373
+ if (typeof item === "object" && item !== null) {
78374
+ const k = item;
78375
+ if (typeof k.label === "string" && typeof k.value_column === "string") {
78376
+ kpis.push({
78377
+ label: k.label,
78378
+ value_column: k.value_column,
78379
+ format: typeof k.format === "string" ? k.format : undefined
78380
+ });
78381
+ }
78382
+ }
78383
+ }
78384
+ }
78385
+ const charts = [];
78386
+ if (Array.isArray(raw.charts)) {
78387
+ for (const item of raw.charts) {
78388
+ if (typeof item === "object" && item !== null) {
78389
+ const c = item;
78390
+ if (typeof c.type === "string" && typeof c.x === "string" && Array.isArray(c.y)) {
78391
+ charts.push({
78392
+ type: c.type,
78393
+ title: typeof c.title === "string" ? c.title : undefined,
78394
+ x: c.x,
78395
+ y: c.y.map(String)
78396
+ });
78397
+ }
78398
+ }
78399
+ }
78400
+ }
78401
+ return {
78402
+ title,
78403
+ kpis: kpis.length > 0 ? kpis : undefined,
78404
+ charts: charts.length > 0 ? charts : undefined
78405
+ };
78406
+ }
78330
78407
  function normaliseIntent(value, input) {
78331
78408
  if (value === undefined || value === null)
78332
78409
  return;
@@ -78483,7 +78560,7 @@ var init_parser = __esm(() => {
78483
78560
 
78484
78561
  // src/core/saved-queries/loader.ts
78485
78562
  import { readdir } from "fs/promises";
78486
- import { join as join12, relative, sep } from "path";
78563
+ import { join as join14, relative, sep } from "path";
78487
78564
  async function loadSnippets(opts) {
78488
78565
  const builtin = await walkAndParse(opts.builtinDir, "builtin");
78489
78566
  const shared = await walkAndParse(opts.sharedDir, "shared");
@@ -78550,7 +78627,7 @@ async function collectFiles(root) {
78550
78627
  async function walk(dir) {
78551
78628
  const entries = await readdir(dir, { withFileTypes: true });
78552
78629
  for (const e of entries) {
78553
- const full = join12(dir, e.name);
78630
+ const full = join14(dir, e.name);
78554
78631
  if (e.isDirectory())
78555
78632
  await walk(full);
78556
78633
  else
@@ -78633,51 +78710,24 @@ var init_runner = __esm(() => {
78633
78710
  init_types4();
78634
78711
  });
78635
78712
 
78636
- // src/utils/package-root.ts
78637
- import * as path3 from "path";
78638
- function findPackageRoot() {
78639
- if (cached)
78640
- return cached;
78641
- let dir = HERE;
78642
- for (let i = 0;i < 6; i++) {
78643
- if (Bun.file(path3.join(dir, "package.json")).size > 0) {
78644
- cached = dir;
78645
- return dir;
78646
- }
78647
- const parent = path3.dirname(dir);
78648
- if (parent === dir)
78649
- break;
78650
- dir = parent;
78651
- }
78652
- cached = path3.resolve(HERE, "..", "..");
78653
- return cached;
78654
- }
78655
- function packageAssetPath(...segments) {
78656
- return path3.join(findPackageRoot(), "assets", ...segments);
78657
- }
78658
- var cached = null, HERE;
78659
- var init_package_root = __esm(() => {
78660
- HERE = import.meta.dir;
78661
- });
78662
-
78663
78713
  // src/core/saved-queries/snippet-paths.ts
78664
- import { join as join14 } from "path";
78714
+ import { join as join15 } from "path";
78665
78715
  function resolveBuiltinDir() {
78666
78716
  return packageAssetPath("snippets");
78667
78717
  }
78668
78718
  function resolveSnippetDirs(workspaceRoot) {
78669
78719
  return {
78670
78720
  builtinDir: resolveBuiltinDir(),
78671
- sharedDir: join14(workspaceRoot, ".dbcli-shared", "queries"),
78672
- localDir: join14(workspaceRoot, ".dbcli", "queries")
78721
+ sharedDir: join15(workspaceRoot, ".dbcli-shared", "queries"),
78722
+ localDir: join15(workspaceRoot, ".dbcli", "queries")
78673
78723
  };
78674
78724
  }
78675
78725
  function snippetKeyToFile(workspaceRoot, key, source) {
78676
78726
  const rel = key.replace(/^@/, "") + ".sql";
78677
78727
  if (source === "builtin")
78678
- return join14(resolveBuiltinDir(), rel);
78728
+ return join15(resolveBuiltinDir(), rel);
78679
78729
  const dir = source === "shared" ? ".dbcli-shared/queries" : ".dbcli/queries";
78680
- return join14(workspaceRoot, dir, rel);
78730
+ return join15(workspaceRoot, dir, rel);
78681
78731
  }
78682
78732
  var init_snippet_paths = __esm(() => {
78683
78733
  init_package_root();
@@ -78864,21 +78914,21 @@ var exports_queries_import = {};
78864
78914
  __export(exports_queries_import, {
78865
78915
  queriesImport: () => queriesImport
78866
78916
  });
78867
- import { stat as stat2, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
78868
- import { basename as basename3, join as join15, extname } from "path";
78917
+ import { stat as stat3, mkdir as mkdir5, copyFile as copyFile2 } from "fs/promises";
78918
+ import { basename as basename3, join as join17, extname } from "path";
78869
78919
  async function queriesImport(filePath, options = {}) {
78870
78920
  const cwd = options.cwd ?? process.cwd();
78871
78921
  if (extname(filePath) !== ".sql") {
78872
78922
  throw new Error(`Expected .sql file, got ${filePath}`);
78873
78923
  }
78874
- await stat2(filePath);
78924
+ await stat3(filePath);
78875
78925
  const text2 = await Bun.file(filePath).text();
78876
78926
  const baseName = options.as ? options.as.replace(/^@/, "") : basename3(filePath, ".sql").replace(/\.(postgres|mysql)$/, "");
78877
78927
  const key = "@" + baseName;
78878
78928
  parseSavedQuery({ key, file: filePath, source: "local", text: text2 });
78879
- const targetDir = join15(cwd, ".dbcli/queries");
78929
+ const targetDir = join17(cwd, ".dbcli/queries");
78880
78930
  await mkdir5(targetDir, { recursive: true });
78881
- const target = join15(targetDir, basename3(filePath));
78931
+ const target = join17(targetDir, basename3(filePath));
78882
78932
  if (await Bun.file(target).exists()) {
78883
78933
  if (!options.force) {
78884
78934
  const ok = await dist_default6({ message: `Overwrite ${target}?`, default: false });
@@ -79003,7 +79053,7 @@ var init_collect_snippets = __esm(() => {
79003
79053
  });
79004
79054
 
79005
79055
  // src/core/inspect/collect-schema-cache.ts
79006
- import { join as join19 } from "path";
79056
+ import { join as join21 } from "path";
79007
79057
  async function collectSchemaCache(opts) {
79008
79058
  const warnings = [];
79009
79059
  if (opts.system && !SQL_SYSTEMS.includes(opts.system)) {
@@ -79013,7 +79063,7 @@ async function collectSchemaCache(opts) {
79013
79063
  };
79014
79064
  }
79015
79065
  const root = resolveSchemaPath(opts.dbcliPath, opts.connectionName);
79016
- const indexPath = join19(root, "index.json");
79066
+ const indexPath = join21(root, "index.json");
79017
79067
  const file = Bun.file(indexPath);
79018
79068
  if (!await file.exists()) {
79019
79069
  return { section: { available: false }, warnings };
@@ -79125,7 +79175,7 @@ var init_suggest_commands = __esm(() => {
79125
79175
  });
79126
79176
 
79127
79177
  // src/core/inspect/collector.ts
79128
- import { join as join20 } from "path";
79178
+ import { join as join22 } from "path";
79129
79179
  async function collectInspect(opts) {
79130
79180
  const warnings = [];
79131
79181
  let config = null;
@@ -79201,11 +79251,11 @@ async function collectInspect(opts) {
79201
79251
  return { ...snapWithoutSuggestions, suggestedCommands, warnings };
79202
79252
  }
79203
79253
  async function hasConfig(configPath) {
79204
- if (await Bun.file(join20(configPath, "config.json")).exists())
79254
+ if (await Bun.file(join22(configPath, "config.json")).exists())
79205
79255
  return true;
79206
79256
  if (await Bun.file(configPath).exists()) {
79207
- const stat3 = await Bun.file(configPath).stat().catch(() => null);
79208
- return stat3?.isFile() === true;
79257
+ const stat4 = await Bun.file(configPath).stat().catch(() => null);
79258
+ return stat4?.isFile() === true;
79209
79259
  }
79210
79260
  return false;
79211
79261
  }
@@ -79371,7 +79421,7 @@ var {
79371
79421
  // package.json
79372
79422
  var package_default = {
79373
79423
  name: "@carllee1983/dbcli",
79374
- version: "1.17.0",
79424
+ version: "1.18.0",
79375
79425
  description: "Database CLI for AI agents",
79376
79426
  type: "module",
79377
79427
  publishConfig: {
@@ -79431,18 +79481,28 @@ var package_default = {
79431
79481
  "cli-table3": "^0.6.5",
79432
79482
  commander: "13.0.0",
79433
79483
  "lru-cache": "^11.3.6",
79484
+ "lucide-react": "^1.14.0",
79434
79485
  mongodb: "^7.2.0",
79435
79486
  mysql2: "^3.22.3",
79487
+ open: "^11.0.0",
79436
79488
  pg: "^8.20.0",
79437
79489
  picocolors: "^1.1.1",
79490
+ react: "^19.2.6",
79491
+ "react-dom": "^19.2.6",
79492
+ recharts: "^3.8.1",
79438
79493
  zod: "^3.25.76"
79439
79494
  },
79440
79495
  devDependencies: {
79441
79496
  "@eslint/js": "^9.39.4",
79442
79497
  "@inquirer/prompts": "^8.4.2",
79443
79498
  "@types/bun": "latest",
79499
+ "@types/react": "^19.2.14",
79500
+ "@types/react-dom": "^19.2.3",
79501
+ autoprefixer: "^10.5.0",
79444
79502
  eslint: "^10.3.0",
79503
+ postcss: "^8.5.14",
79445
79504
  prettier: "^3.8.3",
79505
+ tailwindcss: "3.4.1",
79446
79506
  typescript: "^5.9.3",
79447
79507
  "typescript-eslint": "^8.59.2"
79448
79508
  }
@@ -81558,6 +81618,648 @@ async function writeSchema(configPath, config, connectionName) {
81558
81618
  init_message_loader();
81559
81619
  init_adapters();
81560
81620
 
81621
+ // src/formatters/html-formatter.ts
81622
+ init_package_root();
81623
+ async function generateHtmlReport(payload) {
81624
+ const templatePath = packageAssetPath("ui-template.html");
81625
+ const templateFile = Bun.file(templatePath);
81626
+ if (!await templateFile.exists()) {
81627
+ throw new Error(`UI template not found at ${templatePath}. Please run 'bun run build' first.`);
81628
+ }
81629
+ let html = await templateFile.text();
81630
+ const jsonPayload = JSON.stringify(payload).replace(/</g, "\\u003c");
81631
+ const injection = `window.__DBCLI_PAYLOAD__ = ${jsonPayload};`;
81632
+ html = html.replace("/*DBCLI_PAYLOAD*/", () => injection);
81633
+ return html;
81634
+ }
81635
+
81636
+ // node_modules/open/index.js
81637
+ import process10 from "process";
81638
+ import path4 from "path";
81639
+ import { fileURLToPath } from "url";
81640
+ import childProcess3 from "child_process";
81641
+ import fs5, { constants as fsConstants2 } from "fs/promises";
81642
+
81643
+ // node_modules/wsl-utils/index.js
81644
+ import { promisify as promisify2 } from "util";
81645
+ import childProcess2 from "child_process";
81646
+ import fs4, { constants as fsConstants } from "fs/promises";
81647
+
81648
+ // node_modules/is-wsl/index.js
81649
+ import process4 from "process";
81650
+ import os2 from "os";
81651
+ import fs3 from "fs";
81652
+
81653
+ // node_modules/is-inside-container/index.js
81654
+ import fs2 from "fs";
81655
+
81656
+ // node_modules/is-docker/index.js
81657
+ import fs from "fs";
81658
+ var isDockerCached;
81659
+ function hasDockerEnv() {
81660
+ try {
81661
+ fs.statSync("/.dockerenv");
81662
+ return true;
81663
+ } catch {
81664
+ return false;
81665
+ }
81666
+ }
81667
+ function hasDockerCGroup() {
81668
+ try {
81669
+ return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
81670
+ } catch {
81671
+ return false;
81672
+ }
81673
+ }
81674
+ function isDocker() {
81675
+ if (isDockerCached === undefined) {
81676
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
81677
+ }
81678
+ return isDockerCached;
81679
+ }
81680
+
81681
+ // node_modules/is-inside-container/index.js
81682
+ var cachedResult;
81683
+ var hasContainerEnv = () => {
81684
+ try {
81685
+ fs2.statSync("/run/.containerenv");
81686
+ return true;
81687
+ } catch {
81688
+ return false;
81689
+ }
81690
+ };
81691
+ function isInsideContainer() {
81692
+ if (cachedResult === undefined) {
81693
+ cachedResult = hasContainerEnv() || isDocker();
81694
+ }
81695
+ return cachedResult;
81696
+ }
81697
+
81698
+ // node_modules/is-wsl/index.js
81699
+ var isWsl = () => {
81700
+ if (process4.platform !== "linux") {
81701
+ return false;
81702
+ }
81703
+ if (os2.release().toLowerCase().includes("microsoft")) {
81704
+ if (isInsideContainer()) {
81705
+ return false;
81706
+ }
81707
+ return true;
81708
+ }
81709
+ try {
81710
+ if (fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
81711
+ return !isInsideContainer();
81712
+ }
81713
+ } catch {}
81714
+ if (fs3.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs3.existsSync("/run/WSL")) {
81715
+ return !isInsideContainer();
81716
+ }
81717
+ return false;
81718
+ };
81719
+ var is_wsl_default = process4.env.__IS_WSL_TEST__ ? isWsl : isWsl();
81720
+
81721
+ // node_modules/powershell-utils/index.js
81722
+ import process5 from "process";
81723
+ import { Buffer as Buffer2 } from "buffer";
81724
+ import { promisify } from "util";
81725
+ import childProcess from "child_process";
81726
+ var execFile = promisify(childProcess.execFile);
81727
+ var powerShellPath = () => `${process5.env.SYSTEMROOT || process5.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
81728
+ var executePowerShell = async (command, options = {}) => {
81729
+ const {
81730
+ powerShellPath: psPath,
81731
+ ...execFileOptions
81732
+ } = options;
81733
+ const encodedCommand = executePowerShell.encodeCommand(command);
81734
+ return execFile(psPath ?? powerShellPath(), [
81735
+ ...executePowerShell.argumentsPrefix,
81736
+ encodedCommand
81737
+ ], {
81738
+ encoding: "utf8",
81739
+ ...execFileOptions
81740
+ });
81741
+ };
81742
+ executePowerShell.argumentsPrefix = [
81743
+ "-NoProfile",
81744
+ "-NonInteractive",
81745
+ "-ExecutionPolicy",
81746
+ "Bypass",
81747
+ "-EncodedCommand"
81748
+ ];
81749
+ executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
81750
+ executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
81751
+
81752
+ // node_modules/wsl-utils/utilities.js
81753
+ function parseMountPointFromConfig(content) {
81754
+ for (const line of content.split(`
81755
+ `)) {
81756
+ if (/^\s*#/.test(line)) {
81757
+ continue;
81758
+ }
81759
+ const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
81760
+ if (!match) {
81761
+ continue;
81762
+ }
81763
+ return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
81764
+ }
81765
+ }
81766
+
81767
+ // node_modules/wsl-utils/index.js
81768
+ var execFile2 = promisify2(childProcess2.execFile);
81769
+ var wslDrivesMountPoint = (() => {
81770
+ const defaultMountPoint = "/mnt/";
81771
+ let mountPoint;
81772
+ return async function() {
81773
+ if (mountPoint) {
81774
+ return mountPoint;
81775
+ }
81776
+ const configFilePath = "/etc/wsl.conf";
81777
+ let isConfigFileExists = false;
81778
+ try {
81779
+ await fs4.access(configFilePath, fsConstants.F_OK);
81780
+ isConfigFileExists = true;
81781
+ } catch {}
81782
+ if (!isConfigFileExists) {
81783
+ return defaultMountPoint;
81784
+ }
81785
+ const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
81786
+ const parsedMountPoint = parseMountPointFromConfig(configContent);
81787
+ if (parsedMountPoint === undefined) {
81788
+ return defaultMountPoint;
81789
+ }
81790
+ mountPoint = parsedMountPoint;
81791
+ mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
81792
+ return mountPoint;
81793
+ };
81794
+ })();
81795
+ var powerShellPathFromWsl = async () => {
81796
+ const mountPoint = await wslDrivesMountPoint();
81797
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
81798
+ };
81799
+ var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
81800
+ var canAccessPowerShellPromise;
81801
+ var canAccessPowerShell = async () => {
81802
+ canAccessPowerShellPromise ??= (async () => {
81803
+ try {
81804
+ const psPath = await powerShellPath2();
81805
+ await fs4.access(psPath, fsConstants.X_OK);
81806
+ return true;
81807
+ } catch {
81808
+ return false;
81809
+ }
81810
+ })();
81811
+ return canAccessPowerShellPromise;
81812
+ };
81813
+ var wslDefaultBrowser = async () => {
81814
+ const psPath = await powerShellPath2();
81815
+ const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
81816
+ const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
81817
+ return stdout.trim();
81818
+ };
81819
+ var convertWslPathToWindows = async (path4) => {
81820
+ if (/^[a-z]+:\/\//i.test(path4)) {
81821
+ return path4;
81822
+ }
81823
+ try {
81824
+ const { stdout } = await execFile2("wslpath", ["-aw", path4], { encoding: "utf8" });
81825
+ return stdout.trim();
81826
+ } catch {
81827
+ return path4;
81828
+ }
81829
+ };
81830
+
81831
+ // node_modules/define-lazy-prop/index.js
81832
+ function defineLazyProperty(object, propertyName, valueGetter) {
81833
+ const define2 = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
81834
+ Object.defineProperty(object, propertyName, {
81835
+ configurable: true,
81836
+ enumerable: true,
81837
+ get() {
81838
+ const result = valueGetter();
81839
+ define2(result);
81840
+ return result;
81841
+ },
81842
+ set(value) {
81843
+ define2(value);
81844
+ }
81845
+ });
81846
+ return object;
81847
+ }
81848
+
81849
+ // node_modules/default-browser/index.js
81850
+ import { promisify as promisify6 } from "util";
81851
+ import process8 from "process";
81852
+ import { execFile as execFile6 } from "child_process";
81853
+
81854
+ // node_modules/default-browser-id/index.js
81855
+ import { promisify as promisify3 } from "util";
81856
+ import process6 from "process";
81857
+ import { execFile as execFile3 } from "child_process";
81858
+ var execFileAsync = promisify3(execFile3);
81859
+ async function defaultBrowserId() {
81860
+ if (process6.platform !== "darwin") {
81861
+ throw new Error("macOS only");
81862
+ }
81863
+ const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
81864
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
81865
+ const browserId = match?.groups.id ?? "com.apple.Safari";
81866
+ if (browserId === "com.apple.safari") {
81867
+ return "com.apple.Safari";
81868
+ }
81869
+ return browserId;
81870
+ }
81871
+
81872
+ // node_modules/run-applescript/index.js
81873
+ import process7 from "process";
81874
+ import { promisify as promisify4 } from "util";
81875
+ import { execFile as execFile4, execFileSync } from "child_process";
81876
+ var execFileAsync2 = promisify4(execFile4);
81877
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
81878
+ if (process7.platform !== "darwin") {
81879
+ throw new Error("macOS only");
81880
+ }
81881
+ const outputArguments = humanReadableOutput ? [] : ["-ss"];
81882
+ const execOptions = {};
81883
+ if (signal) {
81884
+ execOptions.signal = signal;
81885
+ }
81886
+ const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
81887
+ return stdout.trim();
81888
+ }
81889
+
81890
+ // node_modules/bundle-name/index.js
81891
+ async function bundleName(bundleId) {
81892
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
81893
+ tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
81894
+ }
81895
+
81896
+ // node_modules/default-browser/windows.js
81897
+ import { promisify as promisify5 } from "util";
81898
+ import { execFile as execFile5 } from "child_process";
81899
+ var execFileAsync3 = promisify5(execFile5);
81900
+ var windowsBrowserProgIds = {
81901
+ MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
81902
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
81903
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
81904
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
81905
+ ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
81906
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
81907
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
81908
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
81909
+ BraveHTML: { name: "Brave", id: "com.brave.Browser" },
81910
+ BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
81911
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
81912
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
81913
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
81914
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
81915
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
81916
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
81917
+ };
81918
+ var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
81919
+
81920
+ class UnknownBrowserError extends Error {
81921
+ }
81922
+ async function defaultBrowser(_execFileAsync = execFileAsync3) {
81923
+ const { stdout } = await _execFileAsync("reg", [
81924
+ "QUERY",
81925
+ " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
81926
+ "/v",
81927
+ "ProgId"
81928
+ ]);
81929
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
81930
+ if (!match) {
81931
+ throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
81932
+ }
81933
+ const { id } = match.groups;
81934
+ const dotIndex = id.lastIndexOf(".");
81935
+ const hyphenIndex = id.lastIndexOf("-");
81936
+ const baseIdByDot = dotIndex === -1 ? undefined : id.slice(0, dotIndex);
81937
+ const baseIdByHyphen = hyphenIndex === -1 ? undefined : id.slice(0, hyphenIndex);
81938
+ return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
81939
+ }
81940
+
81941
+ // node_modules/default-browser/index.js
81942
+ var execFileAsync4 = promisify6(execFile6);
81943
+ var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
81944
+ async function defaultBrowser2() {
81945
+ if (process8.platform === "darwin") {
81946
+ const id = await defaultBrowserId();
81947
+ const name = await bundleName(id);
81948
+ return { name, id };
81949
+ }
81950
+ if (process8.platform === "linux") {
81951
+ const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
81952
+ const id = stdout.trim();
81953
+ const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
81954
+ return { name, id };
81955
+ }
81956
+ if (process8.platform === "win32") {
81957
+ return defaultBrowser();
81958
+ }
81959
+ throw new Error("Only macOS, Linux, and Windows are supported");
81960
+ }
81961
+
81962
+ // node_modules/is-in-ssh/index.js
81963
+ import process9 from "process";
81964
+ var isInSsh = Boolean(process9.env.SSH_CONNECTION || process9.env.SSH_CLIENT || process9.env.SSH_TTY);
81965
+ var is_in_ssh_default = isInSsh;
81966
+
81967
+ // node_modules/open/index.js
81968
+ var fallbackAttemptSymbol = Symbol("fallbackAttempt");
81969
+ var __dirname2 = import.meta.url ? path4.dirname(fileURLToPath(import.meta.url)) : "";
81970
+ var localXdgOpenPath = path4.join(__dirname2, "xdg-open");
81971
+ var { platform, arch } = process10;
81972
+ var tryEachApp = async (apps, opener) => {
81973
+ if (apps.length === 0) {
81974
+ return;
81975
+ }
81976
+ const errors3 = [];
81977
+ for (const app of apps) {
81978
+ try {
81979
+ return await opener(app);
81980
+ } catch (error) {
81981
+ errors3.push(error);
81982
+ }
81983
+ }
81984
+ throw new AggregateError(errors3, "Failed to open in all supported apps");
81985
+ };
81986
+ var baseOpen = async (options) => {
81987
+ options = {
81988
+ wait: false,
81989
+ background: false,
81990
+ newInstance: false,
81991
+ allowNonzeroExitCode: false,
81992
+ ...options
81993
+ };
81994
+ const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
81995
+ delete options[fallbackAttemptSymbol];
81996
+ if (Array.isArray(options.app)) {
81997
+ return tryEachApp(options.app, (singleApp) => baseOpen({
81998
+ ...options,
81999
+ app: singleApp,
82000
+ [fallbackAttemptSymbol]: true
82001
+ }));
82002
+ }
82003
+ let { name: app, arguments: appArguments = [] } = options.app ?? {};
82004
+ appArguments = [...appArguments];
82005
+ if (Array.isArray(app)) {
82006
+ return tryEachApp(app, (appName) => baseOpen({
82007
+ ...options,
82008
+ app: {
82009
+ name: appName,
82010
+ arguments: appArguments
82011
+ },
82012
+ [fallbackAttemptSymbol]: true
82013
+ }));
82014
+ }
82015
+ if (app === "browser" || app === "browserPrivate") {
82016
+ const ids = {
82017
+ "com.google.chrome": "chrome",
82018
+ "google-chrome.desktop": "chrome",
82019
+ "com.brave.browser": "brave",
82020
+ "org.mozilla.firefox": "firefox",
82021
+ "firefox.desktop": "firefox",
82022
+ "com.microsoft.msedge": "edge",
82023
+ "com.microsoft.edge": "edge",
82024
+ "com.microsoft.edgemac": "edge",
82025
+ "microsoft-edge.desktop": "edge",
82026
+ "com.apple.safari": "safari"
82027
+ };
82028
+ const flags = {
82029
+ chrome: "--incognito",
82030
+ brave: "--incognito",
82031
+ firefox: "--private-window",
82032
+ edge: "--inPrivate"
82033
+ };
82034
+ let browser;
82035
+ if (is_wsl_default) {
82036
+ const progId = await wslDefaultBrowser();
82037
+ const browserInfo = _windowsBrowserProgIdMap.get(progId);
82038
+ browser = browserInfo ?? {};
82039
+ } else {
82040
+ browser = await defaultBrowser2();
82041
+ }
82042
+ if (browser.id in ids) {
82043
+ const browserName = ids[browser.id.toLowerCase()];
82044
+ if (app === "browserPrivate") {
82045
+ if (browserName === "safari") {
82046
+ throw new Error("Safari doesn't support opening in private mode via command line");
82047
+ }
82048
+ appArguments.push(flags[browserName]);
82049
+ }
82050
+ return baseOpen({
82051
+ ...options,
82052
+ app: {
82053
+ name: apps[browserName],
82054
+ arguments: appArguments
82055
+ }
82056
+ });
82057
+ }
82058
+ throw new Error(`${browser.name} is not supported as a default browser`);
82059
+ }
82060
+ let command;
82061
+ const cliArguments = [];
82062
+ const childProcessOptions = {};
82063
+ let shouldUseWindowsInWsl = false;
82064
+ if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
82065
+ shouldUseWindowsInWsl = await canAccessPowerShell();
82066
+ }
82067
+ if (platform === "darwin") {
82068
+ command = "open";
82069
+ if (options.wait) {
82070
+ cliArguments.push("--wait-apps");
82071
+ }
82072
+ if (options.background) {
82073
+ cliArguments.push("--background");
82074
+ }
82075
+ if (options.newInstance) {
82076
+ cliArguments.push("--new");
82077
+ }
82078
+ if (app) {
82079
+ cliArguments.push("-a", app);
82080
+ }
82081
+ } else if (platform === "win32" || shouldUseWindowsInWsl) {
82082
+ command = await powerShellPath2();
82083
+ cliArguments.push(...executePowerShell.argumentsPrefix);
82084
+ if (!is_wsl_default) {
82085
+ childProcessOptions.windowsVerbatimArguments = true;
82086
+ }
82087
+ if (is_wsl_default && options.target) {
82088
+ options.target = await convertWslPathToWindows(options.target);
82089
+ }
82090
+ const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
82091
+ if (options.wait) {
82092
+ encodedArguments.push("-Wait");
82093
+ }
82094
+ if (app) {
82095
+ encodedArguments.push(executePowerShell.escapeArgument(app));
82096
+ if (options.target) {
82097
+ appArguments.push(options.target);
82098
+ }
82099
+ } else if (options.target) {
82100
+ encodedArguments.push(executePowerShell.escapeArgument(options.target));
82101
+ }
82102
+ if (appArguments.length > 0) {
82103
+ appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
82104
+ encodedArguments.push("-ArgumentList", appArguments.join(","));
82105
+ }
82106
+ options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
82107
+ if (!options.wait) {
82108
+ childProcessOptions.stdio = "ignore";
82109
+ }
82110
+ } else {
82111
+ if (app) {
82112
+ command = app;
82113
+ } else {
82114
+ const isBundled = !__dirname2 || __dirname2 === "/";
82115
+ let exeLocalXdgOpen = false;
82116
+ try {
82117
+ await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
82118
+ exeLocalXdgOpen = true;
82119
+ } catch {}
82120
+ const useSystemXdgOpen = process10.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
82121
+ command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
82122
+ }
82123
+ if (appArguments.length > 0) {
82124
+ cliArguments.push(...appArguments);
82125
+ }
82126
+ if (!options.wait) {
82127
+ childProcessOptions.stdio = "ignore";
82128
+ childProcessOptions.detached = true;
82129
+ }
82130
+ }
82131
+ if (platform === "darwin" && appArguments.length > 0) {
82132
+ cliArguments.push("--args", ...appArguments);
82133
+ }
82134
+ if (options.target) {
82135
+ cliArguments.push(options.target);
82136
+ }
82137
+ const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
82138
+ if (options.wait) {
82139
+ return new Promise((resolve4, reject) => {
82140
+ subprocess.once("error", reject);
82141
+ subprocess.once("close", (exitCode) => {
82142
+ if (!options.allowNonzeroExitCode && exitCode !== 0) {
82143
+ reject(new Error(`Exited with code ${exitCode}`));
82144
+ return;
82145
+ }
82146
+ resolve4(subprocess);
82147
+ });
82148
+ });
82149
+ }
82150
+ if (isFallbackAttempt) {
82151
+ return new Promise((resolve4, reject) => {
82152
+ subprocess.once("error", reject);
82153
+ subprocess.once("spawn", () => {
82154
+ subprocess.once("close", (exitCode) => {
82155
+ subprocess.off("error", reject);
82156
+ if (exitCode !== 0) {
82157
+ reject(new Error(`Exited with code ${exitCode}`));
82158
+ return;
82159
+ }
82160
+ subprocess.unref();
82161
+ resolve4(subprocess);
82162
+ });
82163
+ });
82164
+ });
82165
+ }
82166
+ subprocess.unref();
82167
+ return new Promise((resolve4, reject) => {
82168
+ subprocess.once("error", reject);
82169
+ subprocess.once("spawn", () => {
82170
+ subprocess.off("error", reject);
82171
+ resolve4(subprocess);
82172
+ });
82173
+ });
82174
+ };
82175
+ var open = (target, options) => {
82176
+ if (typeof target !== "string") {
82177
+ throw new TypeError("Expected a `target`");
82178
+ }
82179
+ return baseOpen({
82180
+ ...options,
82181
+ target
82182
+ });
82183
+ };
82184
+ function detectArchBinary(binary) {
82185
+ if (typeof binary === "string" || Array.isArray(binary)) {
82186
+ return binary;
82187
+ }
82188
+ const { [arch]: archBinary } = binary;
82189
+ if (!archBinary) {
82190
+ throw new Error(`${arch} is not supported`);
82191
+ }
82192
+ return archBinary;
82193
+ }
82194
+ function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
82195
+ if (wsl && is_wsl_default) {
82196
+ return detectArchBinary(wsl);
82197
+ }
82198
+ if (!platformBinary) {
82199
+ throw new Error(`${platform} is not supported`);
82200
+ }
82201
+ return detectArchBinary(platformBinary);
82202
+ }
82203
+ var apps = {
82204
+ browser: "browser",
82205
+ browserPrivate: "browserPrivate"
82206
+ };
82207
+ defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
82208
+ darwin: "google chrome",
82209
+ win32: "chrome",
82210
+ linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
82211
+ }, {
82212
+ wsl: {
82213
+ ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
82214
+ x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
82215
+ }
82216
+ }));
82217
+ defineLazyProperty(apps, "brave", () => detectPlatformBinary({
82218
+ darwin: "brave browser",
82219
+ win32: "brave",
82220
+ linux: ["brave-browser", "brave"]
82221
+ }, {
82222
+ wsl: {
82223
+ ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
82224
+ x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
82225
+ }
82226
+ }));
82227
+ defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
82228
+ darwin: "firefox",
82229
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
82230
+ linux: "firefox"
82231
+ }, {
82232
+ wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
82233
+ }));
82234
+ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
82235
+ darwin: "microsoft edge",
82236
+ win32: "msedge",
82237
+ linux: ["microsoft-edge", "microsoft-edge-dev"]
82238
+ }, {
82239
+ wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
82240
+ }));
82241
+ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
82242
+ darwin: "Safari"
82243
+ }));
82244
+ var open_default = open;
82245
+
82246
+ // src/utils/opener.ts
82247
+ async function openInBrowser(target) {
82248
+ if (process.env.DBCLI_NO_OPEN === "1" || false) {
82249
+ console.log(`[Opener] DBCLI_NO_OPEN is set. Skipping browser launch for: ${target}`);
82250
+ return;
82251
+ }
82252
+ try {
82253
+ await open_default(target);
82254
+ } catch (err) {
82255
+ console.error(`[Opener] Failed to open ${target}:`, err);
82256
+ }
82257
+ }
82258
+
82259
+ // src/commands/query.ts
82260
+ import { tmpdir } from "os";
82261
+ import { join as join13 } from "path";
82262
+
81561
82263
  // src/core/query-executor.ts
81562
82264
  init_permission_guard();
81563
82265
 
@@ -81763,7 +82465,7 @@ async function queryCommand(sql, options, command) {
81763
82465
  throw new Error("SQL query required");
81764
82466
  }
81765
82467
  if (options.format) {
81766
- validateFormat(options.format, ALLOWED_FORMATS3, "query");
82468
+ validateFormat(options.format, [...ALLOWED_FORMATS3, "html"], "query");
81767
82469
  }
81768
82470
  sql = sql.trim();
81769
82471
  const configPath = resolveConfigPath(command, options);
@@ -81802,6 +82504,26 @@ async function queryCommand(sql, options, command) {
81802
82504
  autoLimit,
81803
82505
  limitValue: options.limit
81804
82506
  });
82507
+ if (options.ui || options.format === "html") {
82508
+ const html = await generateHtmlReport({
82509
+ meta: {
82510
+ name: "Query Results",
82511
+ key: "raw-sql",
82512
+ params: [],
82513
+ tags: [],
82514
+ description: sql.length > 100 ? sql.slice(0, 97) + "..." : sql
82515
+ },
82516
+ rows: result.rows
82517
+ });
82518
+ if (options.ui) {
82519
+ const tempPath = join13(tmpdir(), `dbcli-query-${Date.now()}.html`);
82520
+ await Bun.write(tempPath, html);
82521
+ await openInBrowser(tempPath);
82522
+ } else {
82523
+ console.log(html);
82524
+ }
82525
+ return;
82526
+ }
81805
82527
  const formatter = new QueryResultFormatter;
81806
82528
  const output = formatter.format(result, {
81807
82529
  format: options.format || "table"
@@ -82406,6 +83128,8 @@ init_blacklist();
82406
83128
  init_permission_guard();
82407
83129
  init_saved_queries();
82408
83130
  init_strategies();
83131
+ import { tmpdir as tmpdir2 } from "os";
83132
+ import { join as join16 } from "path";
82409
83133
  function formatDryRun(input) {
82410
83134
  const lines = ["Dry-run preview (no execution):"];
82411
83135
  if (input.family === "es") {
@@ -82475,6 +83199,20 @@ async function qCommand(name, options, command) {
82475
83199
  const executionTimeMs = Math.round(performance.now() - start);
82476
83200
  const columnNames = result.rows[0] ? Object.keys(result.rows[0]) : [];
82477
83201
  const filtered = family === "redis" ? { filteredRows: result.rows, omittedColumns: [] } : blacklistValidator.filterColumns(targetName, result.rows, columnNames);
83202
+ if (options.ui || options.format === "html") {
83203
+ const html = await generateHtmlReport({
83204
+ meta: snippet.query.meta,
83205
+ rows: filtered.filteredRows
83206
+ });
83207
+ if (options.ui) {
83208
+ const tempPath = join16(tmpdir2(), `dbcli-report-${Date.now()}.html`);
83209
+ await Bun.write(tempPath, html);
83210
+ await openInBrowser(tempPath);
83211
+ } else {
83212
+ console.log(html);
83213
+ }
83214
+ return;
83215
+ }
82478
83216
  const formatter = new QueryResultFormatter;
82479
83217
  const out = formatter.format({
82480
83218
  rows: filtered.filteredRows,
@@ -82508,10 +83246,10 @@ function parseCliParams(list) {
82508
83246
  }
82509
83247
  return out;
82510
83248
  }
82511
- async function readParamFile(path4) {
82512
- if (!path4)
83249
+ async function readParamFile(path5) {
83250
+ if (!path5)
82513
83251
  return {};
82514
- const text2 = await Bun.file(path4).text();
83252
+ const text2 = await Bun.file(path5).text();
82515
83253
  const parsed = JSON.parse(text2);
82516
83254
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
82517
83255
  throw new Error(`--param-file must be a JSON object (got ${typeof parsed})`);
@@ -83089,10 +83827,10 @@ queriesCommand.command("copy <src> <dst>").description(t("queries.copy_descripti
83089
83827
  process.exit(1);
83090
83828
  }
83091
83829
  });
83092
- queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (path4, options) => {
83830
+ queriesCommand.command("import <path>").description(t("queries.import_description")).option("--force", "Overwrite existing file without prompting").option("--as <name>", "Override snippet name (defaults to filename)").action(async (path5, options) => {
83093
83831
  try {
83094
83832
  const { queriesImport: queriesImport2 } = await Promise.resolve().then(() => (init_queries_import(), exports_queries_import));
83095
- await queriesImport2(path4, options);
83833
+ await queriesImport2(path5, options);
83096
83834
  } catch (e) {
83097
83835
  console.error(e.message);
83098
83836
  process.exit(1);
@@ -84078,8 +84816,8 @@ async function exportCommand(sql, options, command) {
84078
84816
  if (!sql || sql.trim() === "") {
84079
84817
  throw new Error("Query required");
84080
84818
  }
84081
- if (!options.format || !["json", "jsonl", "csv"].includes(options.format)) {
84082
- throw new Error("--format must be json, jsonl, or csv");
84819
+ if (!options.format || !["json", "jsonl", "csv", "html"].includes(options.format)) {
84820
+ throw new Error("--format must be json, jsonl, csv, or html");
84083
84821
  }
84084
84822
  sql = sql.trim();
84085
84823
  const configPath = resolveConfigPath(command, options);
@@ -84106,10 +84844,24 @@ async function exportCommand(sql, options, command) {
84106
84844
  try {
84107
84845
  const executor3 = new QueryExecutor(adapter, config.permission);
84108
84846
  const result = await executor3.execute(sql, { autoLimit: true });
84109
- const formatter = new QueryResultFormatter;
84110
- const formatted = formatter.format(result, {
84111
- format: options.format
84112
- });
84847
+ let formatted;
84848
+ if (options.format === "html") {
84849
+ formatted = await generateHtmlReport({
84850
+ meta: {
84851
+ name: "Exported Report",
84852
+ key: "export",
84853
+ params: [],
84854
+ tags: [],
84855
+ description: sql
84856
+ },
84857
+ rows: result.rows
84858
+ });
84859
+ } else {
84860
+ const formatter = new QueryResultFormatter;
84861
+ formatted = formatter.format(result, {
84862
+ format: options.format
84863
+ });
84864
+ }
84113
84865
  if (options.output) {
84114
84866
  const file = Bun.file(options.output);
84115
84867
  const exists = await file.exists();
@@ -84319,7 +85071,7 @@ function escapeCsvField(value) {
84319
85071
  init_message_loader();
84320
85072
  init_package_root();
84321
85073
  var {$ } = globalThis.Bun;
84322
- import * as path4 from "path";
85074
+ import * as path5 from "path";
84323
85075
  import { homedir as homedir2 } from "os";
84324
85076
  var SKILL_SOURCE_PATH = packageAssetPath("SKILL.md");
84325
85077
  var REFERENCE_SOURCE_PATH = packageAssetPath("reference.md");
@@ -84361,14 +85113,14 @@ async function checkSkillUpdates() {
84361
85113
  if (!await sourceFile.exists())
84362
85114
  return [];
84363
85115
  const sourceContent = await sourceFile.text();
84364
- for (const platform of SUPPORTED_PLATFORMS) {
85116
+ for (const platform2 of SUPPORTED_PLATFORMS) {
84365
85117
  try {
84366
- const installPath = getInstallPath(platform);
85118
+ const installPath = getInstallPath(platform2);
84367
85119
  const installedFile = Bun.file(installPath);
84368
85120
  if (await installedFile.exists()) {
84369
85121
  const installedContent = await installedFile.text();
84370
85122
  if (installedContent !== sourceContent) {
84371
- outdated.push(platform);
85123
+ outdated.push(platform2);
84372
85124
  }
84373
85125
  }
84374
85126
  } catch {}
@@ -84376,33 +85128,33 @@ async function checkSkillUpdates() {
84376
85128
  } catch {}
84377
85129
  return outdated;
84378
85130
  }
84379
- function getInstallPath(platform) {
85131
+ function getInstallPath(platform2) {
84380
85132
  const home = process.env.HOME || homedir2();
84381
- const platformLower = platform.toLowerCase();
85133
+ const platformLower = platform2.toLowerCase();
84382
85134
  switch (platformLower) {
84383
85135
  case "claude":
84384
- return path4.join(home, ".claude", "skills", "dbcli", "SKILL.md");
85136
+ return path5.join(home, ".claude", "skills", "dbcli", "SKILL.md");
84385
85137
  case "gemini":
84386
- return path4.join(home, ".gemini", "skills", "dbcli", "SKILL.md");
85138
+ return path5.join(home, ".gemini", "skills", "dbcli", "SKILL.md");
84387
85139
  case "copilot":
84388
- return path4.join(process.cwd(), ".github", "skills", "dbcli", "SKILL.md");
85140
+ return path5.join(process.cwd(), ".github", "skills", "dbcli", "SKILL.md");
84389
85141
  case "cursor":
84390
- return path4.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
85142
+ return path5.join(process.cwd(), ".cursor", "rules", "dbcli.mdc");
84391
85143
  default:
84392
- throw new Error(`Unknown platform: ${platform}. Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`);
85144
+ throw new Error(`Unknown platform: ${platform2}. Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`);
84393
85145
  }
84394
85146
  }
84395
- async function writeSkillInstall(platform, installPath, skillMarkdown, referenceMarkdown) {
84396
- const platformLower = platform.toLowerCase();
84397
- await ensureDir(path4.dirname(installPath));
85147
+ async function writeSkillInstall(platform2, installPath, skillMarkdown, referenceMarkdown) {
85148
+ const platformLower = platform2.toLowerCase();
85149
+ await ensureDir(path5.dirname(installPath));
84398
85150
  await Bun.file(installPath).write(skillMarkdown);
84399
85151
  if (platformLower === "cursor") {
84400
- const refPath2 = path4.join(process.cwd(), ".cursor", "skills", "dbcli", "reference.md");
84401
- await ensureDir(path4.dirname(refPath2));
85152
+ const refPath2 = path5.join(process.cwd(), ".cursor", "skills", "dbcli", "reference.md");
85153
+ await ensureDir(path5.dirname(refPath2));
84402
85154
  await Bun.file(refPath2).write(referenceMarkdown);
84403
85155
  return { referencePath: refPath2 };
84404
85156
  }
84405
- const refPath = path4.join(path4.dirname(installPath), "reference.md");
85157
+ const refPath = path5.join(path5.dirname(installPath), "reference.md");
84406
85158
  await Bun.file(refPath).write(referenceMarkdown);
84407
85159
  return { referencePath: refPath };
84408
85160
  }
@@ -84443,15 +85195,15 @@ class AgentTaskError extends Error {
84443
85195
  }
84444
85196
  // src/core/agent-tasks/task-paths.ts
84445
85197
  init_package_root();
84446
- import { join as join17 } from "path";
85198
+ import { join as join19 } from "path";
84447
85199
  function resolveBuiltinDir2() {
84448
85200
  return packageAssetPath("tasks");
84449
85201
  }
84450
85202
  function resolveAgentTaskDirs(workspaceRoot) {
84451
85203
  return {
84452
85204
  builtinDir: resolveBuiltinDir2(),
84453
- sharedDir: join17(workspaceRoot, ".dbcli-shared", "tasks"),
84454
- localDir: join17(workspaceRoot, ".dbcli", "tasks")
85205
+ sharedDir: join19(workspaceRoot, ".dbcli-shared", "tasks"),
85206
+ localDir: join19(workspaceRoot, ".dbcli", "tasks")
84455
85207
  };
84456
85208
  }
84457
85209
  // src/core/agent-tasks/parser.ts
@@ -84589,7 +85341,7 @@ function parseSteps(value, input) {
84589
85341
  }
84590
85342
  // src/core/agent-tasks/loader.ts
84591
85343
  import { readdir as readdir2 } from "fs/promises";
84592
- import { join as join18, relative as relative2, sep as sep2 } from "path";
85344
+ import { join as join20, relative as relative2, sep as sep2 } from "path";
84593
85345
  async function loadAgentTasks(opts, flags) {
84594
85346
  const errors3 = [];
84595
85347
  const builtin = await walkAndParse2(opts.builtinDir, "builtin", errors3);
@@ -84646,7 +85398,7 @@ async function safeCollectMd(root) {
84646
85398
  return;
84647
85399
  }
84648
85400
  for (const e of entries) {
84649
- const full = join18(dir, e.name);
85401
+ const full = join20(dir, e.name);
84650
85402
  if (e.isDirectory())
84651
85403
  await walk(full);
84652
85404
  else
@@ -86504,7 +87256,7 @@ var recoveryCommand = new Command().name("recovery").description(t("recovery.des
86504
87256
  // src/commands/recover.ts
86505
87257
  init_recovery();
86506
87258
  init_last_envelope();
86507
- import { stat as stat3 } from "fs/promises";
87259
+ import { stat as stat4 } from "fs/promises";
86508
87260
  import { resolve as resolve4 } from "path";
86509
87261
 
86510
87262
  // src/core/recovery/envelope-schema.ts
@@ -86563,8 +87315,8 @@ var savedRecoveryEnvelopeSchema = exports_external.object({
86563
87315
  }).strict();
86564
87316
  function summarizeZodError2(err) {
86565
87317
  return err.issues.map((iss) => {
86566
- const path5 = iss.path.join(".") || "<root>";
86567
- return `${path5}: ${iss.message}`;
87318
+ const path6 = iss.path.join(".") || "<root>";
87319
+ return `${path6}: ${iss.message}`;
86568
87320
  }).join("; ");
86569
87321
  }
86570
87322
  function parseRecoveryEnvelope(input) {
@@ -86600,10 +87352,10 @@ class RecoverCliError extends Error {
86600
87352
  }
86601
87353
  async function resolveApplySource(opts) {
86602
87354
  if (opts.from !== undefined) {
86603
- const path5 = resolve4(opts.cwd, opts.from);
87355
+ const path6 = resolve4(opts.cwd, opts.from);
86604
87356
  let raw;
86605
87357
  try {
86606
- raw = await Bun.file(path5).text();
87358
+ raw = await Bun.file(path6).text();
86607
87359
  } catch {
86608
87360
  throw new RecoverCliError(`--from ${opts.from}: file not readable.`, EXIT_CODE.malformed);
86609
87361
  }
@@ -86620,13 +87372,13 @@ async function resolveApplySource(opts) {
86620
87372
  }
86621
87373
  const saved2 = r3.value;
86622
87374
  try {
86623
- await stat3(saved2.cwd);
87375
+ await stat4(saved2.cwd);
86624
87376
  } catch {
86625
87377
  throw new RecoverCliError(`--from ${opts.from}: saved cwd '${saved2.cwd}' no longer exists.`, EXIT_CODE.malformed);
86626
87378
  }
86627
87379
  return {
86628
87380
  kind: "from",
86629
- path: path5,
87381
+ path: path6,
86630
87382
  cwd: saved2.cwd,
86631
87383
  envelope: saved2.envelope,
86632
87384
  command: saved2.command
@@ -86638,7 +87390,7 @@ async function resolveApplySource(opts) {
86638
87390
  }
86639
87391
  return {
86640
87392
  kind: "from",
86641
- path: path5,
87393
+ path: path6,
86642
87394
  cwd: process.cwd(),
86643
87395
  envelope: r2.value,
86644
87396
  command: `external --from ${opts.from}`
@@ -86654,7 +87406,7 @@ async function resolveApplySource(opts) {
86654
87406
  }
86655
87407
  const saved = r.value;
86656
87408
  try {
86657
- await stat3(saved.cwd);
87409
+ await stat4(saved.cwd);
86658
87410
  } catch {
86659
87411
  throw new RecoverCliError(`Auto-saved ${LAST_ENVELOPE_PATH} references cwd '${saved.cwd}' that no longer exists.`, EXIT_CODE.malformed);
86660
87412
  }
@@ -86784,7 +87536,7 @@ init_config_v2();
86784
87536
  init_config_binding();
86785
87537
  init_schema_path();
86786
87538
  init_config();
86787
- import { join as join21 } from "path";
87539
+ import { join as join23 } from "path";
86788
87540
  import { resolveSrv as resolveSrv2 } from "dns/promises";
86789
87541
  var ALLOWED_FORMATS13 = ["text", "json"];
86790
87542
  var SENSITIVE_PATTERNS = [
@@ -86858,7 +87610,7 @@ var runDoctorChecks = {
86858
87610
  }
86859
87611
  },
86860
87612
  async checkConfigExists(configPath, existsFn) {
86861
- const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join21(configPath, "config.json")).exists();
87613
+ const exists = existsFn ? await existsFn(configPath) : await Bun.file(configPath).exists() || await Bun.file(join23(configPath, "config.json")).exists();
86862
87614
  return {
86863
87615
  group: "Configuration",
86864
87616
  label: "Config exists",
@@ -87015,7 +87767,7 @@ var runDoctorChecks = {
87015
87767
  async checkV2Config(configPath) {
87016
87768
  const results = [];
87017
87769
  const storagePath = await resolveConfigStoragePath(configPath);
87018
- const configFile = Bun.file(join21(storagePath, "config.json"));
87770
+ const configFile = Bun.file(join23(storagePath, "config.json"));
87019
87771
  if (!await configFile.exists())
87020
87772
  return results;
87021
87773
  let raw;
@@ -87055,7 +87807,7 @@ var runDoctorChecks = {
87055
87807
  }
87056
87808
  for (const [name, conn] of Object.entries(config.connections)) {
87057
87809
  if (conn.envFile) {
87058
- const envPath = join21(storagePath, conn.envFile);
87810
+ const envPath = join23(storagePath, conn.envFile);
87059
87811
  const exists = await Bun.file(envPath).exists();
87060
87812
  results.push({
87061
87813
  group: "Configuration",
@@ -87274,7 +88026,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
87274
88026
  }
87275
88027
  try {
87276
88028
  const schemaConnName = await getSchemaIsolationConnectionName(configPath);
87277
- const indexPath = join21(resolveSchemaPath(storagePath, schemaConnName), "index.json");
88029
+ const indexPath = join23(resolveSchemaPath(storagePath, schemaConnName), "index.json");
87278
88030
  const indexFile = Bun.file(indexPath);
87279
88031
  let indexParsed = null;
87280
88032
  if (await indexFile.exists()) {
@@ -87317,7 +88069,7 @@ var doctorCommand = new Command("doctor").description("Run diagnostic checks on
87317
88069
 
87318
88070
  // src/commands/completion.ts
87319
88071
  init_colors();
87320
- import { join as join22 } from "path";
88072
+ import { join as join24 } from "path";
87321
88073
  import { homedir as homedir3 } from "os";
87322
88074
  function extractCommands(program2) {
87323
88075
  return program2.commands.map((cmd) => ({
@@ -87419,11 +88171,11 @@ function getInstallPath2(shell) {
87419
88171
  const home = homedir3();
87420
88172
  switch (shell) {
87421
88173
  case "bash":
87422
- return join22(home, ".bashrc");
88174
+ return join24(home, ".bashrc");
87423
88175
  case "zsh":
87424
- return join22(home, ".zshrc");
88176
+ return join24(home, ".zshrc");
87425
88177
  case "fish":
87426
- return join22(home, ".config", "fish", "completions", "dbcli.fish");
88178
+ return join24(home, ".config", "fish", "completions", "dbcli.fish");
87427
88179
  default:
87428
88180
  throw new Error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
87429
88181
  }
@@ -87443,7 +88195,7 @@ var MARKER_END = "# <<< dbcli completion <<<";
87443
88195
  async function installCompletion(shell, script) {
87444
88196
  const targetPath = getInstallPath2(shell);
87445
88197
  if (shell === "fish") {
87446
- const dir = join22(homedir3(), ".config", "fish", "completions");
88198
+ const dir = join24(homedir3(), ".config", "fish", "completions");
87447
88199
  await Bun.$`mkdir -p ${dir}`.quiet();
87448
88200
  await Bun.file(targetPath).write(script);
87449
88201
  console.log(colors.success(`\u2713 Fish completion installed to ${targetPath}`));
@@ -87676,7 +88428,7 @@ ${t("upgrade.failed")}`));
87676
88428
  init_config();
87677
88429
  init_adapters();
87678
88430
  import { createInterface as createInterface2 } from "readline";
87679
- import { join as join23 } from "path";
88431
+ import { join as join25 } from "path";
87680
88432
  import { homedir as homedir4 } from "os";
87681
88433
 
87682
88434
  // src/core/repl/types.ts
@@ -88401,7 +89153,7 @@ class MongoShellAdapter {
88401
89153
  }
88402
89154
 
88403
89155
  // src/commands/shell.ts
88404
- var HISTORY_PATH = join23(homedir4(), ".dbcli_history");
89156
+ var HISTORY_PATH = join25(homedir4(), ".dbcli_history");
88405
89157
  var MONGO_COMPLETION_EAGER_THRESHOLD = 20;
88406
89158
  async function populateMongoColumns(mongoAdapter, collectionNames, threshold = MONGO_COMPLETION_EAGER_THRESHOLD) {
88407
89159
  const columnsByTable = {};
@@ -89382,7 +90134,7 @@ init_config();
89382
90134
  init_errors();
89383
90135
  init_message_loader();
89384
90136
  init_config_binding();
89385
- import { join as join24 } from "path";
90137
+ import { join as join26 } from "path";
89386
90138
  async function switchDefault(configPath, name, config) {
89387
90139
  if (!config.connections[name]) {
89388
90140
  const available = Object.keys(config.connections).join(", ");
@@ -89405,7 +90157,7 @@ function listConnectionsForDisplay(config) {
89405
90157
  }
89406
90158
  async function ensureV2Config(configPath) {
89407
90159
  const storagePath = await resolveConfigStoragePath(configPath);
89408
- const configFile = Bun.file(join24(storagePath, "config.json"));
90160
+ const configFile = Bun.file(join26(storagePath, "config.json"));
89409
90161
  const legacyFile = Bun.file(configPath);
89410
90162
  if (!await configFile.exists() && !await legacyFile.exists()) {
89411
90163
  throw new ConfigError(t("init.config_not_found"));
@@ -89468,7 +90220,7 @@ var useCommand = new Command("use").description("Switch or display the default d
89468
90220
 
89469
90221
  // src/cli.ts
89470
90222
  init_config();
89471
- import { join as join25 } from "path";
90223
+ import { join as join27 } from "path";
89472
90224
  var _bgVersionCheckResult;
89473
90225
  function shouldSkipBackgroundChecks() {
89474
90226
  return process.env.DBCLI_NO_UPDATE_CHECK === "1" || process.env.DBCLI_NO_UPDATE_CHECK === "true" || false;
@@ -89497,7 +90249,7 @@ program2.hook("preAction", (thisCommand, actionCommand) => {
89497
90249
  try {
89498
90250
  let cache = null;
89499
90251
  try {
89500
- const cacheFile = Bun.file(join25(configPath, "version-check.json"));
90252
+ const cacheFile = Bun.file(join27(configPath, "version-check.json"));
89501
90253
  if (await cacheFile.exists()) {
89502
90254
  cache = await cacheFile.json();
89503
90255
  }
@@ -89527,7 +90279,7 @@ program2.hook("postAction", async (thisCommand, actionCommand) => {
89527
90279
  program2.addCommand(initCommand);
89528
90280
  program2.addCommand(listCommand);
89529
90281
  program2.addCommand(schemaCommand);
89530
- program2.command("query <sql>").description(t("query.description")).option("--format <type>", "Output format: table, json, csv", "table").option("--limit <number>", "Limit result rows (overrides auto-limit)", (val) => parseInt(val, 10)).option("--no-limit", "Disable auto-limit in query-only mode").option("--collection <name>", "MongoDB collection name; Elasticsearch index name").option("--index <name>", "Elasticsearch index name (alias for --collection)").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (sql, options, command) => {
90282
+ program2.command("query <sql>").description(t("query.description")).option("--format <type>", "Output format: table, json, csv, html", "table").option("--ui", "Show interactive dashboard in browser", false).option("--limit <number>", "Limit result rows (overrides auto-limit)", (val) => parseInt(val, 10)).option("--no-limit", "Disable auto-limit in query-only mode").option("--collection <name>", "MongoDB collection name; Elasticsearch index name").option("--index <name>", "Elasticsearch index name (alias for --collection)").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (sql, options, command) => {
89531
90283
  try {
89532
90284
  await queryCommand(sql, options, command);
89533
90285
  } catch (error) {
@@ -89542,7 +90294,7 @@ program2.command("query <sql>").description(t("query.description")).option("--fo
89542
90294
  program2.command("plan <sql>").description("Analyze SQL risk without executing").option("--format <type>", "Output format: text, json", "text").action(async (sql, options, command) => {
89543
90295
  await planCommand(sql, options, command);
89544
90296
  });
89545
- program2.command("q <name>").description(t("q.description")).option("--format <type>", "Output format: table, json, csv", "table").option("--no-limit", "Disable size guard wrap (LIMIT 1000)").option("--dry-run", "Show final SQL + bind values; do not execute").option("--param <kv>", "Pass parameter as key=value (repeatable)", (val, prev = []) => prev.concat([val]), []).option("--param-file <path>", "JSON file containing param values").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (name, options, command) => {
90297
+ program2.command("q <name>").description(t("q.description")).option("--format <type>", "Output format: table, json, csv, html", "table").option("--ui", "Show interactive dashboard in browser", false).option("--no-limit", "Disable size guard wrap (LIMIT 1000)").option("--dry-run", "Show final SQL + bind values; do not execute").option("--param <kv>", "Pass parameter as key=value (repeatable)", (val, prev = []) => prev.concat([val]), []).option("--param-file <path>", "JSON file containing param values").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (name, options, command) => {
89546
90298
  await qCommand(name, options, command);
89547
90299
  });
89548
90300
  program2.command("insert <table>").description(t("insert.description")).option("--data <json>", "JSON object to insert").option("--dry-run", "Show generated SQL without executing").option("--force", "Skip confirmation prompt").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (table, options, command) => {
@@ -89581,10 +90333,10 @@ program2.command("delete <table>").description(t("delete.description")).option("
89581
90333
  process.exit(1);
89582
90334
  }
89583
90335
  });
89584
- program2.command("export <sql>").description(t("export.description")).option("--format <format>", "Output format: json, jsonl, or csv", "json").option("--output <path>", "Output file path (if omitted, write to stdout)", undefined).option("--force", "Skip overwrite confirmation", false).option("--collection <name>", "MongoDB collection name; Elasticsearch index name").option("--index <name>", "Elasticsearch index name (alias for --collection)").option("--limit <number>", "Limit result rows (overrides auto-limit)", (val) => parseInt(val, 10)).option("--no-limit", "Disable auto-limit in query-only mode").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (sql, options, command) => {
90336
+ program2.command("export <sql>").description(t("export.description")).option("--format <format>", "Output format: json, jsonl, csv, html", "json").option("--output <path>", "Output file path (if omitted, write to stdout)", undefined).option("--force", "Skip overwrite confirmation", false).option("--collection <name>", "MongoDB collection name; Elasticsearch index name").option("--index <name>", "Elasticsearch index name (alias for --collection)").option("--limit <number>", "Limit result rows (overrides auto-limit)", (val) => parseInt(val, 10)).option("--no-limit", "Disable auto-limit in query-only mode").option("--recovery", "On failure, emit a structured recovery envelope to stdout (suppresses human stderr message)", false).action(async (sql, options, command) => {
89585
90337
  try {
89586
90338
  const { validateFormat: validateFormat2 } = await Promise.resolve().then(() => (init_validation(), exports_validation));
89587
- validateFormat2(options.format, ["json", "jsonl", "csv"], "export");
90339
+ validateFormat2(options.format, ["json", "jsonl", "csv", "html"], "export");
89588
90340
  return await exportCommand(sql, options, command);
89589
90341
  } catch (error) {
89590
90342
  if (options.recovery === true) {