@yawlabs/mcp 0.62.0 → 0.63.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/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
signIn,
|
|
18
18
|
signOut,
|
|
19
19
|
userConfigDir
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-BTL5M3GN.js";
|
|
21
21
|
|
|
22
22
|
// src/audit-cmd.ts
|
|
23
23
|
import { homedir as homedir3 } from "os";
|
|
@@ -401,7 +401,7 @@ function parseAuditArgs(argv) {
|
|
|
401
401
|
if (a === "--json") {
|
|
402
402
|
json = true;
|
|
403
403
|
} else if (a === "--help" || a === "-h") {
|
|
404
|
-
return { ok: false, error: AUDIT_USAGE };
|
|
404
|
+
return { ok: false, error: AUDIT_USAGE, help: true };
|
|
405
405
|
} else if (a.startsWith("-")) {
|
|
406
406
|
return { ok: false, error: `yaw-mcp audit: unknown argument "${a}"
|
|
407
407
|
|
|
@@ -451,7 +451,7 @@ async function runAudit(opts = {}) {
|
|
|
451
451
|
const namespace = opts.namespace;
|
|
452
452
|
if (!namespace) {
|
|
453
453
|
printErr("yaw-mcp audit: missing <namespace>.");
|
|
454
|
-
return { exitCode:
|
|
454
|
+
return { exitCode: 2, lines };
|
|
455
455
|
}
|
|
456
456
|
const home = opts.home ?? homedir3();
|
|
457
457
|
const { config, path: path5 } = await loadLocalBundles({ cwd: opts.cwd, home });
|
|
@@ -576,6 +576,9 @@ function topPartialBundles(installedNamespaces, limit) {
|
|
|
576
576
|
}).slice(0, limit);
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
+
// src/config.ts
|
|
580
|
+
import { request } from "undici";
|
|
581
|
+
|
|
579
582
|
// src/config-loader.ts
|
|
580
583
|
import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
581
584
|
import { homedir as homedir4 } from "os";
|
|
@@ -841,7 +844,6 @@ function profileAllows(profile, namespace) {
|
|
|
841
844
|
}
|
|
842
845
|
|
|
843
846
|
// src/config.ts
|
|
844
|
-
import { request } from "undici";
|
|
845
847
|
async function fetchConfig(apiUrl5, token5, currentVersion) {
|
|
846
848
|
const url = `${apiUrl5.replace(/\/$/, "")}/api/connect/config`;
|
|
847
849
|
const headers = {
|
|
@@ -1152,6 +1154,12 @@ var SUBCOMMAND_SPEC = [
|
|
|
1152
1154
|
// Other.
|
|
1153
1155
|
{ name: "audit", description: "Run a full-pass audit of loaded servers", flags: ["--json", "--help"] },
|
|
1154
1156
|
{ name: "compliance", description: "Run the compliance suite against a server", flags: ["--publish", "--help"] },
|
|
1157
|
+
{
|
|
1158
|
+
name: "foundry",
|
|
1159
|
+
description: "Export the opt-in dispatch-trace corpus",
|
|
1160
|
+
positional: ["export"],
|
|
1161
|
+
flags: ["--out", "--cap", "--json", "--help"]
|
|
1162
|
+
},
|
|
1155
1163
|
{ name: "help", description: "Show usage", flags: [] }
|
|
1156
1164
|
];
|
|
1157
1165
|
function parseCompletionArgs(argv) {
|
|
@@ -1226,7 +1234,7 @@ ${posClause}
|
|
|
1226
1234
|
# Install: save this to ~/.local/share/bash-completion/completions/yaw-mcp
|
|
1227
1235
|
# or source it from your .bashrc.
|
|
1228
1236
|
_yaw-mcp() {
|
|
1229
|
-
local cur
|
|
1237
|
+
local cur cword
|
|
1230
1238
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1231
1239
|
cword=$COMP_CWORD
|
|
1232
1240
|
|
|
@@ -1337,14 +1345,17 @@ ${caseBranches}
|
|
|
1337
1345
|
// src/compliance-cmd.ts
|
|
1338
1346
|
import { spawn } from "child_process";
|
|
1339
1347
|
import { request as request2 } from "undici";
|
|
1348
|
+
var COMPLIANCE_USAGE = '\n Usage: yaw-mcp compliance <target> [extraArgs...] [--publish]\n\n Examples:\n yaw-mcp compliance "npx -y @modelcontextprotocol/server-filesystem /tmp"\n yaw-mcp compliance https://example.com/mcp --publish\n\n';
|
|
1340
1349
|
async function runComplianceCommand(argv) {
|
|
1350
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
1351
|
+
process.stdout.write(COMPLIANCE_USAGE);
|
|
1352
|
+
return 0;
|
|
1353
|
+
}
|
|
1341
1354
|
const publish = argv.includes("--publish");
|
|
1342
1355
|
const args = argv.filter((a) => a !== "--publish");
|
|
1343
1356
|
if (args.length === 0) {
|
|
1344
|
-
process.stderr.write(
|
|
1345
|
-
|
|
1346
|
-
);
|
|
1347
|
-
return 1;
|
|
1357
|
+
process.stderr.write(COMPLIANCE_USAGE);
|
|
1358
|
+
return 2;
|
|
1348
1359
|
}
|
|
1349
1360
|
const apiUrl5 = process.env.YAW_MCP_URL ?? "https://yaw.sh/mcp";
|
|
1350
1361
|
const report = await runTest(args);
|
|
@@ -1372,6 +1383,8 @@ Delete token (save this): ${result.deleteToken}
|
|
|
1372
1383
|
}
|
|
1373
1384
|
return 0;
|
|
1374
1385
|
}
|
|
1386
|
+
var MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
1387
|
+
var CHILD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1375
1388
|
function runTest(args) {
|
|
1376
1389
|
return new Promise((resolve7) => {
|
|
1377
1390
|
const child = spawn("npx", ["-y", "@yawlabs/mcp-compliance", "test", "--format", "json", ...args], {
|
|
@@ -1379,16 +1392,48 @@ function runTest(args) {
|
|
|
1379
1392
|
shell: process.platform === "win32"
|
|
1380
1393
|
});
|
|
1381
1394
|
let stdout = "";
|
|
1395
|
+
let stdoutBytes = 0;
|
|
1396
|
+
let settled = false;
|
|
1397
|
+
const timer = setTimeout(() => {
|
|
1398
|
+
if (settled) return;
|
|
1399
|
+
settled = true;
|
|
1400
|
+
child.kill();
|
|
1401
|
+
process.stderr.write(`
|
|
1402
|
+
mcp-compliance timed out after ${CHILD_TIMEOUT_MS / 1e3}s; killed.
|
|
1403
|
+
`);
|
|
1404
|
+
resolve7(null);
|
|
1405
|
+
}, CHILD_TIMEOUT_MS);
|
|
1406
|
+
timer.unref?.();
|
|
1382
1407
|
child.stdout.on("data", (chunk) => {
|
|
1408
|
+
if (settled) return;
|
|
1409
|
+
stdoutBytes += chunk.length;
|
|
1410
|
+
if (stdoutBytes > MAX_STDOUT_BYTES) {
|
|
1411
|
+
settled = true;
|
|
1412
|
+
clearTimeout(timer);
|
|
1413
|
+
child.kill();
|
|
1414
|
+
process.stderr.write(
|
|
1415
|
+
`
|
|
1416
|
+
mcp-compliance produced more than ${MAX_STDOUT_BYTES / (1024 * 1024)} MB of output; killed.
|
|
1417
|
+
`
|
|
1418
|
+
);
|
|
1419
|
+
resolve7(null);
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1383
1422
|
stdout += chunk.toString();
|
|
1384
1423
|
});
|
|
1385
1424
|
child.on("error", (err) => {
|
|
1425
|
+
if (settled) return;
|
|
1426
|
+
settled = true;
|
|
1427
|
+
clearTimeout(timer);
|
|
1386
1428
|
process.stderr.write(`
|
|
1387
1429
|
Failed to launch mcp-compliance: ${err.message}
|
|
1388
1430
|
`);
|
|
1389
1431
|
resolve7(null);
|
|
1390
1432
|
});
|
|
1391
1433
|
child.on("close", (code) => {
|
|
1434
|
+
if (settled) return;
|
|
1435
|
+
settled = true;
|
|
1436
|
+
clearTimeout(timer);
|
|
1392
1437
|
try {
|
|
1393
1438
|
const parsed = JSON.parse(stdout);
|
|
1394
1439
|
if (!parsed.grade || !parsed.summary) {
|
|
@@ -1417,12 +1462,36 @@ Target: ${url}
|
|
|
1417
1462
|
`
|
|
1418
1463
|
);
|
|
1419
1464
|
}
|
|
1465
|
+
function projectForPublish(report) {
|
|
1466
|
+
const tests = Array.isArray(report.tests) ? report.tests : [];
|
|
1467
|
+
return {
|
|
1468
|
+
grade: report.grade,
|
|
1469
|
+
score: report.score,
|
|
1470
|
+
url: report.url,
|
|
1471
|
+
summary: {
|
|
1472
|
+
total: report.summary.total,
|
|
1473
|
+
passed: report.summary.passed,
|
|
1474
|
+
failed: report.summary.failed,
|
|
1475
|
+
required: report.summary.required,
|
|
1476
|
+
requiredPassed: report.summary.requiredPassed
|
|
1477
|
+
},
|
|
1478
|
+
tests: tests.map((t) => {
|
|
1479
|
+
const test = t ?? {};
|
|
1480
|
+
const projected = {};
|
|
1481
|
+
if (typeof test.name === "string") projected.name = test.name;
|
|
1482
|
+
if (typeof test.status === "string") projected.status = test.status;
|
|
1483
|
+
if (typeof test.required === "boolean") projected.required = test.required;
|
|
1484
|
+
if (typeof test.message === "string") projected.message = test.message;
|
|
1485
|
+
return projected;
|
|
1486
|
+
})
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1420
1489
|
async function publishReport(apiUrl5, report) {
|
|
1421
1490
|
try {
|
|
1422
1491
|
const res = await request2(`${apiUrl5.replace(/\/$/, "")}/api/compliance/ext`, {
|
|
1423
1492
|
method: "POST",
|
|
1424
1493
|
headers: { "Content-Type": "application/json" },
|
|
1425
|
-
body: JSON.stringify(report)
|
|
1494
|
+
body: JSON.stringify(projectForPublish(report))
|
|
1426
1495
|
});
|
|
1427
1496
|
if (res.statusCode !== 200) {
|
|
1428
1497
|
const body = await res.body.text().catch(() => "");
|
|
@@ -1720,7 +1789,7 @@ function resolveShadowedClis(server) {
|
|
|
1720
1789
|
if (cache.length < 3) return [];
|
|
1721
1790
|
const prefixes = /* @__PURE__ */ new Set();
|
|
1722
1791
|
for (const t of cache) {
|
|
1723
|
-
const first = t.name.split(/[_
|
|
1792
|
+
const first = t.name.split(/[_.-]/)[0];
|
|
1724
1793
|
if (first) prefixes.add(first.toLowerCase());
|
|
1725
1794
|
}
|
|
1726
1795
|
if (prefixes.size !== 1) return [];
|
|
@@ -2083,7 +2152,7 @@ async function reportTools(serverId, tools) {
|
|
|
2083
2152
|
// src/try-cmd.ts
|
|
2084
2153
|
import { createHash as createHash2 } from "crypto";
|
|
2085
2154
|
import { existsSync as existsSync3 } from "fs";
|
|
2086
|
-
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile6,
|
|
2155
|
+
import { chmod as chmod2, mkdir as mkdir2, readdir, readFile as readFile6, unlink } from "fs/promises";
|
|
2087
2156
|
import { homedir as homedir7, hostname, userInfo } from "os";
|
|
2088
2157
|
import { join as join7, resolve as resolve5 } from "path";
|
|
2089
2158
|
import { request as request5 } from "undici";
|
|
@@ -2189,7 +2258,8 @@ import { chmod, readFile as readFile5 } from "fs/promises";
|
|
|
2189
2258
|
import { homedir as homedir6 } from "os";
|
|
2190
2259
|
import { join as join6, resolve as resolve4 } from "path";
|
|
2191
2260
|
import { createInterface } from "readline/promises";
|
|
2192
|
-
|
|
2261
|
+
import { Writable } from "stream";
|
|
2262
|
+
var USAGE = "Usage: yaw-mcp install <claude-code|claude-desktop|cursor|vscode> [--scope user|project|local]\n [--token <mcp_pat_\u2026>] [--project-dir <path>] [--os macos|linux|windows]\n [--force | --skip] [--dry-run] [--no-yaw-mcp-config]\n yaw-mcp install --list (detect clients; no writes)\n yaw-mcp install --all [--token <mcp_pat_\u2026>] (install into every detected client)\n\n Note: --token puts the PAT on the command line, where it is visible in shell\n history and the process table (ps/Task Manager) -- avoid it on shared machines.\n Prefer seeding ~/.yaw-mcp/config.json once (install reads the token from there),\n or set the token via your account before installing.";
|
|
2193
2263
|
async function runInstall(opts) {
|
|
2194
2264
|
const stdout = opts.io?.stdout ?? process.stdout;
|
|
2195
2265
|
const stderr = opts.io?.stderr ?? process.stderr;
|
|
@@ -2351,7 +2421,7 @@ ${USAGE}`);
|
|
|
2351
2421
|
if (opts.dryRun) {
|
|
2352
2422
|
log2("\n--- dry run: would write the following ---");
|
|
2353
2423
|
if (writeYawMcpConfig) log2(`# ${yawMcpConfigPath}
|
|
2354
|
-
${yawMcpConfigJson}`);
|
|
2424
|
+
${redactConfigToken(yawMcpConfigJson)}`);
|
|
2355
2425
|
log2(`
|
|
2356
2426
|
# ${resolved.absolute}
|
|
2357
2427
|
${clientJson}`);
|
|
@@ -2371,7 +2441,7 @@ ${settingsPatch.nextJson}`);
|
|
|
2371
2441
|
const written = [];
|
|
2372
2442
|
if (writeYawMcpConfig) {
|
|
2373
2443
|
try {
|
|
2374
|
-
await atomicWriteFile(yawMcpConfigPath, yawMcpConfigJson);
|
|
2444
|
+
await atomicWriteFile(yawMcpConfigPath, yawMcpConfigJson, "utf8", 384);
|
|
2375
2445
|
if (process.platform !== "win32") {
|
|
2376
2446
|
try {
|
|
2377
2447
|
await chmod(yawMcpConfigPath, 384);
|
|
@@ -2527,6 +2597,21 @@ function removeFromClientConfig(existing, containerPath, entryName) {
|
|
|
2527
2597
|
parent[leafKey] = container;
|
|
2528
2598
|
return out;
|
|
2529
2599
|
}
|
|
2600
|
+
async function writeBackup(path5, raw) {
|
|
2601
|
+
const candidate = `${path5}.bak-${Date.now()}`;
|
|
2602
|
+
try {
|
|
2603
|
+
await atomicWriteFile(candidate, raw, "utf8", 384);
|
|
2604
|
+
if (process.platform !== "win32") {
|
|
2605
|
+
try {
|
|
2606
|
+
await chmod(candidate, 384);
|
|
2607
|
+
} catch {
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
return candidate;
|
|
2611
|
+
} catch {
|
|
2612
|
+
return void 0;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2530
2615
|
async function composeYawMcpConfig(path5, token5) {
|
|
2531
2616
|
let existing = {};
|
|
2532
2617
|
let backupPath;
|
|
@@ -2543,20 +2628,10 @@ async function composeYawMcpConfig(path5, token5) {
|
|
|
2543
2628
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2544
2629
|
existing = parsed;
|
|
2545
2630
|
} else {
|
|
2546
|
-
|
|
2547
|
-
try {
|
|
2548
|
-
await atomicWriteFile(candidate, raw);
|
|
2549
|
-
backupPath = candidate;
|
|
2550
|
-
} catch {
|
|
2551
|
-
}
|
|
2631
|
+
backupPath = await writeBackup(path5, raw);
|
|
2552
2632
|
}
|
|
2553
2633
|
} catch {
|
|
2554
|
-
|
|
2555
|
-
try {
|
|
2556
|
-
await atomicWriteFile(candidate, raw);
|
|
2557
|
-
backupPath = candidate;
|
|
2558
|
-
} catch {
|
|
2559
|
-
}
|
|
2634
|
+
backupPath = await writeBackup(path5, raw);
|
|
2560
2635
|
}
|
|
2561
2636
|
}
|
|
2562
2637
|
}
|
|
@@ -2566,6 +2641,9 @@ async function composeYawMcpConfig(path5, token5) {
|
|
|
2566
2641
|
return { json: `${JSON.stringify(next, null, 2)}
|
|
2567
2642
|
`, backupPath };
|
|
2568
2643
|
}
|
|
2644
|
+
function redactConfigToken(json) {
|
|
2645
|
+
return json.replace(/("token"\s*:\s*)"(?:[^"\\]|\\.)*"/g, '$1"mcp_pat_***"');
|
|
2646
|
+
}
|
|
2569
2647
|
function parseInstallArgs(argv) {
|
|
2570
2648
|
if (argv.length === 0) return { ok: false, error: USAGE };
|
|
2571
2649
|
const positional = [];
|
|
@@ -2680,7 +2758,7 @@ async function runInstallList(opts, log2) {
|
|
|
2680
2758
|
}
|
|
2681
2759
|
log2("");
|
|
2682
2760
|
log2("Install into a specific client: `yaw-mcp install <client> [--scope user|project|local]`");
|
|
2683
|
-
log2("Install into every available user
|
|
2761
|
+
log2("Install into every available client (user scope where supported): `yaw-mcp install --all`");
|
|
2684
2762
|
return { written: [], wouldWrite: [], messages: [], exitCode: 0 };
|
|
2685
2763
|
}
|
|
2686
2764
|
function statusFor(p) {
|
|
@@ -2737,15 +2815,35 @@ async function runInstallAll(opts, log2, err) {
|
|
|
2737
2815
|
const aggregateMessages = [];
|
|
2738
2816
|
let failed = 0;
|
|
2739
2817
|
let succeeded = 0;
|
|
2818
|
+
const collisionClients = [];
|
|
2819
|
+
const realStderr = opts.io?.stderr ?? process.stderr;
|
|
2820
|
+
const isCollisionRefusal = (s) => s.includes(`already has a "${ENTRY_NAME}" entry and stdin is not a TTY`);
|
|
2740
2821
|
for (const plan of plans) {
|
|
2741
2822
|
log2(`\u2500\u2500 ${plan.clientId} (${plan.scope}) \u2500\u2500`);
|
|
2823
|
+
let sawCollision = false;
|
|
2824
|
+
const subStderr = new Writable({
|
|
2825
|
+
write(chunk, _enc, cb) {
|
|
2826
|
+
const text = chunk.toString();
|
|
2827
|
+
if (isCollisionRefusal(text)) sawCollision = true;
|
|
2828
|
+
else realStderr.write(text);
|
|
2829
|
+
cb();
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
const baseIo = opts.io ?? {
|
|
2833
|
+
stdin: process.stdin,
|
|
2834
|
+
stdout: process.stdout,
|
|
2835
|
+
stderr: process.stderr,
|
|
2836
|
+
isTTY: Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY)
|
|
2837
|
+
};
|
|
2742
2838
|
const result = await runInstall({
|
|
2743
2839
|
...opts,
|
|
2744
2840
|
listOnly: false,
|
|
2745
2841
|
all: false,
|
|
2746
2842
|
clientId: plan.clientId,
|
|
2747
|
-
scope: plan.scope
|
|
2843
|
+
scope: plan.scope,
|
|
2844
|
+
io: { ...baseIo, stderr: subStderr }
|
|
2748
2845
|
});
|
|
2846
|
+
if (sawCollision) collisionClients.push(plan.clientId);
|
|
2749
2847
|
aggregateWritten.push(...result.written);
|
|
2750
2848
|
aggregateWouldWrite.push(...result.wouldWrite);
|
|
2751
2849
|
aggregateMessages.push(...result.messages);
|
|
@@ -2753,6 +2851,12 @@ async function runInstallAll(opts, log2, err) {
|
|
|
2753
2851
|
else failed += 1;
|
|
2754
2852
|
log2("");
|
|
2755
2853
|
}
|
|
2854
|
+
if (collisionClients.length > 0) {
|
|
2855
|
+
err(
|
|
2856
|
+
`yaw-mcp install --all: ${collisionClients.length} client${collisionClients.length === 1 ? "" : "s"} already have a "${ENTRY_NAME}" entry (${collisionClients.join(", ")}) and stdin is not a TTY.
|
|
2857
|
+
Re-run \`yaw-mcp install --all --force\` to overwrite them, or \`--skip\` to leave them untouched.`
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2756
2860
|
const totalPlanned = plans.length;
|
|
2757
2861
|
if (failed === 0) {
|
|
2758
2862
|
log2(`Done: ${succeeded}/${totalPlanned} clients installed successfully.`);
|
|
@@ -2829,7 +2933,7 @@ function parseTryArgs(argv) {
|
|
|
2829
2933
|
}
|
|
2830
2934
|
case "--env": {
|
|
2831
2935
|
const v = next();
|
|
2832
|
-
if (!v
|
|
2936
|
+
if (!v?.includes("=")) return { ok: false, error: "--env requires KEY=value" };
|
|
2833
2937
|
const eq = v.indexOf("=");
|
|
2834
2938
|
const key = v.slice(0, eq);
|
|
2835
2939
|
const val = v.slice(eq + 1);
|
|
@@ -2844,7 +2948,7 @@ function parseTryArgs(argv) {
|
|
|
2844
2948
|
break;
|
|
2845
2949
|
case "--base": {
|
|
2846
2950
|
const v = next();
|
|
2847
|
-
if (!v) return { ok: false, error: "--base requires a URL" };
|
|
2951
|
+
if (!v || v.startsWith("--")) return { ok: false, error: "--base requires a URL" };
|
|
2848
2952
|
opts.baseUrl = v;
|
|
2849
2953
|
break;
|
|
2850
2954
|
}
|
|
@@ -2853,6 +2957,8 @@ function parseTryArgs(argv) {
|
|
|
2853
2957
|
return { ok: false, error: TRY_USAGE, help: true };
|
|
2854
2958
|
default:
|
|
2855
2959
|
if (a.startsWith("--")) return { ok: false, error: `Unknown flag: ${a}
|
|
2960
|
+
${TRY_USAGE}` };
|
|
2961
|
+
if (a === "-") return { ok: false, error: `Invalid argument "-".
|
|
2856
2962
|
${TRY_USAGE}` };
|
|
2857
2963
|
positional.push(a);
|
|
2858
2964
|
}
|
|
@@ -2879,6 +2985,8 @@ function parseTryCleanupArgs(argv) {
|
|
|
2879
2985
|
continue;
|
|
2880
2986
|
}
|
|
2881
2987
|
if (a.startsWith("--")) return { ok: false, error: `Unknown flag: ${a}
|
|
2988
|
+
${TRY_CLEANUP_USAGE}` };
|
|
2989
|
+
if (a === "-") return { ok: false, error: `Invalid argument "-".
|
|
2882
2990
|
${TRY_CLEANUP_USAGE}` };
|
|
2883
2991
|
positional.push(a);
|
|
2884
2992
|
}
|
|
@@ -3045,6 +3153,10 @@ async function runTry(opts) {
|
|
|
3045
3153
|
for (const [k, v] of Object.entries(opts.envOverrides ?? {})) {
|
|
3046
3154
|
if (!(k in trialEnv)) trialEnv[k] = v;
|
|
3047
3155
|
}
|
|
3156
|
+
const overrides = opts.envOverrides ?? {};
|
|
3157
|
+
const ambientOnlyRequired = (server.requiredEnvVars ?? []).filter(
|
|
3158
|
+
(k) => (!overrides[k] || overrides[k] === "") && (supplied[k] ?? "").trim() !== ""
|
|
3159
|
+
);
|
|
3048
3160
|
const entry = buildLaunchEntry({
|
|
3049
3161
|
os,
|
|
3050
3162
|
upstream: {
|
|
@@ -3066,8 +3178,9 @@ async function runTry(opts) {
|
|
|
3066
3178
|
entryName,
|
|
3067
3179
|
createdAt: now
|
|
3068
3180
|
};
|
|
3181
|
+
const clientPreExisted = existsSync3(resolved.absolute);
|
|
3069
3182
|
let existing = {};
|
|
3070
|
-
if (
|
|
3183
|
+
if (clientPreExisted) {
|
|
3071
3184
|
try {
|
|
3072
3185
|
const raw = await readFile6(resolved.absolute, "utf8");
|
|
3073
3186
|
if (raw.trim().length > 0) {
|
|
@@ -3110,6 +3223,12 @@ async function runTry(opts) {
|
|
|
3110
3223
|
try {
|
|
3111
3224
|
await atomicWriteFile(resolved.absolute, clientJson);
|
|
3112
3225
|
written.push(resolved.absolute);
|
|
3226
|
+
if (!clientPreExisted && entry.env && Object.keys(entry.env).length > 0 && process.platform !== "win32") {
|
|
3227
|
+
try {
|
|
3228
|
+
await chmod2(resolved.absolute, 384);
|
|
3229
|
+
} catch {
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3113
3232
|
} catch (e) {
|
|
3114
3233
|
printErr(`yaw-mcp try: failed to write ${resolved.absolute}: ${e.message}`);
|
|
3115
3234
|
await unlink(trialMarkerPath(slug, home)).catch(() => void 0);
|
|
@@ -3122,6 +3241,11 @@ async function runTry(opts) {
|
|
|
3122
3241
|
print(`Trial wired: ${server.name} via yaw-mcp-try-${slug} -> ${resolved.absolute}`);
|
|
3123
3242
|
print(`Expires in ${ttlPretty}; remove sooner with: yaw-mcp try-cleanup ${slug}`);
|
|
3124
3243
|
print(`Liking it? Sign up at ${baseUrl}/signup to keep ${server.name} on every machine.`);
|
|
3244
|
+
if (ambientOnlyRequired.length > 0) {
|
|
3245
|
+
printErr(
|
|
3246
|
+
`Note: ${ambientOnlyRequired.join(", ")} ${ambientOnlyRequired.length === 1 ? "was" : "were"} read from your shell env and written into the trial entry at ${resolved.absolute}. Remove the trial with: yaw-mcp try-cleanup ${slug}`
|
|
3247
|
+
);
|
|
3248
|
+
}
|
|
3125
3249
|
return { exitCode: 0, written, marker };
|
|
3126
3250
|
}
|
|
3127
3251
|
async function runTryCleanup(opts) {
|
|
@@ -3308,7 +3432,7 @@ ${UPGRADE_USAGE}` };
|
|
|
3308
3432
|
function detectInstallMethod(argvPath) {
|
|
3309
3433
|
if (!argvPath) return "unknown";
|
|
3310
3434
|
const normalized = argvPath.replace(/\\/g, "/");
|
|
3311
|
-
if (/\/_npx\//.test(normalized)) return "npx";
|
|
3435
|
+
if (/\/_npx\/[0-9a-f]+\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "npx";
|
|
3312
3436
|
if (/\/app\.asar\.unpacked\//.test(normalized)) return "bundled-app";
|
|
3313
3437
|
if (/\/npm\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
|
|
3314
3438
|
if (/\/lib\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
|
|
@@ -3521,8 +3645,9 @@ async function runUpgrade(opts = {}) {
|
|
|
3521
3645
|
return { exitCode: 0, lines };
|
|
3522
3646
|
}
|
|
3523
3647
|
if (method === "binary") {
|
|
3524
|
-
print("yaw-mcp is running as a standalone binary \u2014
|
|
3525
|
-
print("
|
|
3648
|
+
print("yaw-mcp is running as a standalone binary \u2014 manual upgrade required.");
|
|
3649
|
+
print("There's no package manager to upgrade it, and `--run` can't automate");
|
|
3650
|
+
print("this: download the latest build and replace this executable:");
|
|
3526
3651
|
print("");
|
|
3527
3652
|
print(` ${BINARY_DOWNLOAD_URL}`);
|
|
3528
3653
|
return { exitCode: opts.run ? 2 : 1, lines };
|
|
@@ -3537,7 +3662,7 @@ async function runUpgrade(opts = {}) {
|
|
|
3537
3662
|
if (runSpec) {
|
|
3538
3663
|
print("Run `yaw-mcp upgrade --run` to upgrade in place, or run it yourself:");
|
|
3539
3664
|
} else {
|
|
3540
|
-
print("
|
|
3665
|
+
print("Manual upgrade required (--run can't safely automate this install method). Run it yourself:");
|
|
3541
3666
|
}
|
|
3542
3667
|
print("");
|
|
3543
3668
|
if (installRoot) {
|
|
@@ -3547,7 +3672,9 @@ async function runUpgrade(opts = {}) {
|
|
|
3547
3672
|
return { exitCode: 1, lines };
|
|
3548
3673
|
}
|
|
3549
3674
|
if (!runSpec) {
|
|
3550
|
-
printErr(
|
|
3675
|
+
printErr(
|
|
3676
|
+
`yaw-mcp upgrade --run: a "${method}" install can't be upgraded automatically (manual upgrade required). Run it yourself:`
|
|
3677
|
+
);
|
|
3551
3678
|
printErr("");
|
|
3552
3679
|
printErr(` ${plan.command}`);
|
|
3553
3680
|
return { exitCode: 2, lines };
|
|
@@ -3572,7 +3699,7 @@ async function runUpgrade(opts = {}) {
|
|
|
3572
3699
|
return { exitCode: 3, lines };
|
|
3573
3700
|
}
|
|
3574
3701
|
function readCurrentVersion() {
|
|
3575
|
-
return true ? "0.
|
|
3702
|
+
return true ? "0.63.0" : "dev";
|
|
3576
3703
|
}
|
|
3577
3704
|
|
|
3578
3705
|
// src/usage-hints.ts
|
|
@@ -3634,7 +3761,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
3634
3761
|
}
|
|
3635
3762
|
|
|
3636
3763
|
// src/doctor-cmd.ts
|
|
3637
|
-
var VERSION = true ? "0.
|
|
3764
|
+
var VERSION = true ? "0.63.0" : "dev";
|
|
3638
3765
|
function isPersistenceDisabled(env) {
|
|
3639
3766
|
const raw = env.YAW_MCP_DISABLE_PERSISTENCE;
|
|
3640
3767
|
return raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
|
|
@@ -3814,6 +3941,31 @@ async function runDoctorJson(opts) {
|
|
|
3814
3941
|
}
|
|
3815
3942
|
}
|
|
3816
3943
|
const shellShadows = scanShellHistoryForShadows({ home, env });
|
|
3944
|
+
const trialGc = await gcExpiredTrials({ home, env, postEvent: opts.postTryEvent, now: opts.now }).catch(() => ({
|
|
3945
|
+
cleared: 0,
|
|
3946
|
+
failed: 0
|
|
3947
|
+
}));
|
|
3948
|
+
const trialScan = await scanTrials({ home, now: opts.now });
|
|
3949
|
+
const trials = {
|
|
3950
|
+
cleared: trialGc.cleared,
|
|
3951
|
+
live: trialScan.live.map(({ marker, msUntilExpiry }) => ({
|
|
3952
|
+
slug: marker.slug,
|
|
3953
|
+
clientName: marker.clientName,
|
|
3954
|
+
clientPath: marker.clientPath,
|
|
3955
|
+
msUntilExpiry
|
|
3956
|
+
})),
|
|
3957
|
+
malformed: trialScan.malformed
|
|
3958
|
+
};
|
|
3959
|
+
const analyticsFailure = getLastAnalyticsFailure();
|
|
3960
|
+
const reportFailure = getLastReportFailure();
|
|
3961
|
+
const backgroundPosters = {
|
|
3962
|
+
analytics: analyticsFailure ? {
|
|
3963
|
+
statusCode: analyticsFailure.statusCode,
|
|
3964
|
+
url: analyticsFailure.url,
|
|
3965
|
+
at: new Date(analyticsFailure.at).toISOString()
|
|
3966
|
+
} : null,
|
|
3967
|
+
toolReport: reportFailure ? { statusCode: reportFailure.statusCode, url: reportFailure.url, at: new Date(reportFailure.at).toISOString() } : null
|
|
3968
|
+
};
|
|
3817
3969
|
const skipCheck = (opts.skipRegistryCheck === true || Boolean(process.env.VITEST)) && !opts.registryFetch;
|
|
3818
3970
|
const latest = skipCheck ? null : await fetchLatestVersion(opts.registryFetch);
|
|
3819
3971
|
const effectiveVersion = opts.currentVersion ?? VERSION;
|
|
@@ -3846,6 +3998,8 @@ async function runDoctorJson(opts) {
|
|
|
3846
3998
|
reliability,
|
|
3847
3999
|
clients,
|
|
3848
4000
|
shellShadows,
|
|
4001
|
+
trials,
|
|
4002
|
+
backgroundPosters,
|
|
3849
4003
|
upgrade: { current: effectiveVersion, latest, stale },
|
|
3850
4004
|
diagnosis: { exitCode, summary }
|
|
3851
4005
|
};
|
|
@@ -4284,8 +4438,10 @@ import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
|
4284
4438
|
import { homedir as homedir10 } from "os";
|
|
4285
4439
|
import path3 from "path";
|
|
4286
4440
|
|
|
4287
|
-
// src/foundry
|
|
4288
|
-
import {
|
|
4441
|
+
// src/foundry.ts
|
|
4442
|
+
import { appendFile, mkdir as mkdir3, stat as stat4 } from "fs/promises";
|
|
4443
|
+
import { homedir as homedir9 } from "os";
|
|
4444
|
+
import path2 from "path";
|
|
4289
4445
|
|
|
4290
4446
|
// src/relevance.ts
|
|
4291
4447
|
var K1 = 1.2;
|
|
@@ -4404,7 +4560,64 @@ function rankServers(context, servers) {
|
|
|
4404
4560
|
return results;
|
|
4405
4561
|
}
|
|
4406
4562
|
|
|
4563
|
+
// src/foundry.ts
|
|
4564
|
+
var SECRET_PREFIXES = ["sk_", "sk-", "tok_", "ghp_", "gho_", "xox", "pk_", "akia"];
|
|
4565
|
+
function looksSensitive(token5) {
|
|
4566
|
+
for (const prefix of SECRET_PREFIXES) {
|
|
4567
|
+
if (token5.startsWith(prefix)) return true;
|
|
4568
|
+
}
|
|
4569
|
+
if (token5.length >= 16 && /^[0-9a-f]+$/.test(token5)) return true;
|
|
4570
|
+
if (token5.length >= 12 && /[a-z]/.test(token5) && /[0-9]/.test(token5)) return true;
|
|
4571
|
+
if (token5.length >= 16 && /^[a-z]+$/.test(token5)) return true;
|
|
4572
|
+
return false;
|
|
4573
|
+
}
|
|
4574
|
+
function redactIntent(intent) {
|
|
4575
|
+
const all = tokenize(intent);
|
|
4576
|
+
const tokens = [];
|
|
4577
|
+
let redactedCount = 0;
|
|
4578
|
+
for (const token5 of all) {
|
|
4579
|
+
if (looksSensitive(token5)) {
|
|
4580
|
+
redactedCount++;
|
|
4581
|
+
} else {
|
|
4582
|
+
tokens.push(token5);
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
tokens.sort();
|
|
4586
|
+
return { tokens, redactedCount };
|
|
4587
|
+
}
|
|
4588
|
+
function isFoundryEnabled() {
|
|
4589
|
+
const raw = process.env.YAW_MCP_FOUNDRY;
|
|
4590
|
+
if (!raw) return false;
|
|
4591
|
+
const v = raw.trim().toLowerCase();
|
|
4592
|
+
return v === "1" || v === "true";
|
|
4593
|
+
}
|
|
4594
|
+
var MAX_FOUNDRY_BYTES = 5 * 1024 * 1024;
|
|
4595
|
+
var FOUNDRY_FILENAME = "foundry.jsonl";
|
|
4596
|
+
async function appendFoundryTrace(trace, home = homedir9()) {
|
|
4597
|
+
try {
|
|
4598
|
+
if (!isFoundryEnabled()) return;
|
|
4599
|
+
const dir = userConfigDir(home);
|
|
4600
|
+
const file = path2.join(dir, FOUNDRY_FILENAME);
|
|
4601
|
+
try {
|
|
4602
|
+
const info = await stat4(file);
|
|
4603
|
+
if (info.size >= MAX_FOUNDRY_BYTES) return;
|
|
4604
|
+
} catch {
|
|
4605
|
+
}
|
|
4606
|
+
const line = `${JSON.stringify({
|
|
4607
|
+
tokens: trace.tokens,
|
|
4608
|
+
candidates: trace.candidates,
|
|
4609
|
+
chosen: trace.chosen,
|
|
4610
|
+
redactedCount: trace.redactedCount
|
|
4611
|
+
})}
|
|
4612
|
+
`;
|
|
4613
|
+
await mkdir3(dir, { recursive: true });
|
|
4614
|
+
await appendFile(file, line, "utf8");
|
|
4615
|
+
} catch {
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
|
|
4407
4619
|
// src/foundry-corpus.ts
|
|
4620
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
4408
4621
|
var FOUNDRY_CORPUS_VERSION = 1;
|
|
4409
4622
|
var DEFAULT_CORPUS_CAP = 500;
|
|
4410
4623
|
function parseTraceLines(text) {
|
|
@@ -4485,65 +4698,6 @@ function scoreCorpus(corpus) {
|
|
|
4485
4698
|
};
|
|
4486
4699
|
}
|
|
4487
4700
|
|
|
4488
|
-
// src/foundry.ts
|
|
4489
|
-
import { appendFile, mkdir as mkdir3, stat as stat4 } from "fs/promises";
|
|
4490
|
-
import { homedir as homedir9 } from "os";
|
|
4491
|
-
import path2 from "path";
|
|
4492
|
-
var SECRET_PREFIXES = ["sk_", "sk-", "tok_", "ghp_", "gho_", "xox", "pk_", "akia"];
|
|
4493
|
-
function looksSensitive(token5) {
|
|
4494
|
-
for (const prefix of SECRET_PREFIXES) {
|
|
4495
|
-
if (token5.startsWith(prefix)) return true;
|
|
4496
|
-
}
|
|
4497
|
-
if (token5.length >= 16 && /^[0-9a-f]+$/.test(token5)) return true;
|
|
4498
|
-
if (token5.length >= 12 && /[a-z]/.test(token5) && /[0-9]/.test(token5)) return true;
|
|
4499
|
-
if (token5.length >= 16 && /^[a-z]+$/.test(token5)) return true;
|
|
4500
|
-
return false;
|
|
4501
|
-
}
|
|
4502
|
-
function redactIntent(intent) {
|
|
4503
|
-
const all = tokenize(intent);
|
|
4504
|
-
const tokens = [];
|
|
4505
|
-
let redactedCount = 0;
|
|
4506
|
-
for (const token5 of all) {
|
|
4507
|
-
if (looksSensitive(token5)) {
|
|
4508
|
-
redactedCount++;
|
|
4509
|
-
} else {
|
|
4510
|
-
tokens.push(token5);
|
|
4511
|
-
}
|
|
4512
|
-
}
|
|
4513
|
-
tokens.sort();
|
|
4514
|
-
return { tokens, redactedCount };
|
|
4515
|
-
}
|
|
4516
|
-
function isFoundryEnabled() {
|
|
4517
|
-
const raw = process.env.YAW_MCP_FOUNDRY;
|
|
4518
|
-
if (!raw) return false;
|
|
4519
|
-
const v = raw.trim().toLowerCase();
|
|
4520
|
-
return v === "1" || v === "true";
|
|
4521
|
-
}
|
|
4522
|
-
var MAX_FOUNDRY_BYTES = 5 * 1024 * 1024;
|
|
4523
|
-
var FOUNDRY_FILENAME = "foundry.jsonl";
|
|
4524
|
-
async function appendFoundryTrace(trace, home = homedir9()) {
|
|
4525
|
-
try {
|
|
4526
|
-
if (!isFoundryEnabled()) return;
|
|
4527
|
-
const dir = userConfigDir(home);
|
|
4528
|
-
const file = path2.join(dir, FOUNDRY_FILENAME);
|
|
4529
|
-
try {
|
|
4530
|
-
const info = await stat4(file);
|
|
4531
|
-
if (info.size >= MAX_FOUNDRY_BYTES) return;
|
|
4532
|
-
} catch {
|
|
4533
|
-
}
|
|
4534
|
-
const line = `${JSON.stringify({
|
|
4535
|
-
tokens: trace.tokens,
|
|
4536
|
-
candidates: trace.candidates,
|
|
4537
|
-
chosen: trace.chosen,
|
|
4538
|
-
redactedCount: trace.redactedCount
|
|
4539
|
-
})}
|
|
4540
|
-
`;
|
|
4541
|
-
await mkdir3(dir, { recursive: true });
|
|
4542
|
-
await appendFile(file, line, "utf8");
|
|
4543
|
-
} catch {
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
|
|
4547
4701
|
// src/foundry-cmd.ts
|
|
4548
4702
|
var DEFAULT_OUT = path3.join("src", "tests", "fixtures", "foundry-corpus.json");
|
|
4549
4703
|
var FOUNDRY_USAGE = `Usage: yaw-mcp foundry export [--out <path>] [--cap <n>] [--json]
|
|
@@ -4676,60 +4830,6 @@ async function runFoundryExport(opts) {
|
|
|
4676
4830
|
return { exitCode: 0, lines };
|
|
4677
4831
|
}
|
|
4678
4832
|
|
|
4679
|
-
// src/fuzzy.ts
|
|
4680
|
-
function levenshtein(a, b) {
|
|
4681
|
-
if (a === b) return 0;
|
|
4682
|
-
const aLen = a.length;
|
|
4683
|
-
const bLen = b.length;
|
|
4684
|
-
if (aLen === 0) return bLen;
|
|
4685
|
-
if (bLen === 0) return aLen;
|
|
4686
|
-
let prev = new Array(bLen + 1);
|
|
4687
|
-
let curr = new Array(bLen + 1);
|
|
4688
|
-
for (let j = 0; j <= bLen; j++) prev[j] = j;
|
|
4689
|
-
for (let i = 1; i <= aLen; i++) {
|
|
4690
|
-
curr[0] = i;
|
|
4691
|
-
for (let j = 1; j <= bLen; j++) {
|
|
4692
|
-
const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
|
|
4693
|
-
curr[j] = Math.min(
|
|
4694
|
-
curr[j - 1] + 1,
|
|
4695
|
-
// insertion
|
|
4696
|
-
prev[j] + 1,
|
|
4697
|
-
// deletion
|
|
4698
|
-
prev[j - 1] + cost
|
|
4699
|
-
// substitution
|
|
4700
|
-
);
|
|
4701
|
-
}
|
|
4702
|
-
[prev, curr] = [curr, prev];
|
|
4703
|
-
}
|
|
4704
|
-
return prev[bLen];
|
|
4705
|
-
}
|
|
4706
|
-
function closestNames(query, candidates, limit) {
|
|
4707
|
-
if (limit <= 0) return [];
|
|
4708
|
-
const q = query.toLowerCase();
|
|
4709
|
-
const scored = [];
|
|
4710
|
-
for (const c of candidates) {
|
|
4711
|
-
if (c === query) continue;
|
|
4712
|
-
const lc = c.toLowerCase();
|
|
4713
|
-
let score = null;
|
|
4714
|
-
if (lc === q) {
|
|
4715
|
-
score = 0;
|
|
4716
|
-
} else if (lc.startsWith(q) || q.startsWith(lc)) {
|
|
4717
|
-
score = 1;
|
|
4718
|
-
} else if (lc.includes(q) || q.includes(lc)) {
|
|
4719
|
-
score = 2;
|
|
4720
|
-
} else {
|
|
4721
|
-
const d = levenshtein(q, lc);
|
|
4722
|
-
if (d <= 2) score = 2 + d;
|
|
4723
|
-
}
|
|
4724
|
-
if (score !== null) scored.push({ name: c, score });
|
|
4725
|
-
}
|
|
4726
|
-
scored.sort((a, b) => {
|
|
4727
|
-
if (a.score !== b.score) return a.score - b.score;
|
|
4728
|
-
return a.name.localeCompare(b.name);
|
|
4729
|
-
});
|
|
4730
|
-
return scored.slice(0, limit).map((s) => s.name);
|
|
4731
|
-
}
|
|
4732
|
-
|
|
4733
4833
|
// src/local-add-cmd.ts
|
|
4734
4834
|
import { homedir as homedir11 } from "os";
|
|
4735
4835
|
var SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
@@ -4747,7 +4847,7 @@ var ADD_USAGE = `Usage: yaw-mcp add <slug> [flags]
|
|
|
4747
4847
|
--json Emit the written entry as JSON (implies success on stdout).
|
|
4748
4848
|
--catalog <url> Override the catalog URL (default the public catalog).`;
|
|
4749
4849
|
function parseEnvFlag(v, bag) {
|
|
4750
|
-
if (!v
|
|
4850
|
+
if (!v?.includes("=")) return "--env requires KEY=value";
|
|
4751
4851
|
const eq = v.indexOf("=");
|
|
4752
4852
|
const key = v.slice(0, eq);
|
|
4753
4853
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) return `--env: invalid KEY "${key}"`;
|
|
@@ -4776,7 +4876,7 @@ function parseAddArgs(argv) {
|
|
|
4776
4876
|
break;
|
|
4777
4877
|
case "--catalog": {
|
|
4778
4878
|
const v = next();
|
|
4779
|
-
if (!v) return { ok: false, error: "--catalog requires a URL" };
|
|
4879
|
+
if (!v || v.startsWith("--")) return { ok: false, error: "--catalog requires a URL" };
|
|
4780
4880
|
opts.catalogUrl = v;
|
|
4781
4881
|
break;
|
|
4782
4882
|
}
|
|
@@ -4828,7 +4928,7 @@ async function runAdd(opts) {
|
|
|
4828
4928
|
}
|
|
4829
4929
|
const namespace = deriveNamespace(server.name);
|
|
4830
4930
|
const supplied = { ...env, ...opts.envOverrides ?? {} };
|
|
4831
|
-
const missing = server.requiredEnvKeys.filter((k) =>
|
|
4931
|
+
const missing = server.requiredEnvKeys.filter((k) => (supplied[k] ?? "").trim() === "");
|
|
4832
4932
|
if (missing.length > 0) {
|
|
4833
4933
|
printErr(`yaw-mcp add: ${server.name} needs the following env var(s) before it can run:`);
|
|
4834
4934
|
for (const k of missing) printErr(` - ${k}`);
|
|
@@ -4840,7 +4940,11 @@ async function runAdd(opts) {
|
|
|
4840
4940
|
}
|
|
4841
4941
|
const entryEnv = {};
|
|
4842
4942
|
for (const k of server.requiredEnvKeys) entryEnv[k] = "";
|
|
4843
|
-
for (const [k, v] of Object.entries(opts.envOverrides ?? {}))
|
|
4943
|
+
for (const [k, v] of Object.entries(opts.envOverrides ?? {})) {
|
|
4944
|
+
const trimmed = v.trim();
|
|
4945
|
+
if (trimmed === "") continue;
|
|
4946
|
+
entryEnv[k] = trimmed;
|
|
4947
|
+
}
|
|
4844
4948
|
const overrides = opts.envOverrides ?? {};
|
|
4845
4949
|
const ambientOnlyRequired = server.requiredEnvKeys.filter(
|
|
4846
4950
|
(k) => (!overrides[k] || overrides[k] === "") && env[k] != null && env[k] !== ""
|
|
@@ -4947,7 +5051,7 @@ async function runRemove(opts) {
|
|
|
4947
5051
|
printErr(`yaw-mcp remove: ${e.message}`);
|
|
4948
5052
|
return { exitCode: 1, written: [] };
|
|
4949
5053
|
}
|
|
4950
|
-
if (!res
|
|
5054
|
+
if (!res?.removed) {
|
|
4951
5055
|
print(`yaw-mcp remove: no server matching "${opts.target}" in ${res?.path ?? "bundles.json"} (nothing to do).`);
|
|
4952
5056
|
const shadow2 = await findShadowingProjectBundles(cwd, home).catch(() => null);
|
|
4953
5057
|
if (shadow2) {
|
|
@@ -5037,9 +5141,11 @@ function parseLoginArgs(argv) {
|
|
|
5037
5141
|
const a = argv[i];
|
|
5038
5142
|
if (a === "--key") {
|
|
5039
5143
|
const v = argv[++i];
|
|
5040
|
-
if (!v
|
|
5144
|
+
if (!v || v.startsWith("-")) {
|
|
5145
|
+
return { ok: false, error: `yaw-mcp login: --key requires a value
|
|
5041
5146
|
|
|
5042
5147
|
${LOGIN_USAGE}` };
|
|
5148
|
+
}
|
|
5043
5149
|
opts.key = v;
|
|
5044
5150
|
} else if (a === "--json") {
|
|
5045
5151
|
opts.json = true;
|
|
@@ -5142,7 +5248,7 @@ async function runLogout(opts = {}, io = {
|
|
|
5142
5248
|
}
|
|
5143
5249
|
|
|
5144
5250
|
// src/reset-learning-cmd.ts
|
|
5145
|
-
import { unlink as unlink2 } from "fs/promises";
|
|
5251
|
+
import { readFile as readFile8, unlink as unlink2 } from "fs/promises";
|
|
5146
5252
|
import { homedir as homedir12 } from "os";
|
|
5147
5253
|
import { join as join9 } from "path";
|
|
5148
5254
|
var RESET_LEARNING_USAGE = `Usage: yaw-mcp reset-learning
|
|
@@ -5154,16 +5260,15 @@ var RESET_LEARNING_USAGE = `Usage: yaw-mcp reset-learning
|
|
|
5154
5260
|
|
|
5155
5261
|
-h, --help Show this help.`;
|
|
5156
5262
|
function parseResetLearningArgs(argv) {
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5263
|
+
if (argv.length === 0) return { kind: "ok", options: {} };
|
|
5264
|
+
const first = argv[0];
|
|
5265
|
+
if (first === "-h" || first === "--help") return { kind: "help" };
|
|
5266
|
+
return {
|
|
5267
|
+
kind: "error",
|
|
5268
|
+
error: `yaw-mcp reset-learning: unknown argument "${first}"
|
|
5162
5269
|
|
|
5163
5270
|
${RESET_LEARNING_USAGE}`
|
|
5164
|
-
|
|
5165
|
-
}
|
|
5166
|
-
return { kind: "ok", options: {} };
|
|
5271
|
+
};
|
|
5167
5272
|
}
|
|
5168
5273
|
async function runResetLearning(opts = {}) {
|
|
5169
5274
|
const home = opts.home ?? homedir12();
|
|
@@ -5191,6 +5296,7 @@ async function runResetLearning(opts = {}) {
|
|
|
5191
5296
|
const persisted = await loadState(filePath);
|
|
5192
5297
|
const learningCount = Object.keys(persisted.learning).length;
|
|
5193
5298
|
const packCount = persisted.packHistory.length;
|
|
5299
|
+
const parsedCleanly = await peekParsedCleanly(filePath);
|
|
5194
5300
|
try {
|
|
5195
5301
|
await unlink2(filePath);
|
|
5196
5302
|
} catch (err) {
|
|
@@ -5203,12 +5309,33 @@ async function runResetLearning(opts = {}) {
|
|
|
5203
5309
|
printErr(`yaw-mcp reset-learning: failed to remove ${filePath}: ${msg}`);
|
|
5204
5310
|
return { exitCode: 1, lines, removed: false, path: filePath };
|
|
5205
5311
|
}
|
|
5312
|
+
if (!parsedCleanly) {
|
|
5313
|
+
print("yaw-mcp reset-learning: cleared persisted state (contents unreadable).");
|
|
5314
|
+
print(` path: ${filePath}`);
|
|
5315
|
+
return { exitCode: 0, lines, removed: true, path: filePath };
|
|
5316
|
+
}
|
|
5206
5317
|
print("yaw-mcp reset-learning: cleared persisted state.");
|
|
5207
5318
|
print(` path: ${filePath}`);
|
|
5208
5319
|
print(` learning entries removed: ${learningCount}`);
|
|
5209
5320
|
print(` pack history entries removed: ${packCount}`);
|
|
5210
5321
|
return { exitCode: 0, lines, removed: true, path: filePath };
|
|
5211
5322
|
}
|
|
5323
|
+
async function peekParsedCleanly(filePath) {
|
|
5324
|
+
let raw;
|
|
5325
|
+
try {
|
|
5326
|
+
raw = await readFile8(filePath, "utf8");
|
|
5327
|
+
} catch (err) {
|
|
5328
|
+
if (isFileNotFound2(err)) return true;
|
|
5329
|
+
return false;
|
|
5330
|
+
}
|
|
5331
|
+
try {
|
|
5332
|
+
const parsed = JSON.parse(raw);
|
|
5333
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
5334
|
+
return parsed.version === STATE_SCHEMA_VERSION;
|
|
5335
|
+
} catch {
|
|
5336
|
+
return false;
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5212
5339
|
function isFileNotFound2(err) {
|
|
5213
5340
|
return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
|
|
5214
5341
|
}
|
|
@@ -5219,7 +5346,7 @@ import { homedir as homedir14 } from "os";
|
|
|
5219
5346
|
|
|
5220
5347
|
// src/secrets-vault.ts
|
|
5221
5348
|
import { existsSync as existsSync5 } from "fs";
|
|
5222
|
-
import { chmod as chmod3, readFile as
|
|
5349
|
+
import { chmod as chmod3, readFile as readFile9 } from "fs/promises";
|
|
5223
5350
|
import { homedir as homedir13 } from "os";
|
|
5224
5351
|
import { join as join10 } from "path";
|
|
5225
5352
|
|
|
@@ -5294,7 +5421,7 @@ async function loadVault(path5) {
|
|
|
5294
5421
|
if (!existsSync5(path5)) return null;
|
|
5295
5422
|
let raw;
|
|
5296
5423
|
try {
|
|
5297
|
-
raw = await
|
|
5424
|
+
raw = await readFile9(path5, "utf8");
|
|
5298
5425
|
} catch (err) {
|
|
5299
5426
|
log("warn", "Failed to read vault", { path: path5, error: err instanceof Error ? err.message : String(err) });
|
|
5300
5427
|
return null;
|
|
@@ -5330,7 +5457,7 @@ function isEncryptedEntry(v) {
|
|
|
5330
5457
|
}
|
|
5331
5458
|
async function saveVault(path5, vault) {
|
|
5332
5459
|
await atomicWriteFile(path5, `${JSON.stringify(vault, null, 2)}
|
|
5333
|
-
|
|
5460
|
+
`, "utf8", 384);
|
|
5334
5461
|
if (process.platform !== "win32") {
|
|
5335
5462
|
try {
|
|
5336
5463
|
await chmod3(path5, 384);
|
|
@@ -5443,6 +5570,10 @@ Actions:
|
|
|
5443
5570
|
line, no echo). Override with --value <v> or
|
|
5444
5571
|
--stdin (raw, multi-line) for scripting.
|
|
5445
5572
|
get <name> Decrypt and print one secret value to stdout.
|
|
5573
|
+
NOTE: this prints the secret in CLEARTEXT (with
|
|
5574
|
+
or without --json). Redirect to a file or pipe
|
|
5575
|
+
to a consumer; avoid running it interactively so
|
|
5576
|
+
the value does not land in terminal scrollback.
|
|
5446
5577
|
list Show vault entry names (values stay encrypted).
|
|
5447
5578
|
remove <name> Delete an entry.
|
|
5448
5579
|
lock Clear the in-process passphrase cache.
|
|
@@ -5487,9 +5618,14 @@ function parseSecretsArgs(argv) {
|
|
|
5487
5618
|
}
|
|
5488
5619
|
if (a === "--value") {
|
|
5489
5620
|
const v = argv[++i];
|
|
5490
|
-
if (v === void 0
|
|
5621
|
+
if (v === void 0 || v.startsWith("-")) {
|
|
5622
|
+
return {
|
|
5623
|
+
ok: false,
|
|
5624
|
+
error: `yaw-mcp secrets: --value requires a value (for a dash-leading value use --stdin)
|
|
5491
5625
|
|
|
5492
|
-
${SECRETS_USAGE}`
|
|
5626
|
+
${SECRETS_USAGE}`
|
|
5627
|
+
};
|
|
5628
|
+
}
|
|
5493
5629
|
opts.value = v;
|
|
5494
5630
|
continue;
|
|
5495
5631
|
}
|
|
@@ -5564,7 +5700,7 @@ function readPassphraseFromTTY(stdin, stdout, prompt = "Vault passphrase: ") {
|
|
|
5564
5700
|
stdin.setEncoding("utf8");
|
|
5565
5701
|
const onData = (chunk) => {
|
|
5566
5702
|
for (const ch of chunk) {
|
|
5567
|
-
if (ch === "\n" || ch === "\r"
|
|
5703
|
+
if (ch === "\n" || ch === "\r") {
|
|
5568
5704
|
stdout.write("\n");
|
|
5569
5705
|
stdin.removeListener("data", onData);
|
|
5570
5706
|
try {
|
|
@@ -5575,6 +5711,17 @@ function readPassphraseFromTTY(stdin, stdout, prompt = "Vault passphrase: ") {
|
|
|
5575
5711
|
resolve7(chunks.join(""));
|
|
5576
5712
|
return;
|
|
5577
5713
|
}
|
|
5714
|
+
if (ch === "") {
|
|
5715
|
+
stdout.write("\n");
|
|
5716
|
+
stdin.removeListener("data", onData);
|
|
5717
|
+
try {
|
|
5718
|
+
stdin.setRawMode?.(wasRaw);
|
|
5719
|
+
} catch {
|
|
5720
|
+
}
|
|
5721
|
+
stdin.pause();
|
|
5722
|
+
resolve7("");
|
|
5723
|
+
return;
|
|
5724
|
+
}
|
|
5578
5725
|
if (ch === "") {
|
|
5579
5726
|
stdout.write("\n");
|
|
5580
5727
|
process.exit(130);
|
|
@@ -5693,6 +5840,14 @@ async function runSecrets(opts, io = {
|
|
|
5693
5840
|
`);
|
|
5694
5841
|
return { exitCode: 1 };
|
|
5695
5842
|
}
|
|
5843
|
+
const outStream = opts.io?.stdout ?? process.stdout;
|
|
5844
|
+
if (outStream.isTTY === true) {
|
|
5845
|
+
const stderr = opts.io?.stderr ?? process.stderr;
|
|
5846
|
+
stderr.write(
|
|
5847
|
+
`yaw-mcp secrets: warning -- printing "${name}" in cleartext to your terminal; it will remain in scrollback.
|
|
5848
|
+
`
|
|
5849
|
+
);
|
|
5850
|
+
}
|
|
5696
5851
|
if (opts.json) io.out(`${JSON.stringify({ ok: true, name, value })}
|
|
5697
5852
|
`);
|
|
5698
5853
|
else io.out(`${value}
|
|
@@ -5811,9 +5966,9 @@ async function runSecretsPull(opts, io) {
|
|
|
5811
5966
|
const remote = await getResource(MCP_SECRETS_RESOURCE, { home, baseUrl: opts.baseUrl });
|
|
5812
5967
|
const remoteEntries = remote.data?.entries;
|
|
5813
5968
|
const remoteHasEntries = remoteEntries !== void 0 && remoteEntries !== null && typeof remoteEntries === "object" && Object.keys(remoteEntries).length > 0;
|
|
5814
|
-
if (!remote.data
|
|
5969
|
+
if (!remote.data?.salt || !remoteHasEntries) {
|
|
5815
5970
|
const msg = "Remote mcp_secrets is empty. Push from this machine to seed it.";
|
|
5816
|
-
if (opts.json) io.out(`${JSON.stringify({ ok: true, empty: true })}
|
|
5971
|
+
if (opts.json) io.out(`${JSON.stringify({ ok: true, empty: true, message: msg })}
|
|
5817
5972
|
`);
|
|
5818
5973
|
else io.out(`${msg}
|
|
5819
5974
|
`);
|
|
@@ -5864,7 +6019,7 @@ async function runSecretsPull(opts, io) {
|
|
|
5864
6019
|
}
|
|
5865
6020
|
|
|
5866
6021
|
// src/server.ts
|
|
5867
|
-
import { readFile as
|
|
6022
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
5868
6023
|
import { homedir as homedir15 } from "os";
|
|
5869
6024
|
import { isAbsolute as isAbsolute2, relative, resolve as resolve6 } from "path";
|
|
5870
6025
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -5930,7 +6085,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
5930
6085
|
async function maybeAutoUpgrade(deps = {}) {
|
|
5931
6086
|
const optOut = process.env.YAW_MCP_AUTO_UPGRADE;
|
|
5932
6087
|
if (optOut === "0" || optOut?.toLowerCase() === "false") return;
|
|
5933
|
-
const current = deps.currentVersion ?? (true ? "0.
|
|
6088
|
+
const current = deps.currentVersion ?? (true ? "0.63.0" : "dev");
|
|
5934
6089
|
if (current === "dev") return;
|
|
5935
6090
|
const method = (deps.isSeaImpl ? await deps.isSeaImpl() : await detectSea()) ? "binary" : detectInstallMethod(deps.argvPath ?? process.argv[1]);
|
|
5936
6091
|
const latest = await (deps.fetchLatestImpl ?? fetchLatestVersion2)();
|
|
@@ -6365,14 +6520,76 @@ function stepBindingKey(step, index) {
|
|
|
6365
6520
|
return typeof step.id === "string" && step.id.length > 0 ? step.id : String(index);
|
|
6366
6521
|
}
|
|
6367
6522
|
|
|
6523
|
+
// src/fuzzy.ts
|
|
6524
|
+
function levenshtein(a, b) {
|
|
6525
|
+
if (a === b) return 0;
|
|
6526
|
+
const aLen = a.length;
|
|
6527
|
+
const bLen = b.length;
|
|
6528
|
+
if (aLen === 0) return bLen;
|
|
6529
|
+
if (bLen === 0) return aLen;
|
|
6530
|
+
let prev = new Array(bLen + 1);
|
|
6531
|
+
let curr = new Array(bLen + 1);
|
|
6532
|
+
for (let j = 0; j <= bLen; j++) prev[j] = j;
|
|
6533
|
+
for (let i = 1; i <= aLen; i++) {
|
|
6534
|
+
curr[0] = i;
|
|
6535
|
+
for (let j = 1; j <= bLen; j++) {
|
|
6536
|
+
const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
|
|
6537
|
+
curr[j] = Math.min(
|
|
6538
|
+
curr[j - 1] + 1,
|
|
6539
|
+
// insertion
|
|
6540
|
+
prev[j] + 1,
|
|
6541
|
+
// deletion
|
|
6542
|
+
prev[j - 1] + cost
|
|
6543
|
+
// substitution
|
|
6544
|
+
);
|
|
6545
|
+
}
|
|
6546
|
+
[prev, curr] = [curr, prev];
|
|
6547
|
+
}
|
|
6548
|
+
return prev[bLen];
|
|
6549
|
+
}
|
|
6550
|
+
function closestNames(query, candidates, limit) {
|
|
6551
|
+
if (limit <= 0) return [];
|
|
6552
|
+
const q = query.toLowerCase();
|
|
6553
|
+
const scored = [];
|
|
6554
|
+
for (const c of candidates) {
|
|
6555
|
+
if (c === query) continue;
|
|
6556
|
+
const lc = c.toLowerCase();
|
|
6557
|
+
let score = null;
|
|
6558
|
+
if (lc === q) {
|
|
6559
|
+
score = 0;
|
|
6560
|
+
} else if (lc.startsWith(q) || q.startsWith(lc)) {
|
|
6561
|
+
score = 1;
|
|
6562
|
+
} else if (
|
|
6563
|
+
// Substring containment is only a credible "typo" signal when the
|
|
6564
|
+
// query is long enough to be specific AND the shorter string covers
|
|
6565
|
+
// at least half the longer one. Without these gates a 1-2 char query
|
|
6566
|
+
// ("ls", "set") substring-matches long commands ("list", "set-active",
|
|
6567
|
+
// "secrets") and surfaces misleading suggestions the header calls
|
|
6568
|
+
// conservative. The Levenshtein tier still catches genuine short typos.
|
|
6569
|
+
q.length >= 3 && (lc.includes(q) || q.includes(lc)) && Math.min(q.length, lc.length) * 2 >= Math.max(q.length, lc.length)
|
|
6570
|
+
) {
|
|
6571
|
+
score = 2;
|
|
6572
|
+
} else {
|
|
6573
|
+
const d = levenshtein(q, lc);
|
|
6574
|
+
if (d <= 2) score = 2 + d;
|
|
6575
|
+
}
|
|
6576
|
+
if (score !== null) scored.push({ name: c, score });
|
|
6577
|
+
}
|
|
6578
|
+
scored.sort((a, b) => {
|
|
6579
|
+
if (a.score !== b.score) return a.score - b.score;
|
|
6580
|
+
return a.name.localeCompare(b.name);
|
|
6581
|
+
});
|
|
6582
|
+
return scored.slice(0, limit).map((s) => s.name);
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6368
6585
|
// src/guide.ts
|
|
6369
|
-
import { readFile as
|
|
6586
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
6370
6587
|
var GUIDE_READ_TIMEOUT_MS = 1e3;
|
|
6371
6588
|
async function readGuide(path5, scope) {
|
|
6372
6589
|
let raw;
|
|
6373
6590
|
try {
|
|
6374
6591
|
raw = await Promise.race([
|
|
6375
|
-
|
|
6592
|
+
readFile10(path5, "utf8"),
|
|
6376
6593
|
new Promise(
|
|
6377
6594
|
(_, reject) => setTimeout(() => reject(new Error("guide read timeout")), GUIDE_READ_TIMEOUT_MS)
|
|
6378
6595
|
)
|
|
@@ -7187,7 +7404,7 @@ var PackDetector = class {
|
|
|
7187
7404
|
loadSnapshot(snapshot) {
|
|
7188
7405
|
const clean = [];
|
|
7189
7406
|
for (const c of snapshot) {
|
|
7190
|
-
if (!c
|
|
7407
|
+
if (!c?.namespace || !c.toolName) continue;
|
|
7191
7408
|
clean.push({ namespace: c.namespace, toolName: c.toolName, at: c.at });
|
|
7192
7409
|
}
|
|
7193
7410
|
if (clean.length > this.maxHistory) {
|
|
@@ -7374,7 +7591,7 @@ async function routeResourceRead(uri, resourceRoutes, activeConnections, builtin
|
|
|
7374
7591
|
return { contents: [{ uri, text: `Unknown resource: ${uri}` }] };
|
|
7375
7592
|
}
|
|
7376
7593
|
const connection = activeConnections.get(route.namespace);
|
|
7377
|
-
if (
|
|
7594
|
+
if (connection?.status !== "connected") {
|
|
7378
7595
|
return { contents: [{ uri, text: `Server "${route.namespace}" is not connected.` }] };
|
|
7379
7596
|
}
|
|
7380
7597
|
try {
|
|
@@ -7392,7 +7609,7 @@ async function routePromptGet(name, args, promptRoutes, activeConnections) {
|
|
|
7392
7609
|
return { messages: [{ role: "user", content: { type: "text", text: `Unknown prompt: ${name}` } }] };
|
|
7393
7610
|
}
|
|
7394
7611
|
const connection = activeConnections.get(route.namespace);
|
|
7395
|
-
if (
|
|
7612
|
+
if (connection?.status !== "connected") {
|
|
7396
7613
|
return {
|
|
7397
7614
|
messages: [{ role: "user", content: { type: "text", text: `Server "${route.namespace}" is not connected.` } }]
|
|
7398
7615
|
};
|
|
@@ -7420,7 +7637,7 @@ async function routeToolCall(toolName, args, toolRoutes, activeConnections) {
|
|
|
7420
7637
|
};
|
|
7421
7638
|
}
|
|
7422
7639
|
const connection = activeConnections.get(route.namespace);
|
|
7423
|
-
if (
|
|
7640
|
+
if (connection?.status !== "connected") {
|
|
7424
7641
|
return {
|
|
7425
7642
|
content: [
|
|
7426
7643
|
{
|
|
@@ -7731,10 +7948,50 @@ async function callLegacyRerank(payload) {
|
|
|
7731
7948
|
}
|
|
7732
7949
|
}
|
|
7733
7950
|
async function readTeamCookie() {
|
|
7734
|
-
const teamSync = await import("./team-sync-
|
|
7951
|
+
const teamSync = await import("./team-sync-OONB72BJ.js");
|
|
7735
7952
|
return teamSync.getCachedCookie();
|
|
7736
7953
|
}
|
|
7737
7954
|
|
|
7955
|
+
// src/reward.ts
|
|
7956
|
+
var ERROR_SHAPED_CATEGORIES = /* @__PURE__ */ new Set([
|
|
7957
|
+
"validation_error",
|
|
7958
|
+
"timeout",
|
|
7959
|
+
"unauthorized",
|
|
7960
|
+
"unknown_tool",
|
|
7961
|
+
"connection_lost",
|
|
7962
|
+
"rate_limited",
|
|
7963
|
+
"not_found"
|
|
7964
|
+
]);
|
|
7965
|
+
function firstTextBlock(result) {
|
|
7966
|
+
const content = result.content;
|
|
7967
|
+
if (!content || content.length === 0) return void 0;
|
|
7968
|
+
for (const block of content) {
|
|
7969
|
+
if (typeof block.text === "string" && block.text.trim().length > 0) return block.text;
|
|
7970
|
+
}
|
|
7971
|
+
return void 0;
|
|
7972
|
+
}
|
|
7973
|
+
function isEmptyBody(result) {
|
|
7974
|
+
const content = result.content;
|
|
7975
|
+
if (!content || content.length === 0) return true;
|
|
7976
|
+
for (const block of content) {
|
|
7977
|
+
if (typeof block.text === "string" && block.text.trim().length > 0) {
|
|
7978
|
+
return false;
|
|
7979
|
+
}
|
|
7980
|
+
}
|
|
7981
|
+
return true;
|
|
7982
|
+
}
|
|
7983
|
+
function computeOutcomeReward(result) {
|
|
7984
|
+
if (result.isError === true) return 0;
|
|
7985
|
+
const text = firstTextBlock(result);
|
|
7986
|
+
if (text !== void 0) {
|
|
7987
|
+
if (ERROR_SHAPED_CATEGORIES.has(classifyError(text))) {
|
|
7988
|
+
return 0.2;
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7991
|
+
if (isEmptyBody(result)) return 0.3;
|
|
7992
|
+
return 1;
|
|
7993
|
+
}
|
|
7994
|
+
|
|
7738
7995
|
// src/reward-grader.ts
|
|
7739
7996
|
function isRewardGraderEnabled() {
|
|
7740
7997
|
const raw = process.env.YAW_MCP_REWARD_GRADER;
|
|
@@ -7836,46 +8093,6 @@ function extractText(content) {
|
|
|
7836
8093
|
return "";
|
|
7837
8094
|
}
|
|
7838
8095
|
|
|
7839
|
-
// src/reward.ts
|
|
7840
|
-
var ERROR_SHAPED_CATEGORIES = /* @__PURE__ */ new Set([
|
|
7841
|
-
"validation_error",
|
|
7842
|
-
"timeout",
|
|
7843
|
-
"unauthorized",
|
|
7844
|
-
"unknown_tool",
|
|
7845
|
-
"connection_lost",
|
|
7846
|
-
"rate_limited",
|
|
7847
|
-
"not_found"
|
|
7848
|
-
]);
|
|
7849
|
-
function firstTextBlock(result) {
|
|
7850
|
-
const content = result.content;
|
|
7851
|
-
if (!content || content.length === 0) return void 0;
|
|
7852
|
-
for (const block of content) {
|
|
7853
|
-
if (typeof block.text === "string" && block.text.trim().length > 0) return block.text;
|
|
7854
|
-
}
|
|
7855
|
-
return void 0;
|
|
7856
|
-
}
|
|
7857
|
-
function isEmptyBody(result) {
|
|
7858
|
-
const content = result.content;
|
|
7859
|
-
if (!content || content.length === 0) return true;
|
|
7860
|
-
for (const block of content) {
|
|
7861
|
-
if (typeof block.text === "string" && block.text.trim().length > 0) {
|
|
7862
|
-
return false;
|
|
7863
|
-
}
|
|
7864
|
-
}
|
|
7865
|
-
return true;
|
|
7866
|
-
}
|
|
7867
|
-
function computeOutcomeReward(result) {
|
|
7868
|
-
if (result.isError === true) return 0;
|
|
7869
|
-
const text = firstTextBlock(result);
|
|
7870
|
-
if (text !== void 0) {
|
|
7871
|
-
if (ERROR_SHAPED_CATEGORIES.has(classifyError(text))) {
|
|
7872
|
-
return 0.2;
|
|
7873
|
-
}
|
|
7874
|
-
}
|
|
7875
|
-
if (isEmptyBody(result)) return 0.3;
|
|
7876
|
-
return 1;
|
|
7877
|
-
}
|
|
7878
|
-
|
|
7879
8096
|
// src/runtime-detect.ts
|
|
7880
8097
|
import { spawn as spawn4 } from "child_process";
|
|
7881
8098
|
import { request as request8 } from "undici";
|
|
@@ -8194,7 +8411,9 @@ async function bestOfNViaSampling(server, intent, candidates, n) {
|
|
|
8194
8411
|
}
|
|
8195
8412
|
if (votes.size === 0) return null;
|
|
8196
8413
|
const order = /* @__PURE__ */ new Map();
|
|
8197
|
-
candidates.forEach((c, i) =>
|
|
8414
|
+
candidates.forEach((c, i) => {
|
|
8415
|
+
order.set(c.namespace, i);
|
|
8416
|
+
});
|
|
8198
8417
|
let winner = null;
|
|
8199
8418
|
let bestVotes = -1;
|
|
8200
8419
|
let bestRank = Number.POSITIVE_INFINITY;
|
|
@@ -8557,7 +8776,7 @@ function categorizeSpawnError(err) {
|
|
|
8557
8776
|
}
|
|
8558
8777
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
8559
8778
|
const client = new Client(
|
|
8560
|
-
{ name: "yaw-mcp", version: true ? "0.
|
|
8779
|
+
{ name: "yaw-mcp", version: true ? "0.63.0" : "dev" },
|
|
8561
8780
|
{ capabilities: {} }
|
|
8562
8781
|
);
|
|
8563
8782
|
let transport;
|
|
@@ -8884,7 +9103,7 @@ var ConnectServer = class _ConnectServer {
|
|
|
8884
9103
|
this.apiUrl = apiUrl5;
|
|
8885
9104
|
this.token = token5;
|
|
8886
9105
|
this.server = new Server(
|
|
8887
|
-
{ name: "yaw-mcp", version: true ? "0.
|
|
9106
|
+
{ name: "yaw-mcp", version: true ? "0.63.0" : "dev" },
|
|
8888
9107
|
{
|
|
8889
9108
|
capabilities: {
|
|
8890
9109
|
tools: { listChanged: true },
|
|
@@ -10484,7 +10703,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
10484
10703
|
let changed = false;
|
|
10485
10704
|
for (const [namespace, connection] of this.connections) {
|
|
10486
10705
|
const newServerConfig = newServersByNs.get(namespace);
|
|
10487
|
-
if (!newServerConfig
|
|
10706
|
+
if (!newServerConfig?.isActive) {
|
|
10488
10707
|
log("info", "Server removed or disabled in config, deactivating", { namespace });
|
|
10489
10708
|
await disconnectFromUpstream(connection);
|
|
10490
10709
|
this.connections.delete(namespace);
|
|
@@ -10582,7 +10801,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
10582
10801
|
isError: true
|
|
10583
10802
|
};
|
|
10584
10803
|
}
|
|
10585
|
-
const raw = await
|
|
10804
|
+
const raw = await readFile11(resolved, "utf-8");
|
|
10586
10805
|
const parsed = JSON.parse(raw);
|
|
10587
10806
|
if (!parsed.mcpServers || typeof parsed.mcpServers !== "object" || Array.isArray(parsed.mcpServers)) {
|
|
10588
10807
|
return {
|
|
@@ -11386,7 +11605,7 @@ import { homedir as homedir16 } from "os";
|
|
|
11386
11605
|
|
|
11387
11606
|
// src/sync-state.ts
|
|
11388
11607
|
import { existsSync as existsSync7 } from "fs";
|
|
11389
|
-
import { mkdir as mkdir4, readFile as
|
|
11608
|
+
import { mkdir as mkdir4, readFile as readFile12 } from "fs/promises";
|
|
11390
11609
|
import { dirname as dirname2, join as join11 } from "path";
|
|
11391
11610
|
var SYNC_STATE_FILENAME = "sync-state.json";
|
|
11392
11611
|
function syncStatePath(home) {
|
|
@@ -11396,7 +11615,7 @@ async function readSyncState(home) {
|
|
|
11396
11615
|
const path5 = syncStatePath(home);
|
|
11397
11616
|
if (!existsSync7(path5)) return {};
|
|
11398
11617
|
try {
|
|
11399
|
-
const raw = await
|
|
11618
|
+
const raw = await readFile12(path5, "utf8");
|
|
11400
11619
|
const parsed = JSON.parse(raw);
|
|
11401
11620
|
if (!parsed || typeof parsed !== "object") return {};
|
|
11402
11621
|
return parsed;
|
|
@@ -11644,7 +11863,11 @@ function formatPlain(events, opts, orderId, total) {
|
|
|
11644
11863
|
lines.push(` ${c.client.padEnd(24)} ${c.total} calls`);
|
|
11645
11864
|
}
|
|
11646
11865
|
lines.push("");
|
|
11647
|
-
|
|
11866
|
+
if (events.length > renderedCount) {
|
|
11867
|
+
lines.push("Recent events (newest first, capped at --limit; By-server / By-AI-client above span the full window):");
|
|
11868
|
+
} else {
|
|
11869
|
+
lines.push("Recent events (newest first):");
|
|
11870
|
+
}
|
|
11648
11871
|
const recent = events.slice(-Math.min(events.length, opts.limit ?? 50)).reverse();
|
|
11649
11872
|
for (const e of recent) {
|
|
11650
11873
|
const when = new Date(e.ts).toISOString().replace("T", " ").slice(0, 19);
|
|
@@ -11715,9 +11938,45 @@ async function runStats(opts, io = {
|
|
|
11715
11938
|
}
|
|
11716
11939
|
}
|
|
11717
11940
|
|
|
11941
|
+
// src/subcommands.ts
|
|
11942
|
+
var FLAG_ALIASES = ["--help", "-h", "--version", "-V"];
|
|
11943
|
+
var KNOWN_SUBCOMMANDS = [
|
|
11944
|
+
"compliance",
|
|
11945
|
+
"audit",
|
|
11946
|
+
"foundry",
|
|
11947
|
+
"install",
|
|
11948
|
+
"add",
|
|
11949
|
+
"remove",
|
|
11950
|
+
"list",
|
|
11951
|
+
"doctor",
|
|
11952
|
+
"reset-learning",
|
|
11953
|
+
"servers",
|
|
11954
|
+
"bundles",
|
|
11955
|
+
"completion",
|
|
11956
|
+
"upgrade",
|
|
11957
|
+
"try",
|
|
11958
|
+
"try-cleanup",
|
|
11959
|
+
"login",
|
|
11960
|
+
"logout",
|
|
11961
|
+
"sync",
|
|
11962
|
+
"stats",
|
|
11963
|
+
"secrets",
|
|
11964
|
+
"set-active",
|
|
11965
|
+
"help",
|
|
11966
|
+
...FLAG_ALIASES
|
|
11967
|
+
];
|
|
11968
|
+
function suggestSubcommand(input, limit = 3) {
|
|
11969
|
+
const visible = KNOWN_SUBCOMMANDS.filter((s) => !s.startsWith("-"));
|
|
11970
|
+
return closestNames(input, visible, limit);
|
|
11971
|
+
}
|
|
11972
|
+
function suggestFlag(input, limit = 2) {
|
|
11973
|
+
if (input.length <= 2) return [];
|
|
11974
|
+
return closestNames(input, FLAG_ALIASES, limit);
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11718
11977
|
// src/sync-cmd.ts
|
|
11719
11978
|
import { existsSync as existsSync8 } from "fs";
|
|
11720
|
-
import { mkdir as mkdir5, readFile as
|
|
11979
|
+
import { mkdir as mkdir5, readFile as readFile13 } from "fs/promises";
|
|
11721
11980
|
import { homedir as homedir18 } from "os";
|
|
11722
11981
|
import { dirname as dirname3, join as join12 } from "path";
|
|
11723
11982
|
var SYNC_USAGE = `Usage: yaw-mcp sync <push|pull|status> [--json]
|
|
@@ -11771,7 +12030,7 @@ function bundlesPath(home) {
|
|
|
11771
12030
|
async function readLocalBundles(home) {
|
|
11772
12031
|
const path5 = bundlesPath(home);
|
|
11773
12032
|
if (!existsSync8(path5)) return { version: 1, servers: [] };
|
|
11774
|
-
const raw = await
|
|
12033
|
+
const raw = await readFile13(path5, "utf8");
|
|
11775
12034
|
let parsed;
|
|
11776
12035
|
try {
|
|
11777
12036
|
parsed = JSON.parse(raw);
|
|
@@ -11989,45 +12248,30 @@ function handleSyncError(err, opts, io) {
|
|
|
11989
12248
|
}
|
|
11990
12249
|
|
|
11991
12250
|
// src/index.ts
|
|
11992
|
-
|
|
11993
|
-
"
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
11999
|
-
|
|
12000
|
-
"doctor",
|
|
12001
|
-
"reset-learning",
|
|
12002
|
-
"servers",
|
|
12003
|
-
"bundles",
|
|
12004
|
-
"completion",
|
|
12005
|
-
"upgrade",
|
|
12006
|
-
"try",
|
|
12007
|
-
"try-cleanup",
|
|
12008
|
-
"login",
|
|
12009
|
-
"logout",
|
|
12010
|
-
"sync",
|
|
12011
|
-
"stats",
|
|
12012
|
-
"secrets",
|
|
12013
|
-
"set-active",
|
|
12014
|
-
"help",
|
|
12015
|
-
"--help",
|
|
12016
|
-
"-h",
|
|
12017
|
-
"--version",
|
|
12018
|
-
"-V"
|
|
12019
|
-
];
|
|
12251
|
+
function dispatch(cmd, p) {
|
|
12252
|
+
p.then((r) => process.exit(typeof r === "number" ? r : r.exitCode)).catch((err) => {
|
|
12253
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12254
|
+
process.stderr.write(`yaw-mcp ${cmd}: ${msg}
|
|
12255
|
+
`);
|
|
12256
|
+
process.exit(1);
|
|
12257
|
+
});
|
|
12258
|
+
}
|
|
12020
12259
|
var subcommand = process.argv[2];
|
|
12021
12260
|
if (subcommand === "compliance") {
|
|
12022
|
-
runComplianceCommand(process.argv.slice(3))
|
|
12261
|
+
dispatch("compliance", runComplianceCommand(process.argv.slice(3)));
|
|
12023
12262
|
} else if (subcommand === "audit") {
|
|
12024
12263
|
const parsed = parseAuditArgs(process.argv.slice(3));
|
|
12025
12264
|
if (!parsed.ok) {
|
|
12265
|
+
if (parsed.help) {
|
|
12266
|
+
process.stdout.write(`${parsed.error}
|
|
12267
|
+
`);
|
|
12268
|
+
process.exit(0);
|
|
12269
|
+
}
|
|
12026
12270
|
process.stderr.write(`${parsed.error}
|
|
12027
12271
|
`);
|
|
12028
12272
|
process.exit(2);
|
|
12029
12273
|
}
|
|
12030
|
-
runAudit(parsed.options)
|
|
12274
|
+
dispatch("audit", runAudit(parsed.options));
|
|
12031
12275
|
} else if (subcommand === "foundry") {
|
|
12032
12276
|
const parsed = parseFoundryArgs(process.argv.slice(3));
|
|
12033
12277
|
if (!parsed.ok) {
|
|
@@ -12036,7 +12280,7 @@ if (subcommand === "compliance") {
|
|
|
12036
12280
|
`);
|
|
12037
12281
|
process.exit(isHelp ? 0 : 2);
|
|
12038
12282
|
}
|
|
12039
|
-
runFoundryExport(parsed.options)
|
|
12283
|
+
dispatch("foundry", runFoundryExport(parsed.options));
|
|
12040
12284
|
} else if (subcommand === "install") {
|
|
12041
12285
|
const parsed = parseInstallArgs(process.argv.slice(3));
|
|
12042
12286
|
if (!parsed.ok) {
|
|
@@ -12050,23 +12294,28 @@ if (subcommand === "compliance") {
|
|
|
12050
12294
|
process.exit(2);
|
|
12051
12295
|
}
|
|
12052
12296
|
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR && process.env.CLAUDE_CONFIG_DIR.length > 0 ? process.env.CLAUDE_CONFIG_DIR : void 0;
|
|
12053
|
-
runInstall({ ...parsed.options, claudeConfigDir })
|
|
12297
|
+
dispatch("install", runInstall({ ...parsed.options, claudeConfigDir }));
|
|
12054
12298
|
} else if (subcommand === "doctor") {
|
|
12055
12299
|
const doctorArgs = process.argv.slice(3);
|
|
12056
12300
|
const doctorJson = doctorArgs.includes("--json");
|
|
12057
|
-
const
|
|
12058
|
-
|
|
12301
|
+
const isHelpArg = (a) => a === "--help" || a === "-h";
|
|
12302
|
+
const doctorUnknowns = doctorArgs.filter((a) => a !== "--json" && !isHelpArg(a));
|
|
12303
|
+
const firstHelpIdx = doctorArgs.findIndex(isHelpArg);
|
|
12304
|
+
const firstUnknownIdx = doctorArgs.findIndex((a) => a !== "--json" && !isHelpArg(a));
|
|
12305
|
+
const helpWins = firstHelpIdx !== -1 && (firstUnknownIdx === -1 || firstHelpIdx < firstUnknownIdx);
|
|
12306
|
+
if (helpWins) {
|
|
12059
12307
|
process.stdout.write(
|
|
12060
12308
|
"Usage: yaw-mcp doctor [--json]\n\n Print a diagnostic of your yaw-mcp setup.\n\n --json Emit machine-readable JSON instead of text.\n"
|
|
12061
12309
|
);
|
|
12062
12310
|
process.exit(0);
|
|
12063
12311
|
}
|
|
12064
|
-
if (
|
|
12065
|
-
|
|
12312
|
+
if (doctorUnknowns.length > 0) {
|
|
12313
|
+
const quoted = doctorUnknowns.map((a) => `"${a}"`).join(", ");
|
|
12314
|
+
process.stderr.write(`yaw-mcp doctor: unknown argument${doctorUnknowns.length > 1 ? "s" : ""} ${quoted}
|
|
12066
12315
|
`);
|
|
12067
12316
|
process.exit(2);
|
|
12068
12317
|
}
|
|
12069
|
-
runDoctor({ json: doctorJson })
|
|
12318
|
+
dispatch("doctor", runDoctor({ json: doctorJson }));
|
|
12070
12319
|
} else if (subcommand === "reset-learning") {
|
|
12071
12320
|
const parsed = parseResetLearningArgs(process.argv.slice(3));
|
|
12072
12321
|
if (parsed.kind === "help") {
|
|
@@ -12079,7 +12328,7 @@ if (subcommand === "compliance") {
|
|
|
12079
12328
|
`);
|
|
12080
12329
|
process.exit(2);
|
|
12081
12330
|
}
|
|
12082
|
-
|
|
12331
|
+
dispatch("reset-learning", runResetLearning());
|
|
12083
12332
|
} else if (subcommand === "servers") {
|
|
12084
12333
|
const parsed = parseServersArgs(process.argv.slice(3));
|
|
12085
12334
|
if (!parsed.ok) {
|
|
@@ -12092,7 +12341,7 @@ if (subcommand === "compliance") {
|
|
|
12092
12341
|
`);
|
|
12093
12342
|
process.exit(2);
|
|
12094
12343
|
}
|
|
12095
|
-
runServersCommand(parsed.options)
|
|
12344
|
+
dispatch("servers", runServersCommand(parsed.options));
|
|
12096
12345
|
} else if (subcommand === "bundles") {
|
|
12097
12346
|
const parsed = parseBundlesArgs(process.argv.slice(3));
|
|
12098
12347
|
if (!parsed.ok) {
|
|
@@ -12105,7 +12354,7 @@ if (subcommand === "compliance") {
|
|
|
12105
12354
|
`);
|
|
12106
12355
|
process.exit(2);
|
|
12107
12356
|
}
|
|
12108
|
-
runBundlesCommand(parsed.options)
|
|
12357
|
+
dispatch("bundles", runBundlesCommand(parsed.options));
|
|
12109
12358
|
} else if (subcommand === "completion") {
|
|
12110
12359
|
const parsed = parseCompletionArgs(process.argv.slice(3));
|
|
12111
12360
|
if (!parsed.ok) {
|
|
@@ -12118,7 +12367,7 @@ if (subcommand === "compliance") {
|
|
|
12118
12367
|
`);
|
|
12119
12368
|
process.exit(2);
|
|
12120
12369
|
}
|
|
12121
|
-
runCompletion(parsed.options)
|
|
12370
|
+
dispatch("completion", runCompletion(parsed.options));
|
|
12122
12371
|
} else if (subcommand === "upgrade") {
|
|
12123
12372
|
const parsed = parseUpgradeArgs(process.argv.slice(3));
|
|
12124
12373
|
if (!parsed.ok) {
|
|
@@ -12131,7 +12380,7 @@ if (subcommand === "compliance") {
|
|
|
12131
12380
|
`);
|
|
12132
12381
|
process.exit(2);
|
|
12133
12382
|
}
|
|
12134
|
-
runUpgrade(parsed.options)
|
|
12383
|
+
dispatch("upgrade", runUpgrade(parsed.options));
|
|
12135
12384
|
} else if (subcommand === "try") {
|
|
12136
12385
|
const parsed = parseTryArgs(process.argv.slice(3));
|
|
12137
12386
|
if (!parsed.ok) {
|
|
@@ -12144,7 +12393,7 @@ if (subcommand === "compliance") {
|
|
|
12144
12393
|
`);
|
|
12145
12394
|
process.exit(2);
|
|
12146
12395
|
}
|
|
12147
|
-
runTry(parsed.options)
|
|
12396
|
+
dispatch("try", runTry(parsed.options));
|
|
12148
12397
|
} else if (subcommand === "try-cleanup") {
|
|
12149
12398
|
const parsed = parseTryCleanupArgs(process.argv.slice(3));
|
|
12150
12399
|
if (!parsed.ok) {
|
|
@@ -12157,7 +12406,7 @@ if (subcommand === "compliance") {
|
|
|
12157
12406
|
`);
|
|
12158
12407
|
process.exit(2);
|
|
12159
12408
|
}
|
|
12160
|
-
runTryCleanup(parsed.options)
|
|
12409
|
+
dispatch("try-cleanup", runTryCleanup(parsed.options));
|
|
12161
12410
|
} else if (subcommand === "add") {
|
|
12162
12411
|
const parsed = parseAddArgs(process.argv.slice(3));
|
|
12163
12412
|
if (!parsed.ok) {
|
|
@@ -12170,7 +12419,7 @@ if (subcommand === "compliance") {
|
|
|
12170
12419
|
`);
|
|
12171
12420
|
process.exit(2);
|
|
12172
12421
|
}
|
|
12173
|
-
runAdd(parsed.options)
|
|
12422
|
+
dispatch("add", runAdd(parsed.options));
|
|
12174
12423
|
} else if (subcommand === "remove") {
|
|
12175
12424
|
const parsed = parseRemoveArgs(process.argv.slice(3));
|
|
12176
12425
|
if (!parsed.ok) {
|
|
@@ -12183,7 +12432,7 @@ if (subcommand === "compliance") {
|
|
|
12183
12432
|
`);
|
|
12184
12433
|
process.exit(2);
|
|
12185
12434
|
}
|
|
12186
|
-
runRemove(parsed.options)
|
|
12435
|
+
dispatch("remove", runRemove(parsed.options));
|
|
12187
12436
|
} else if (subcommand === "list") {
|
|
12188
12437
|
const parsed = parseListArgs(process.argv.slice(3));
|
|
12189
12438
|
if (!parsed.ok) {
|
|
@@ -12196,7 +12445,7 @@ if (subcommand === "compliance") {
|
|
|
12196
12445
|
`);
|
|
12197
12446
|
process.exit(2);
|
|
12198
12447
|
}
|
|
12199
|
-
runList(parsed.options)
|
|
12448
|
+
dispatch("list", runList(parsed.options));
|
|
12200
12449
|
} else if (subcommand === "login") {
|
|
12201
12450
|
const parsed = parseLoginArgs(process.argv.slice(3));
|
|
12202
12451
|
if (!parsed.ok) {
|
|
@@ -12209,7 +12458,7 @@ if (subcommand === "compliance") {
|
|
|
12209
12458
|
`);
|
|
12210
12459
|
process.exit(2);
|
|
12211
12460
|
}
|
|
12212
|
-
runLogin(parsed.options)
|
|
12461
|
+
dispatch("login", runLogin(parsed.options));
|
|
12213
12462
|
} else if (subcommand === "logout") {
|
|
12214
12463
|
const parsed = parseLogoutArgs(process.argv.slice(3));
|
|
12215
12464
|
if (!parsed.ok) {
|
|
@@ -12222,7 +12471,7 @@ if (subcommand === "compliance") {
|
|
|
12222
12471
|
`);
|
|
12223
12472
|
process.exit(2);
|
|
12224
12473
|
}
|
|
12225
|
-
runLogout(parsed.options)
|
|
12474
|
+
dispatch("logout", runLogout(parsed.options));
|
|
12226
12475
|
} else if (subcommand === "sync") {
|
|
12227
12476
|
const parsed = parseSyncArgs(process.argv.slice(3));
|
|
12228
12477
|
if (!parsed.ok) {
|
|
@@ -12235,7 +12484,7 @@ if (subcommand === "compliance") {
|
|
|
12235
12484
|
`);
|
|
12236
12485
|
process.exit(2);
|
|
12237
12486
|
}
|
|
12238
|
-
runSync(parsed.options)
|
|
12487
|
+
dispatch("sync", runSync(parsed.options));
|
|
12239
12488
|
} else if (subcommand === "stats") {
|
|
12240
12489
|
const parsed = parseStatsArgs(process.argv.slice(3));
|
|
12241
12490
|
if (!parsed.ok) {
|
|
@@ -12248,7 +12497,7 @@ if (subcommand === "compliance") {
|
|
|
12248
12497
|
`);
|
|
12249
12498
|
process.exit(2);
|
|
12250
12499
|
}
|
|
12251
|
-
runStats(parsed.options)
|
|
12500
|
+
dispatch("stats", runStats(parsed.options));
|
|
12252
12501
|
} else if (subcommand === "secrets") {
|
|
12253
12502
|
const parsed = parseSecretsArgs(process.argv.slice(3));
|
|
12254
12503
|
if (!parsed.ok) {
|
|
@@ -12261,7 +12510,7 @@ if (subcommand === "compliance") {
|
|
|
12261
12510
|
`);
|
|
12262
12511
|
process.exit(2);
|
|
12263
12512
|
}
|
|
12264
|
-
runSecrets(parsed.options)
|
|
12513
|
+
dispatch("secrets", runSecrets(parsed.options));
|
|
12265
12514
|
} else if (subcommand === "set-active") {
|
|
12266
12515
|
const parsed = parseSetActiveArgs(process.argv.slice(3));
|
|
12267
12516
|
if (!parsed.ok) {
|
|
@@ -12274,7 +12523,7 @@ if (subcommand === "compliance") {
|
|
|
12274
12523
|
`);
|
|
12275
12524
|
process.exit(2);
|
|
12276
12525
|
}
|
|
12277
|
-
runSetActive(parsed.options)
|
|
12526
|
+
dispatch("set-active", runSetActive(parsed.options));
|
|
12278
12527
|
} else if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
12279
12528
|
process.stdout.write(`
|
|
12280
12529
|
yaw-mcp \u2014 one install, every MCP server, managed from the cloud.
|
|
@@ -12391,16 +12640,20 @@ if (subcommand === "compliance") {
|
|
|
12391
12640
|
`);
|
|
12392
12641
|
process.exit(0);
|
|
12393
12642
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
12394
|
-
process.stdout.write(`yaw-mcp ${true ? "0.
|
|
12643
|
+
process.stdout.write(`yaw-mcp ${true ? "0.63.0" : "dev"}
|
|
12395
12644
|
`);
|
|
12396
12645
|
process.exit(0);
|
|
12397
12646
|
} else if (subcommand && !subcommand.startsWith("-")) {
|
|
12398
|
-
const
|
|
12399
|
-
const suggestions = closestNames(subcommand, visible, 3);
|
|
12647
|
+
const suggestions = suggestSubcommand(subcommand);
|
|
12400
12648
|
const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : " Run `yaw-mcp --help` for the list of subcommands.";
|
|
12401
12649
|
process.stderr.write(`yaw-mcp: unknown subcommand "${subcommand}".${hint}
|
|
12402
12650
|
`);
|
|
12403
12651
|
process.exit(2);
|
|
12652
|
+
} else if (subcommand && suggestFlag(subcommand).length > 0) {
|
|
12653
|
+
const suggestions = suggestFlag(subcommand);
|
|
12654
|
+
process.stderr.write(`yaw-mcp: unknown flag "${subcommand}". Did you mean: ${suggestions.join(", ")}?
|
|
12655
|
+
`);
|
|
12656
|
+
process.exit(2);
|
|
12404
12657
|
} else {
|
|
12405
12658
|
runServer();
|
|
12406
12659
|
}
|