@vibgrate/cli 2026.717.1 → 2026.718.2

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.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, detectAssistants, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, formatForExt, verifyDeterminism, renderHtml, isModelReady, SKIP_DIRS, SKIP_FILES } from './chunk-L42NVMD6.js';
2
+ import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, detectAssistants, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, formatForExt, hostedBase, verifyDeterminism, renderHtml, isModelReady, readStoredCredentials, projectCredentialsPath, homeCredentialsPath, SKIP_DIRS, SKIP_FILES } from './chunk-H5M7CXE6.js';
3
3
  import { resolvedGrammarFiles, grammarSetVersion, hashString, canonicalize, hashBytes } from './chunk-VFO5UDAT.js';
4
- import { loadAdvancedScanHook, baselineCommand } from './chunk-WZNIDXZP.js';
5
- import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-M62BGJMK.js';
4
+ import { loadAdvancedScanHook, baselineCommand } from './chunk-BFXDF6ZE.js';
5
+ import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-RME7BFMU.js';
6
6
  import './chunk-RXP66R2E.js';
7
7
  import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-GI6W53LM.js';
8
8
  import * as fs8 from 'fs';
@@ -16,7 +16,7 @@ import * as crypto from 'crypto';
16
16
  import { randomUUID } from 'crypto';
17
17
  import chalk8 from 'chalk';
18
18
  import * as os from 'os';
19
- import * as fs14 from 'fs/promises';
19
+ import * as fs15 from 'fs/promises';
20
20
  import * as readline from 'readline';
21
21
  import semver from 'semver';
22
22
 
@@ -1287,7 +1287,31 @@ var OFFSET_FILE = "stats-share.json";
1287
1287
  var INSTALL_ID_DIR = path.join(os.homedir(), ".vibgrate");
1288
1288
  var INSTALL_ID_FILE = path.join(INSTALL_ID_DIR, "install-id");
1289
1289
  var SEND_TIMEOUT_MS = 4e3;
