@dosu/cli 0.13.1 → 0.13.3

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.
Files changed (2) hide show
  1. package/bin/dosu.js +75 -30
  2. package/package.json +3 -2
package/bin/dosu.js CHANGED
@@ -4341,7 +4341,7 @@ function getSupabaseAnonKey() {
4341
4341
  function getVersionString() {
4342
4342
  return `v${VERSION}`;
4343
4343
  }
4344
- var VERSION = "0.13.1";
4344
+ var VERSION = "0.13.3";
4345
4345
 
4346
4346
  // src/debug/logger.ts
4347
4347
  import {
@@ -4353,6 +4353,17 @@ import {
4353
4353
  writeFileSync as writeFileSync2
4354
4354
  } from "node:fs";
4355
4355
  import { join as join2 } from "node:path";
4356
+ function redactSecrets(message) {
4357
+ let redacted = message;
4358
+ for (const key of SECRET_QUERY_KEYS) {
4359
+ const queryPattern = new RegExp(`([?&]${key}=)[^\\s&#]+`, "gi");
4360
+ redacted = redacted.replace(queryPattern, `$1${REDACTED}`);
4361
+ }
4362
+ redacted = redacted.replace(/(\b(?:access_token|refresh_token|api_key|apikey|id_token|token)\b\s*[:=]\s*)("[^"]+"|'[^']+'|[^\s,]+)/gi, `$1${REDACTED}`);
4363
+ redacted = redacted.replace(/(X-Dosu-API-Key:\s*)[^\s,]+/gi, `$1${REDACTED}`);
4364
+ redacted = redacted.replace(/(Authorization:\s*Bearer\s+)[^\s,]+/gi, `$1${REDACTED}`);
4365
+ return redacted;
4366
+ }
4356
4367
  function ensureInit() {
4357
4368
  if (!initialized) {
4358
4369
  initLogger({});
@@ -4409,7 +4420,8 @@ function initLogger(opts) {
4409
4420
  function writeEntry(level, mod, message) {
4410
4421
  ensureInit();
4411
4422
  const timestamp = new Date().toISOString();
4412
- const line = `[${timestamp}] [${level}] [${mod}] ${message}
4423
+ const safeMessage = redactSecrets(message);
4424
+ const line = `[${timestamp}] [${level}] [${mod}] ${safeMessage}
4413
4425
  `;
4414
4426
  try {
4415
4427
  appendFileSync(resolveLogPath(), line, { mode: 384 });
@@ -4424,10 +4436,19 @@ function writeEntry(level, mod, message) {
4424
4436
  console.error(colorize(line.trimEnd()));
4425
4437
  }
4426
4438
  }
4427
- var import_picocolors, MAX_LOG_SIZE = 1048576, TRUNCATE_KEEP = 524288, LOG_FILENAME = "debug.log", initialized = false, debugToConsole = false, logFilePath = null, logger;
4439
+ var import_picocolors, MAX_LOG_SIZE = 1048576, TRUNCATE_KEEP = 524288, LOG_FILENAME = "debug.log", REDACTED = "[REDACTED]", SECRET_QUERY_KEYS, initialized = false, debugToConsole = false, logFilePath = null, logger;
4428
4440
  var init_logger = __esm(() => {
4429
4441
  init_config();
4430
4442
  import_picocolors = __toESM(require_picocolors(), 1);
4443
+ SECRET_QUERY_KEYS = [
4444
+ "access_token",
4445
+ "refresh_token",
4446
+ "api_key",
4447
+ "apikey",
4448
+ "token",
4449
+ "id_token",
4450
+ "code"
4451
+ ];
4431
4452
  logger = {
4432
4453
  init: initLogger,
4433
4454
  getLogPath() {
@@ -6635,7 +6656,8 @@ var init_skill = __esm(() => {
6635
6656
  });
6636
6657
 
6637
6658
  // src/mcp/config-helpers.ts
6638
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
6659
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "node:fs";
6660
+ import { createRequire as createRequire2 } from "node:module";
6639
6661
  import { dirname } from "node:path";
6640
6662
  function mcpURL(deploymentID) {
6641
6663
  return `${getBackendURL()}/v1/mcp/deployments/${deploymentID}`;
@@ -6644,6 +6666,9 @@ function mcpBaseURL() {
6644
6666
  return `${getBackendURL()}/v1/mcp`;
6645
6667
  }
6646
6668
  function mcpHeaders(apiKey) {
6669
+ if (!apiKey) {
6670
+ throw new Error("API key is required. Run 'dosu setup' to create one.");
6671
+ }
6647
6672
  return { "X-Dosu-API-Key": apiKey };
6648
6673
  }
6649
6674
  function loadJSONConfig(path) {
@@ -6708,11 +6733,14 @@ function stripJSONComments(data) {
6708
6733
  return result.join("");
6709
6734
  }
6710
6735
  function saveJSONConfig(path, cfg) {
6736
+ writeSecureFile(path, JSON.stringify(cfg, null, 2));
6737
+ }
6738
+ function writeSecureFile(path, content) {
6711
6739
  const dir = dirname(path);
6712
6740
  if (!existsSync4(dir)) {
6713
- mkdirSync4(dir, { recursive: true });
6741
+ mkdirSync4(dir, { recursive: true, mode: 448 });
6714
6742
  }
6715
- writeFileSync4(path, JSON.stringify(cfg, null, 2));
6743
+ writeFileAtomic.sync(path, content, { mode: 384, chown: false });
6716
6744
  }
6717
6745
  function isJSONKeyConfigured(configPath, topLevelKey) {
6718
6746
  const cfg = loadJSONConfig(configPath);
@@ -6744,7 +6772,11 @@ function removeJSONServer(configPath, topKey) {
6744
6772
  }
6745
6773
  saveJSONConfig(configPath, jsonCfg);
6746
6774
  }
6747
- var init_config_helpers = () => {};
6775
+ var require2, writeFileAtomic;
6776
+ var init_config_helpers = __esm(() => {
6777
+ require2 = createRequire2(import.meta.url);
6778
+ writeFileAtomic = require2("write-file-atomic");
6779
+ });
6748
6780
 
6749
6781
  // src/mcp/detect.ts
6750
6782
  import { existsSync as existsSync5 } from "node:fs";
@@ -6938,8 +6970,8 @@ var init_cline_cli = __esm(() => {
6938
6970
  });
6939
6971
 
6940
6972
  // src/mcp/providers/codex.ts
6941
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
6942
- import { dirname as dirname2, join as join9 } from "node:path";
6973
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
6974
+ import { join as join9 } from "node:path";
6943
6975
  function codexHome() {
6944
6976
  return process.env.CODEX_HOME ?? expandHome("~/.codex");
6945
6977
  }
@@ -6954,10 +6986,7 @@ function readTOML(path) {
6954
6986
  return readFileSync6(path, "utf-8");
6955
6987
  }
6956
6988
  function writeTOML(path, content) {
6957
- const dir = dirname2(path);
6958
- if (!existsSync6(dir))
6959
- mkdirSync5(dir, { recursive: true });
6960
- writeFileSync5(path, content);
6989
+ writeSecureFile(path, content);
6961
6990
  }
6962
6991
  function mcpEndpoint(cfg) {
6963
6992
  if (cfg.mode === MODE_OSS)
@@ -7139,17 +7168,29 @@ function mcpEndpoint3(cfg) {
7139
7168
  throw new Error("deployment ID is required");
7140
7169
  return mcpURL(cfg.deployment_id);
7141
7170
  }
7171
+ function maskSecret(secret) {
7172
+ if (secret.length <= 8)
7173
+ return "[hidden]";
7174
+ const visibleChars = Math.min(4, Math.floor(secret.length / 4));
7175
+ return `${secret.slice(0, visibleChars)}...${secret.slice(-visibleChars)}`;
7176
+ }
7142
7177
  var ManualProvider = () => ({
7143
7178
  name: () => "Manual Configuration",
7144
7179
  id: () => "manual",
7145
7180
  supportsLocal: () => false,
7146
- install(cfg) {
7181
+ install(cfg, _global, opts = {}) {
7147
7182
  const url = mcpEndpoint3(cfg);
7183
+ const apiKey = mcpHeaders(cfg.api_key)["X-Dosu-API-Key"];
7184
+ const headerValue = opts.showSecret ? apiKey : maskSecret(apiKey);
7148
7185
  console.log("Use these details to configure the Dosu MCP server in your client:");
7149
7186
  console.log();
7150
7187
  console.log(` Transport: HTTP`);
7151
7188
  console.log(` Endpoint: ${url}`);
7152
- console.log(` Header: X-Dosu-API-Key: ${cfg.api_key}`);
7189
+ console.log(` Header: X-Dosu-API-Key: ${headerValue}`);
7190
+ if (!opts.showSecret) {
7191
+ console.log();
7192
+ console.log(" Secret hidden. Re-run with --show-secret to print the full API key.");
7193
+ }
7153
7194
  console.log();
7154
7195
  },
7155
7196
  remove() {
@@ -8659,15 +8700,16 @@ async function createDeploymentForRepo(trpc, orgID, spaceID, repo) {
8659
8700
  await deleteOrphanDeployment(trpc, deployment.deployment_id, repo.slug);
8660
8701
  return null;
8661
8702
  }
8662
- await trpc.dataSource.syncDataSource.mutate(dataSource.data_source_id);
8703
+ const dataSourceID = dataSource.data_source_id;
8704
+ await trpc.dataSource.syncDataSource.mutate(dataSourceID);
8663
8705
  const spaceDeployments = await trpc.workspaces.listForSpace.query(spaceID);
8664
8706
  await Promise.all(spaceDeployments.map((d3) => trpc.deploymentDataSource.create.mutate({
8665
8707
  deployment_id: d3.deployment_id,
8666
- data_source_id: dataSource.data_source_id
8708
+ data_source_id: dataSourceID
8667
8709
  })));
8668
8710
  return {
8669
8711
  deployment_id: deployment.deployment_id,
8670
- data_source_id: dataSource.data_source_id
8712
+ data_source_id: dataSourceID
8671
8713
  };
8672
8714
  } catch (err) {
8673
8715
  const msg = err instanceof Error ? err.message : String(err);
@@ -9685,7 +9727,7 @@ async function startCallbackServer() {
9685
9727
  const url = new URL(req.url ?? "/", `http://localhost`);
9686
9728
  const cookieLen = (req.headers.cookie ?? "").length;
9687
9729
  const ua = req.headers["user-agent"] ?? "none";
9688
- logger.debug("auth.server", `Request: ${req.method} ${req.url} cookie-len=${cookieLen} ua=${ua} has-token=${url.searchParams.has("access_token")}`);
9730
+ logger.debug("auth.server", `Request: ${req.method} ${url.pathname} cookie-len=${cookieLen} ua=${ua} has-token=${url.searchParams.has("access_token")}`);
9689
9731
  if (url.pathname !== "/callback") {
9690
9732
  logger.debug("auth.server", `404: ${url.pathname}`);
9691
9733
  res.writeHead(404, { "Content-Type": "text/plain" });
@@ -10544,8 +10586,8 @@ var init_flow2 = __esm(() => {
10544
10586
  });
10545
10587
 
10546
10588
  // src/commands/insights.ts
10547
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
10548
- import { dirname as dirname3, join as join19 } from "node:path";
10589
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
10590
+ import { dirname as dirname2, join as join19 } from "node:path";
10549
10591
  function shuffled(arr) {
10550
10592
  const copy2 = [...arr];
10551
10593
  for (let i = copy2.length - 1;i > 0; i--) {
@@ -10743,10 +10785,10 @@ function defaultRunner(cfg) {
10743
10785
  build: buildInsights,
10744
10786
  render: renderHTML,
10745
10787
  writeFile: (path2, content) => {
10746
- const dir = dirname3(path2);
10788
+ const dir = dirname2(path2);
10747
10789
  if (!existsSync9(dir))
10748
- mkdirSync6(dir, { recursive: true, mode: 448 });
10749
- writeFileSync6(path2, content, { mode: 384 });
10790
+ mkdirSync5(dir, { recursive: true, mode: 448 });
10791
+ writeFileSync4(path2, content, { mode: 384 });
10750
10792
  },
10751
10793
  prune: pruneOldReports,
10752
10794
  openInBrowser: async (path2) => {
@@ -12377,7 +12419,7 @@ init_skill_update_check();
12377
12419
  init_config();
12378
12420
  init_logger();
12379
12421
  var import_picocolors24 = __toESM(require_picocolors(), 1);
12380
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
12422
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
12381
12423
  import { join as join20 } from "node:path";
12382
12424
  var CACHE_FILENAME2 = "update-check.json";
12383
12425
  var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
@@ -12421,9 +12463,9 @@ function writeCache(cache) {
12421
12463
  try {
12422
12464
  const dir = getConfigDir();
12423
12465
  if (!existsSync10(dir)) {
12424
- mkdirSync7(dir, { recursive: true, mode: 448 });
12466
+ mkdirSync6(dir, { recursive: true, mode: 448 });
12425
12467
  }
12426
- writeFileSync7(getCachePath2(), JSON.stringify(cache), { mode: 384 });
12468
+ writeFileSync5(getCachePath2(), JSON.stringify(cache), { mode: 384 });
12427
12469
  } catch {}
12428
12470
  }
12429
12471
  async function fetchLatestVersion() {
@@ -12556,7 +12598,7 @@ function createProgram() {
12556
12598
  }
12557
12599
  });
12558
12600
  const mcp = program2.command("mcp").description("Manage MCP server integrations");
12559
- mcp.command("add <agent>").description("Add Dosu MCP to an AI tool").option("-g, --global", "Add globally (all projects) instead of project-local", false).action((toolId, opts) => {
12601
+ mcp.command("add <agent>").description("Add Dosu MCP to an AI tool").option("-g, --global", "Add globally (all projects) instead of project-local", false).option("--show-secret", "Print full manual configuration secrets", false).action((toolId, opts) => {
12560
12602
  let provider;
12561
12603
  try {
12562
12604
  provider = getProvider(toolId.toLowerCase());
@@ -12573,8 +12615,11 @@ function createProgram() {
12573
12615
  if (cfg.mode !== MODE_OSS && !cfg.deployment_id) {
12574
12616
  throw new Error("no MCP selected. Run 'dosu' to open the TUI and select an MCP");
12575
12617
  }
12618
+ if (!cfg.api_key) {
12619
+ throw new Error("no API key available. Run 'dosu setup' to create one");
12620
+ }
12576
12621
  if (provider.id() === "manual") {
12577
- provider.install(cfg, false);
12622
+ provider.install(cfg, false, { showSecret: opts.showSecret });
12578
12623
  return;
12579
12624
  }
12580
12625
  let global = opts.global;
@@ -12585,7 +12630,7 @@ function createProgram() {
12585
12630
  }
12586
12631
  const scope = global ? "global (all projects)" : "project-local";
12587
12632
  console.log(`Adding Dosu MCP to ${provider.name()} (${scope})...`);
12588
- provider.install(cfg, global);
12633
+ provider.install(cfg, global, { showSecret: opts.showSecret });
12589
12634
  console.log(`
12590
12635
  ✓ Successfully added Dosu MCP to ${provider.name()}!`);
12591
12636
  if (global) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",
@@ -52,6 +52,7 @@
52
52
  "commander": "^13.1.0",
53
53
  "open": "^10.1.0",
54
54
  "picocolors": "^1.1.1",
55
- "superjson": "^2.2.6"
55
+ "superjson": "^2.2.6",
56
+ "write-file-atomic": "^5.0.1"
56
57
  }
57
58
  }