@dosu/cli 0.13.2 → 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.
- package/bin/dosu.js +71 -27
- 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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
6942
|
-
import {
|
|
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
|
-
|
|
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: ${
|
|
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() {
|
|
@@ -9686,7 +9727,7 @@ async function startCallbackServer() {
|
|
|
9686
9727
|
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
9687
9728
|
const cookieLen = (req.headers.cookie ?? "").length;
|
|
9688
9729
|
const ua = req.headers["user-agent"] ?? "none";
|
|
9689
|
-
logger.debug("auth.server", `Request: ${req.method} ${
|
|
9730
|
+
logger.debug("auth.server", `Request: ${req.method} ${url.pathname} cookie-len=${cookieLen} ua=${ua} has-token=${url.searchParams.has("access_token")}`);
|
|
9690
9731
|
if (url.pathname !== "/callback") {
|
|
9691
9732
|
logger.debug("auth.server", `404: ${url.pathname}`);
|
|
9692
9733
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
@@ -10545,8 +10586,8 @@ var init_flow2 = __esm(() => {
|
|
|
10545
10586
|
});
|
|
10546
10587
|
|
|
10547
10588
|
// src/commands/insights.ts
|
|
10548
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
10549
|
-
import { dirname as
|
|
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";
|
|
10550
10591
|
function shuffled(arr) {
|
|
10551
10592
|
const copy2 = [...arr];
|
|
10552
10593
|
for (let i = copy2.length - 1;i > 0; i--) {
|
|
@@ -10744,10 +10785,10 @@ function defaultRunner(cfg) {
|
|
|
10744
10785
|
build: buildInsights,
|
|
10745
10786
|
render: renderHTML,
|
|
10746
10787
|
writeFile: (path2, content) => {
|
|
10747
|
-
const dir =
|
|
10788
|
+
const dir = dirname2(path2);
|
|
10748
10789
|
if (!existsSync9(dir))
|
|
10749
|
-
|
|
10750
|
-
|
|
10790
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
10791
|
+
writeFileSync4(path2, content, { mode: 384 });
|
|
10751
10792
|
},
|
|
10752
10793
|
prune: pruneOldReports,
|
|
10753
10794
|
openInBrowser: async (path2) => {
|
|
@@ -12378,7 +12419,7 @@ init_skill_update_check();
|
|
|
12378
12419
|
init_config();
|
|
12379
12420
|
init_logger();
|
|
12380
12421
|
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
12381
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
12422
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
12382
12423
|
import { join as join20 } from "node:path";
|
|
12383
12424
|
var CACHE_FILENAME2 = "update-check.json";
|
|
12384
12425
|
var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
|
|
@@ -12422,9 +12463,9 @@ function writeCache(cache) {
|
|
|
12422
12463
|
try {
|
|
12423
12464
|
const dir = getConfigDir();
|
|
12424
12465
|
if (!existsSync10(dir)) {
|
|
12425
|
-
|
|
12466
|
+
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
12426
12467
|
}
|
|
12427
|
-
|
|
12468
|
+
writeFileSync5(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
12428
12469
|
} catch {}
|
|
12429
12470
|
}
|
|
12430
12471
|
async function fetchLatestVersion() {
|
|
@@ -12557,7 +12598,7 @@ function createProgram() {
|
|
|
12557
12598
|
}
|
|
12558
12599
|
});
|
|
12559
12600
|
const mcp = program2.command("mcp").description("Manage MCP server integrations");
|
|
12560
|
-
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) => {
|
|
12561
12602
|
let provider;
|
|
12562
12603
|
try {
|
|
12563
12604
|
provider = getProvider(toolId.toLowerCase());
|
|
@@ -12574,8 +12615,11 @@ function createProgram() {
|
|
|
12574
12615
|
if (cfg.mode !== MODE_OSS && !cfg.deployment_id) {
|
|
12575
12616
|
throw new Error("no MCP selected. Run 'dosu' to open the TUI and select an MCP");
|
|
12576
12617
|
}
|
|
12618
|
+
if (!cfg.api_key) {
|
|
12619
|
+
throw new Error("no API key available. Run 'dosu setup' to create one");
|
|
12620
|
+
}
|
|
12577
12621
|
if (provider.id() === "manual") {
|
|
12578
|
-
provider.install(cfg, false);
|
|
12622
|
+
provider.install(cfg, false, { showSecret: opts.showSecret });
|
|
12579
12623
|
return;
|
|
12580
12624
|
}
|
|
12581
12625
|
let global = opts.global;
|
|
@@ -12586,7 +12630,7 @@ function createProgram() {
|
|
|
12586
12630
|
}
|
|
12587
12631
|
const scope = global ? "global (all projects)" : "project-local";
|
|
12588
12632
|
console.log(`Adding Dosu MCP to ${provider.name()} (${scope})...`);
|
|
12589
|
-
provider.install(cfg, global);
|
|
12633
|
+
provider.install(cfg, global, { showSecret: opts.showSecret });
|
|
12590
12634
|
console.log(`
|
|
12591
12635
|
✓ Successfully added Dosu MCP to ${provider.name()}!`);
|
|
12592
12636
|
if (global) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dosu/cli",
|
|
3
|
-
"version": "0.13.
|
|
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
|
}
|