1290
- function installId() {
1290
+ function telemetryOptOut(env = process.env) {
1291
+ const dnt = (env.DO_NOT_TRACK ?? "").trim().toLowerCase();
1292
+ if (dnt !== "" && dnt !== "0" && dnt !== "false") return "DO_NOT_TRACK";
1293
+ const vt = (env.VIBGRATE_TELEMETRY ?? "").trim().toLowerCase();
1294
+ if (vt === "0" || vt === "false" || vt === "off") return "VIBGRATE_TELEMETRY";
1295
+ return null;
1296
+ }
1297
+ var CI_ENV_VARS = [
1298
+ "CI",
1299
+ "GITHUB_ACTIONS",
1300
+ "GITLAB_CI",
1301
+ "CIRCLECI",
1302
+ "TRAVIS",
1303
+ "BUILDKITE",
1304
+ "JENKINS_URL",
1305
+ "TF_BUILD"
1306
+ ];
1307
+ function isCI(env = process.env) {
1308
+ return CI_ENV_VARS.some((v) => {
1309
+ const val = (env[v] ?? "").trim().toLowerCase();
1310
+ return val !== "" && val !== "0" && val !== "false";
1311
+ });
1312
+ }
1313
+ function installId(env = process.env) {
1314
+ if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();
1291
1315
  try {
1292
1316
  const existing = fs8.readFileSync(INSTALL_ID_FILE, "utf8").trim();
1293
1317
  if (isUuid(existing)) return existing;
@@ -1463,6 +1487,27 @@ var StatsSharer = class {
1463
1487
  }
1464
1488
  };
1465
1489
 
1490
+ // src/util/origin.ts
1491
+ function originAllowed(origin, allowlist) {
1492
+ if (origin === void 0 || origin === "") return true;
1493
+ const allowed = (allowlist ?? "").split(",").map((s) => s.trim()).filter(Boolean);
1494
+ if (allowed.includes("*")) return true;
1495
+ if (allowed.includes(origin)) return true;
1496
+ return isLoopbackOrigin(origin);
1497
+ }
1498
+ function isLoopbackOrigin(origin) {
1499
+ let url;
1500
+ try {
1501
+ url = new URL(origin);
1502
+ } catch {
1503
+ return false;
1504
+ }
1505
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
1506
+ const host = url.hostname;
1507
+ if (host === "localhost" || host === "[::1]" || host === "::1") return true;
1508
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
1509
+ }
1510
+
1466
1511
  // src/commands/serve.ts
1467
1512
  var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
1468
1513
  function registerServe(program) {
@@ -1472,9 +1517,14 @@ function registerServe(program) {
1472
1517
  const graphPath = global.graph ?? defaultGraphPath(root);
1473
1518
  const refresh = opts.refresh !== false && !global.graph;
1474
1519
  const local = global.local === true;
1475
- const shareStats = opts.shareStats === true && !local;
1520
+ const optOut = telemetryOptOut();
1521
+ const shareStats = opts.shareStats === true && !local && optOut === null;
1476
1522
  const serveOpts = {
1477
- savings: opts.savings === true || shareStats,
1523
+ // A *requested* --share-stats always implies local recording, even when
1524
+ // the upload itself is suppressed (--local / env opt-out) — the local
1525
+ // ledger never leaves the machine, and the disclosure messages below
1526
+ // promise "recording locally".
1527
+ savings: opts.savings === true || opts.shareStats === true,
1478
1528
  shareStats,
1479
1529
  local,
1480
1530
  dedup: opts.dedup === true,
@@ -1483,6 +1533,8 @@ function registerServe(program) {
1483
1533
  await ensureServableGraph(root, graphPath, global, refresh);
1484
1534
  if (opts.shareStats === true && local) {
1485
1535
  info(c.dim("vg \xB7 --share-stats ignored under --local (air-gapped): recording locally, not uploading."));
1536
+ } else if (opts.shareStats === true && optOut !== null) {
1537
+ info(c.dim(`vg \xB7 --share-stats disabled by ${optOut}: recording locally, not uploading.`));
1486
1538
  }
1487
1539
  if (shareStats) startSharing(root);
1488
1540
  const freshness = refresh ? "auto-refresh" : "as built";
@@ -1555,6 +1607,13 @@ async function serveHttp(graphPath, host, port, opts, freshness) {
1555
1607
  res.writeHead(404).end("not found");
1556
1608
  return;
1557
1609
  }
1610
+ const origin = req.headers.origin;
1611
+ if (!originAllowed(origin, process.env.VIBGRATE_ALLOWED_ORIGINS)) {
1612
+ res.writeHead(403, { "Content-Type": "text/plain" }).end(
1613
+ "forbidden origin \u2014 vg serve only accepts browser requests from loopback origins. Set VIBGRATE_ALLOWED_ORIGINS to allow others."
1614
+ );
1615
+ return;
1616
+ }
1558
1617
  try {
1559
1618
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
1560
1619
  const server = createServer(source, opts);
@@ -3803,6 +3862,125 @@ function registerBundle(program) {
3803
3862
  });
3804
3863
  applyGlobalOptions(cmd);
3805
3864
  }
3865
+ function registerDoctor(program) {
3866
+ const cmd = program.command("doctor").description("diagnose your setup: config, credentials, map freshness, hosted reachability, MCP launch").action(async function() {
3867
+ await runDoctor(readGlobal(this));
3868
+ });
3869
+ applyGlobalOptions(cmd);
3870
+ }
3871
+ var REACH_TIMEOUT_MS = 3e3;
3872
+ var CONFIG_BASENAMES = ["vibgrate.config.ts", "vibgrate.config.js", "vibgrate.config.json"];
3873
+ async function runDoctor(global) {
3874
+ const root = rootOf(global);
3875
+ const local = global.local === true;
3876
+ const graphPath = global.graph ?? defaultGraphPath(root);
3877
+ const configFile = CONFIG_BASENAMES.find((f) => fs8.existsSync(path.join(root, f))) ?? null;
3878
+ const graph = loadGraph(root, graphPath);
3879
+ let staleFiles = null;
3880
+ if (graph) {
3881
+ const probe = probeFreshness(root);
3882
+ if (probe) staleFiles = driftCount(probe.drift);
3883
+ }
3884
+ const catalog = loadCatalog(root);
3885
+ const libCount = Object.keys(catalog.libraries).length;
3886
+ const creds = diagnoseCredentials(root);
3887
+ const base = hostedBase();
3888
+ const reachable = local ? null : await probeReachable(base);
3889
+ const launch = detectServeLaunch();
3890
+ const optOut = telemetryOptOut();
3891
+ const d = {
3892
+ version: VERSION,
3893
+ node: process.version,
3894
+ platform: `${process.platform}/${process.arch}`,
3895
+ root,
3896
+ configFile,
3897
+ map: {
3898
+ path: path.relative(root, graphPath),
3899
+ built: graph !== null,
3900
+ generatedAt: graph?.generatedAt ?? null,
3901
+ staleFiles
3902
+ },
3903
+ libCatalog: {
3904
+ path: path.relative(root, catalogPath(root)),
3905
+ present: fs8.existsSync(catalogPath(root)),
3906
+ libraries: libCount
3907
+ },
3908
+ credentials: creds,
3909
+ hosted: { base, checked: !local, reachable },
3910
+ mcpLaunch: { command: launch.command, args: launch.args, note: launch.note ?? null },
3911
+ telemetry: { optOut, ci: isCI(), endpoint: statsEndpoint() }
3912
+ };
3913
+ if (global.json) {
3914
+ json(d);
3915
+ return;
3916
+ }
3917
+ info(`${c.cyan("vg")} doctor \xB7 v${d.version} \xB7 node ${d.node} \xB7 ${d.platform}`);
3918
+ info(` root ${d.root}`);
3919
+ info(` config ${d.configFile ? c.green(d.configFile) : c.dim("none (defaults) \u2014 `vg init` writes one")}`);
3920
+ if (!d.map.built) {
3921
+ info(` map ${c.yellow("none")} \u2014 run ${c.bold("vg")} to build ${c.dim(d.map.path)}`);
3922
+ } else {
3923
+ const fresh = d.map.staleFiles == null ? c.dim("freshness unknown (no snapshot)") : d.map.staleFiles === 0 ? c.green("up to date") : c.yellow(`${d.map.staleFiles} file(s) stale \u2014 auto-refreshes on next query`);
3924
+ info(` map ${c.green("built")} ${c.dim(d.map.generatedAt ?? "")} \xB7 ${fresh}`);
3925
+ }
3926
+ info(
3927
+ ` lib ${d.libCatalog.present ? `${c.green(String(d.libCatalog.libraries))} librar${d.libCatalog.libraries === 1 ? "y" : "ies"} in ${d.libCatalog.path}` : c.dim("no catalog \u2014 `vg lib add <source>` starts one; `vg lib <name>` works without it")}`
3928
+ );
3929
+ if (d.credentials.source === "none") {
3930
+ info(` auth ${c.dim("anonymous \u2014 fine for everything local; `vg login` enables push/publish")}`);
3931
+ } else {
3932
+ info(
3933
+ ` auth ${c.green(d.credentials.source)} ${c.dim(d.credentials.path ?? "")} \xB7 workspace ${d.credentials.workspaceId ?? "?"} \xB7 ${d.credentials.host ?? "?"}`
3934
+ );
3935
+ }
3936
+ if (!d.hosted.checked) {
3937
+ info(` hosted ${c.dim(`skipped under --local (${d.hosted.base})`)}`);
3938
+ } else {
3939
+ info(
3940
+ ` hosted ${d.hosted.base} \xB7 ${d.hosted.reachable ? c.green("reachable") : c.yellow("unreachable \u2014 local answers still work")}`
3941
+ );
3942
+ }
3943
+ info(
3944
+ ` mcp ${c.bold(`${d.mcpLaunch.command} ${d.mcpLaunch.args.join(" ")}`)}${d.mcpLaunch.note ? c.dim(` (${d.mcpLaunch.note})`) : ""}`
3945
+ );
3946
+ const tel = d.telemetry.optOut ? c.green(`opted out via ${d.telemetry.optOut}`) : "off by default \u2014 only `vg serve --share-stats` ever uploads";
3947
+ info(` telemetry ${tel}${d.telemetry.ci ? c.dim(" \xB7 CI detected") : ""}`);
3948
+ }
3949
+ function diagnoseCredentials(root) {
3950
+ const none = { source: "none", path: null, host: null, workspaceId: null };
3951
+ if (process.env.VIBGRATE_DSN) {
3952
+ const parsed2 = parseDsn$1(process.env.VIBGRATE_DSN);
3953
+ return { source: "env", path: null, host: parsed2?.host ?? null, workspaceId: parsed2?.workspaceId ?? null };
3954
+ }
3955
+ const stored = readStoredCredentials({ cwd: root });
3956
+ if (!stored) return none;
3957
+ const file = credentialsPath({ cwd: root });
3958
+ const source = process.env.VIBGRATE_CREDENTIALS ? "env" : file === projectCredentialsPath(root) ? "project" : "home";
3959
+ const parsed = parseDsn$1(stored.dsn);
3960
+ return {
3961
+ source,
3962
+ path: shortenHome(file),
3963
+ host: stored.ingestHost ?? parsed?.host ?? null,
3964
+ workspaceId: stored.workspaceId ?? parsed?.workspaceId ?? null
3965
+ };
3966
+ }
3967
+ function shortenHome(p) {
3968
+ const home = homeCredentialsPath().slice(0, -"/.vibgrate/credentials.json".length);
3969
+ return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
3970
+ }
3971
+ async function probeReachable(base) {
3972
+ const controller = new AbortController();
3973
+ const timer = setTimeout(() => controller.abort(), REACH_TIMEOUT_MS);
3974
+ timer.unref?.();
3975
+ try {
3976
+ await fetch(base, { method: "HEAD", signal: controller.signal });
3977
+ return true;
3978
+ } catch {
3979
+ return false;
3980
+ } finally {
3981
+ clearTimeout(timer);
3982
+ }
3983
+ }
3806
3984
  var initCommand = new Command("init").description("Initialize vibgrate in a project").argument("[path]", "Path to initialize", ".").option("--baseline", "Create initial baseline after init").option("--yes", "Skip confirmation prompts").action(async (targetPath, opts) => {
3807
3985
  const rootDir = path.resolve(targetPath);
3808
3986
  const vibgrateDir2 = path.join(rootDir, ".vibgrate");
@@ -3816,7 +3994,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
3816
3994
  console.log(chalk8.green("\u2714") + ` Created ${chalk8.bold("vibgrate.config.ts")}`);
3817
3995
  }
3818
3996
  if (opts.baseline) {
3819
- const { runBaseline } = await import('./baseline-YWGSHCKW.js');
3997
+ const { runBaseline } = await import('./baseline-DKPQWOQ6.js');
3820
3998
  await runBaseline(rootDir);
3821
3999
  }
3822
4000
  console.log("");
@@ -3926,9 +4104,9 @@ async function fileReferencesIdentifier(rootDir, relFile, identifier, readFile3)
3926
4104
  }
3927
4105
  async function defaultReadFile(absPath) {
3928
4106
  try {
3929
- const stat2 = await fs14.stat(absPath);
4107
+ const stat2 = await fs15.stat(absPath);
3930
4108
  if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES) return null;
3931
- return await fs14.readFile(absPath, "utf8");
4109
+ return await fs15.readFile(absPath, "utf8");
3932
4110
  } catch {
3933
4111
  return null;
3934
4112
  }
@@ -5121,8 +5299,8 @@ async function fetchLatestVersion() {
5121
5299
  }
5122
5300
  async function writeCache(data) {
5123
5301
  try {
5124
- await fs14.mkdir(CACHE_DIR, { recursive: true });
5125
- await fs14.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5302
+ await fs15.mkdir(CACHE_DIR, { recursive: true });
5303
+ await fs15.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
5126
5304
  } catch {
5127
5305
  }
5128
5306
  }
@@ -6949,6 +7127,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
6949
7127
  "oddities",
6950
7128
  "serve",
6951
7129
  "lsp",
7130
+ "doctor",
6952
7131
  "install",
6953
7132
  "uninstall",
6954
7133
  "share",
@@ -7004,6 +7183,7 @@ ${aliasNote}Docs: https://vibgrate.com/help`;
7004
7183
  registerLib(program);
7005
7184
  registerExport(program);
7006
7185
  registerBundle(program);
7186
+ registerDoctor(program);
7007
7187
  program.addCommand(initCommand);
7008
7188
  program.addCommand(scanCommand);
7009
7189
  program.addCommand(fixCommand);