@yawlabs/mcp 0.58.0 → 0.58.1
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/README.md +140 -133
- package/dist/chunk-C3WU6HAG.js +381 -0
- package/dist/index.js +359 -634
- package/dist/team-sync-4JF5LBRB.js +31 -0
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CONFIG_DIRNAME,
|
|
4
|
+
TeamSyncAuthError,
|
|
5
|
+
TeamSyncForbiddenError,
|
|
6
|
+
TeamSyncStaleVersionError,
|
|
7
|
+
atomicWriteFile,
|
|
8
|
+
cacheDir,
|
|
9
|
+
findProjectConfigDir,
|
|
10
|
+
getResource,
|
|
11
|
+
getSession,
|
|
12
|
+
guidePath,
|
|
13
|
+
listAnalyticsEvents,
|
|
14
|
+
log,
|
|
15
|
+
postAnalyticsEvent,
|
|
16
|
+
putResource,
|
|
17
|
+
signIn,
|
|
18
|
+
signOut,
|
|
19
|
+
userConfigDir
|
|
20
|
+
} from "./chunk-C3WU6HAG.js";
|
|
2
21
|
|
|
3
22
|
// src/bundles.ts
|
|
4
23
|
var CURATED_BUNDLES = [
|
|
@@ -75,7 +94,7 @@ function topPartialBundles(installedNamespaces, limit) {
|
|
|
75
94
|
|
|
76
95
|
// src/config-loader.ts
|
|
77
96
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
78
|
-
import { homedir
|
|
97
|
+
import { homedir } from "os";
|
|
79
98
|
import { join as join2, resolve } from "path";
|
|
80
99
|
|
|
81
100
|
// src/jsonc.ts
|
|
@@ -130,71 +149,17 @@ function parseJsonc(src) {
|
|
|
130
149
|
return JSON.parse(stripped);
|
|
131
150
|
}
|
|
132
151
|
|
|
133
|
-
// src/logger.ts
|
|
134
|
-
var LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
135
|
-
var minLevel = LOG_LEVELS[process.env.LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
|
|
136
|
-
function log(level, msg, data) {
|
|
137
|
-
if (LOG_LEVELS[level] < minLevel) return;
|
|
138
|
-
const entry = JSON.stringify({ level, msg, ts: (/* @__PURE__ */ new Date()).toISOString(), ...data });
|
|
139
|
-
process.stderr.write(`${entry}
|
|
140
|
-
`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
152
|
// src/migrate.ts
|
|
144
153
|
import { mkdir, rename, stat } from "fs/promises";
|
|
145
154
|
import { dirname, join } from "path";
|
|
146
|
-
|
|
147
|
-
// src/paths.ts
|
|
148
|
-
import { access } from "fs/promises";
|
|
149
|
-
import { homedir } from "os";
|
|
150
|
-
import path from "path";
|
|
151
|
-
function cacheDir() {
|
|
152
|
-
if (process.platform === "win32") {
|
|
153
|
-
const localAppData = process.env.LOCALAPPDATA;
|
|
154
|
-
const base = localAppData && localAppData.length > 0 ? localAppData : path.join(homedir(), "AppData", "Local");
|
|
155
|
-
return path.join(base, "yaw-mcp", "Cache");
|
|
156
|
-
}
|
|
157
|
-
if (process.platform === "darwin") {
|
|
158
|
-
return path.join(homedir(), "Library", "Caches", "yaw-mcp");
|
|
159
|
-
}
|
|
160
|
-
const xdg = process.env.XDG_CACHE_HOME;
|
|
161
|
-
return path.join(xdg && xdg.length > 0 ? xdg : path.join(homedir(), ".cache"), "yaw-mcp");
|
|
162
|
-
}
|
|
163
|
-
var CONFIG_DIRNAME = ".yaw-mcp";
|
|
164
|
-
function userConfigDir(home = homedir()) {
|
|
165
|
-
return path.join(home, CONFIG_DIRNAME);
|
|
166
|
-
}
|
|
167
|
-
async function findProjectConfigDir(start, home = homedir()) {
|
|
168
|
-
const homeResolved = path.resolve(home);
|
|
169
|
-
let dir = path.resolve(start);
|
|
170
|
-
let prev = "";
|
|
171
|
-
while (dir !== prev) {
|
|
172
|
-
if (dir === homeResolved) return null;
|
|
173
|
-
const candidate = path.join(dir, CONFIG_DIRNAME);
|
|
174
|
-
try {
|
|
175
|
-
await access(candidate);
|
|
176
|
-
return candidate;
|
|
177
|
-
} catch {
|
|
178
|
-
}
|
|
179
|
-
prev = dir;
|
|
180
|
-
dir = path.dirname(dir);
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
var GUIDE_FILENAME = "YAW-MCP.md";
|
|
185
|
-
function guidePath(configDir) {
|
|
186
|
-
return path.join(configDir, GUIDE_FILENAME);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// src/migrate.ts
|
|
190
155
|
var LEGACY_GLOBAL_FILENAME = ".yaw-mcp.json";
|
|
191
156
|
var LEGACY_PROJECT_FILENAME = ".yaw-mcp.json";
|
|
192
157
|
var LEGACY_LOCAL_FILENAME = ".yaw-mcp.local.json";
|
|
193
158
|
var NEW_CONFIG_FILENAME = "config.json";
|
|
194
159
|
var NEW_LOCAL_FILENAME = "config.local.json";
|
|
195
|
-
async function exists(
|
|
160
|
+
async function exists(path3) {
|
|
196
161
|
try {
|
|
197
|
-
await stat(
|
|
162
|
+
await stat(path3);
|
|
198
163
|
return true;
|
|
199
164
|
} catch {
|
|
200
165
|
return false;
|
|
@@ -265,10 +230,10 @@ var CONFIG_FILENAME = "config.json";
|
|
|
265
230
|
var LOCAL_CONFIG_FILENAME = "config.local.json";
|
|
266
231
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
267
232
|
var DEFAULT_API_BASE = "https://yaw.sh/mcp";
|
|
268
|
-
async function readConfigAt(
|
|
233
|
+
async function readConfigAt(path3, scope, warnings) {
|
|
269
234
|
let raw;
|
|
270
235
|
try {
|
|
271
|
-
raw = await readFile(
|
|
236
|
+
raw = await readFile(path3, "utf8");
|
|
272
237
|
} catch {
|
|
273
238
|
return null;
|
|
274
239
|
}
|
|
@@ -277,43 +242,43 @@ async function readConfigAt(path5, scope, warnings) {
|
|
|
277
242
|
parsed = parseJsonc(raw);
|
|
278
243
|
} catch (err) {
|
|
279
244
|
const msg = err instanceof Error ? err.message : String(err);
|
|
280
|
-
warnings.push(`${
|
|
281
|
-
log("warn", "Config file is not valid JSON; ignoring", { path:
|
|
245
|
+
warnings.push(`${path3}: invalid JSON (${msg}) \u2014 file ignored`);
|
|
246
|
+
log("warn", "Config file is not valid JSON; ignoring", { path: path3, error: msg });
|
|
282
247
|
return null;
|
|
283
248
|
}
|
|
284
249
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
285
|
-
warnings.push(`${
|
|
250
|
+
warnings.push(`${path3}: root must be a JSON object \u2014 file ignored`);
|
|
286
251
|
return null;
|
|
287
252
|
}
|
|
288
253
|
const obj = parsed;
|
|
289
254
|
const version = typeof obj.version === "number" ? obj.version : void 0;
|
|
290
255
|
if (version !== void 0 && version > CURRENT_SCHEMA_VERSION) {
|
|
291
256
|
warnings.push(
|
|
292
|
-
`${
|
|
257
|
+
`${path3}: schema version ${version} is newer than this yaw-mcp (${CURRENT_SCHEMA_VERSION}); upgrade with \`npm i -g @yawlabs/mcp@latest\`. Loading best-effort.`
|
|
293
258
|
);
|
|
294
259
|
}
|
|
295
|
-
const
|
|
260
|
+
const token5 = typeof obj.token === "string" && obj.token.length > 0 ? obj.token : void 0;
|
|
296
261
|
const apiBase = typeof obj.apiBase === "string" && obj.apiBase.length > 0 ? obj.apiBase : void 0;
|
|
297
262
|
const servers = Array.isArray(obj.servers) ? obj.servers.filter((v) => typeof v === "string") : void 0;
|
|
298
263
|
const blocked = Array.isArray(obj.blocked) ? obj.blocked.filter((v) => typeof v === "string") : void 0;
|
|
299
|
-
if (
|
|
264
|
+
if (token5) {
|
|
300
265
|
if (scope === "project") {
|
|
301
266
|
warnings.push(
|
|
302
|
-
`${
|
|
267
|
+
`${path3}: 'token' should not appear in a project-shared file. Move it to ${CONFIG_DIRNAME}/${LOCAL_CONFIG_FILENAME} (gitignored) or ~/${CONFIG_DIRNAME}/${CONFIG_FILENAME}.`
|
|
303
268
|
);
|
|
304
269
|
}
|
|
305
|
-
await checkPermissions(
|
|
270
|
+
await checkPermissions(path3, warnings);
|
|
306
271
|
}
|
|
307
|
-
return { path:
|
|
272
|
+
return { path: path3, scope, version, token: token5, apiBase, servers, blocked };
|
|
308
273
|
}
|
|
309
|
-
async function checkPermissions(
|
|
274
|
+
async function checkPermissions(path3, warnings) {
|
|
310
275
|
if (process.platform === "win32") return;
|
|
311
276
|
try {
|
|
312
|
-
const st = await stat2(
|
|
277
|
+
const st = await stat2(path3);
|
|
313
278
|
const mode = st.mode & 511;
|
|
314
279
|
if ((mode & 63) !== 0) {
|
|
315
280
|
warnings.push(
|
|
316
|
-
`${
|
|
281
|
+
`${path3}: contains a token but is readable by group/other (mode ${mode.toString(8)}). Run \`chmod 600 ${path3}\` to restrict.`
|
|
317
282
|
);
|
|
318
283
|
}
|
|
319
284
|
} catch {
|
|
@@ -339,7 +304,7 @@ function unionBlocked(files) {
|
|
|
339
304
|
}
|
|
340
305
|
async function loadMcphConfig(opts = {}) {
|
|
341
306
|
const cwd = resolve(opts.cwd ?? process.cwd());
|
|
342
|
-
const home = resolve(opts.home ??
|
|
307
|
+
const home = resolve(opts.home ?? homedir());
|
|
343
308
|
const env = opts.env ?? process.env;
|
|
344
309
|
const warnings = [];
|
|
345
310
|
const loadedFiles = [];
|
|
@@ -361,16 +326,16 @@ async function loadMcphConfig(opts = {}) {
|
|
|
361
326
|
if (project) loadedFiles.push(project);
|
|
362
327
|
const global = await readConfigAt(globalPath, "global", warnings);
|
|
363
328
|
if (global) loadedFiles.push(global);
|
|
364
|
-
let
|
|
329
|
+
let token5 = null;
|
|
365
330
|
let tokenSource = "missing";
|
|
366
331
|
if (typeof env.YAW_MCP_TOKEN === "string" && env.YAW_MCP_TOKEN.length > 0) {
|
|
367
|
-
|
|
332
|
+
token5 = env.YAW_MCP_TOKEN;
|
|
368
333
|
tokenSource = "env";
|
|
369
334
|
} else if (local?.token) {
|
|
370
|
-
|
|
335
|
+
token5 = local.token;
|
|
371
336
|
tokenSource = "local";
|
|
372
337
|
} else if (global?.token) {
|
|
373
|
-
|
|
338
|
+
token5 = global.token;
|
|
374
339
|
tokenSource = "global";
|
|
375
340
|
}
|
|
376
341
|
let apiBase = DEFAULT_API_BASE;
|
|
@@ -389,7 +354,7 @@ async function loadMcphConfig(opts = {}) {
|
|
|
389
354
|
apiBaseSource = "global";
|
|
390
355
|
}
|
|
391
356
|
return {
|
|
392
|
-
token:
|
|
357
|
+
token: token5,
|
|
393
358
|
tokenSource,
|
|
394
359
|
apiBase,
|
|
395
360
|
apiBaseSource,
|
|
@@ -400,10 +365,10 @@ async function loadMcphConfig(opts = {}) {
|
|
|
400
365
|
warnings
|
|
401
366
|
};
|
|
402
367
|
}
|
|
403
|
-
function tokenFingerprint(
|
|
404
|
-
if (!
|
|
405
|
-
if (
|
|
406
|
-
return `${
|
|
368
|
+
function tokenFingerprint(token5) {
|
|
369
|
+
if (!token5) return "(none)";
|
|
370
|
+
if (token5.length <= 8) return `***${token5.slice(-2)}`;
|
|
371
|
+
return `${token5.slice(0, 8)}\u2026${token5.slice(-4)}`;
|
|
407
372
|
}
|
|
408
373
|
function toProfile(config) {
|
|
409
374
|
if (config.servers === void 0 && config.blocked === void 0) return null;
|
|
@@ -442,10 +407,10 @@ function profileAllows(profile, namespace) {
|
|
|
442
407
|
|
|
443
408
|
// src/config.ts
|
|
444
409
|
import { request } from "undici";
|
|
445
|
-
async function fetchConfig(
|
|
446
|
-
const url = `${
|
|
410
|
+
async function fetchConfig(apiUrl5, token5, currentVersion) {
|
|
411
|
+
const url = `${apiUrl5.replace(/\/$/, "")}/api/connect/config`;
|
|
447
412
|
const headers = {
|
|
448
|
-
Authorization: `Bearer ${
|
|
413
|
+
Authorization: `Bearer ${token5}`,
|
|
449
414
|
Accept: "application/json"
|
|
450
415
|
};
|
|
451
416
|
if (currentVersion) {
|
|
@@ -466,7 +431,7 @@ async function fetchConfig(apiUrl6, token6, currentVersion) {
|
|
|
466
431
|
await res.body.text().catch(() => {
|
|
467
432
|
});
|
|
468
433
|
throw new ConfigError(
|
|
469
|
-
`Token rejected (HTTP 401) \u2014 the token ${tokenFingerprint(
|
|
434
|
+
`Token rejected (HTTP 401) \u2014 the token ${tokenFingerprint(token5)} is invalid or revoked.
|
|
470
435
|
Generate a new token at https://yaw.sh/mcp/dashboard/settings/tokens,
|
|
471
436
|
then re-run \`yaw-mcp install <client> --token mcp_pat_...\` or set YAW_MCP_TOKEN.`,
|
|
472
437
|
true
|
|
@@ -476,7 +441,7 @@ async function fetchConfig(apiUrl6, token6, currentVersion) {
|
|
|
476
441
|
await res.body.text().catch(() => {
|
|
477
442
|
});
|
|
478
443
|
throw new ConfigError(
|
|
479
|
-
`Access denied (HTTP 403) \u2014 the token ${tokenFingerprint(
|
|
444
|
+
`Access denied (HTTP 403) \u2014 the token ${tokenFingerprint(token5)} was accepted but lacks permission to read this account's servers.
|
|
480
445
|
The account may be suspended or the token scope reduced \u2014 check
|
|
481
446
|
https://yaw.sh/mcp/dashboard/settings/tokens, or reach support@mcp.hosting.`,
|
|
482
447
|
true
|
|
@@ -905,12 +870,12 @@ async function runComplianceCommand(argv) {
|
|
|
905
870
|
);
|
|
906
871
|
return 1;
|
|
907
872
|
}
|
|
908
|
-
const
|
|
873
|
+
const apiUrl5 = process.env.YAW_MCP_URL ?? "https://yaw.sh/mcp";
|
|
909
874
|
const report = await runTest(args);
|
|
910
875
|
if (!report) return 1;
|
|
911
876
|
printSummary(report);
|
|
912
877
|
if (publish) {
|
|
913
|
-
const result = await publishReport(
|
|
878
|
+
const result = await publishReport(apiUrl5, report);
|
|
914
879
|
if (!result) return 1;
|
|
915
880
|
process.stdout.write(`
|
|
916
881
|
Published: ${result.reportUrl}
|
|
@@ -970,9 +935,9 @@ Target: ${url}
|
|
|
970
935
|
`
|
|
971
936
|
);
|
|
972
937
|
}
|
|
973
|
-
async function publishReport(
|
|
938
|
+
async function publishReport(apiUrl5, report) {
|
|
974
939
|
try {
|
|
975
|
-
const res = await request2(`${
|
|
940
|
+
const res = await request2(`${apiUrl5.replace(/\/$/, "")}/api/compliance/ext`, {
|
|
976
941
|
method: "POST",
|
|
977
942
|
headers: { "Content-Type": "application/json" },
|
|
978
943
|
body: JSON.stringify(report)
|
|
@@ -995,317 +960,13 @@ Publish failed: ${err?.message ?? String(err)}
|
|
|
995
960
|
}
|
|
996
961
|
|
|
997
962
|
// src/doctor-cmd.ts
|
|
998
|
-
import { existsSync as
|
|
999
|
-
import { readFile as
|
|
1000
|
-
import { homedir as
|
|
1001
|
-
import { join as
|
|
963
|
+
import { existsSync as existsSync3, readFileSync, statSync } from "fs";
|
|
964
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
965
|
+
import { homedir as homedir5 } from "os";
|
|
966
|
+
import { join as join6 } from "path";
|
|
1002
967
|
|
|
1003
968
|
// src/analytics.ts
|
|
1004
969
|
import { request as request3 } from "undici";
|
|
1005
|
-
|
|
1006
|
-
// src/team-sync.ts
|
|
1007
|
-
import { existsSync } from "fs";
|
|
1008
|
-
import { chmod, readFile as readFile2, unlink as unlink2 } from "fs/promises";
|
|
1009
|
-
import { homedir as homedir3 } from "os";
|
|
1010
|
-
import { join as join3 } from "path";
|
|
1011
|
-
|
|
1012
|
-
// src/atomic-write.ts
|
|
1013
|
-
import { mkdir as mkdir2, rename as rename2, unlink, writeFile } from "fs/promises";
|
|
1014
|
-
import path2 from "path";
|
|
1015
|
-
async function atomicWriteFile(filePath, contents, encoding = "utf8") {
|
|
1016
|
-
const dir = path2.dirname(filePath);
|
|
1017
|
-
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
1018
|
-
await mkdir2(dir, { recursive: true });
|
|
1019
|
-
try {
|
|
1020
|
-
await writeFile(tmp, contents, encoding);
|
|
1021
|
-
await rename2(tmp, filePath);
|
|
1022
|
-
} catch (err) {
|
|
1023
|
-
await unlink(tmp).catch(() => void 0);
|
|
1024
|
-
throw err;
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// src/team-sync.ts
|
|
1029
|
-
var BASE_URL_DEFAULT = "https://yaw.sh";
|
|
1030
|
-
var COOKIE_NAME = "yaw_team";
|
|
1031
|
-
var REQUEST_TIMEOUT_MS = 15e3;
|
|
1032
|
-
var SESSION_STATE_FILENAME = "team-session.json";
|
|
1033
|
-
var TeamSyncAuthError = class extends Error {
|
|
1034
|
-
constructor(message = "Not signed in.") {
|
|
1035
|
-
super(message);
|
|
1036
|
-
this.name = "TeamSyncAuthError";
|
|
1037
|
-
}
|
|
1038
|
-
};
|
|
1039
|
-
var TeamSyncForbiddenError = class extends Error {
|
|
1040
|
-
constructor(message = "You do not have permission to edit this resource.") {
|
|
1041
|
-
super(message);
|
|
1042
|
-
this.name = "TeamSyncForbiddenError";
|
|
1043
|
-
}
|
|
1044
|
-
};
|
|
1045
|
-
var TeamSyncStaleVersionError = class extends Error {
|
|
1046
|
-
currentVersion;
|
|
1047
|
-
constructor(currentVersion) {
|
|
1048
|
-
super("Your copy is out of date. Pull the latest version and retry.");
|
|
1049
|
-
this.name = "TeamSyncStaleVersionError";
|
|
1050
|
-
this.currentVersion = currentVersion;
|
|
1051
|
-
}
|
|
1052
|
-
};
|
|
1053
|
-
function sessionStatePath(home = homedir3()) {
|
|
1054
|
-
return join3(home, CONFIG_DIRNAME, SESSION_STATE_FILENAME);
|
|
1055
|
-
}
|
|
1056
|
-
function resolveBaseUrl(env = process.env) {
|
|
1057
|
-
const fromEnv = env.YAW_MCP_TEAM_BASE_URL;
|
|
1058
|
-
return fromEnv && fromEnv.length > 0 ? fromEnv.replace(/\/$/, "") : BASE_URL_DEFAULT;
|
|
1059
|
-
}
|
|
1060
|
-
function expMs(session) {
|
|
1061
|
-
const e = session.exp;
|
|
1062
|
-
return e > 0 && e < 1e12 ? e * 1e3 : e;
|
|
1063
|
-
}
|
|
1064
|
-
var cachedState = null;
|
|
1065
|
-
function invalidateState() {
|
|
1066
|
-
cachedState = null;
|
|
1067
|
-
}
|
|
1068
|
-
async function loadStoredState(filePath) {
|
|
1069
|
-
if (cachedState) {
|
|
1070
|
-
const s = cachedState.state;
|
|
1071
|
-
if (s && expMs(s.session) < Date.now()) {
|
|
1072
|
-
cachedState = { state: null };
|
|
1073
|
-
return null;
|
|
1074
|
-
}
|
|
1075
|
-
return s;
|
|
1076
|
-
}
|
|
1077
|
-
let parsed;
|
|
1078
|
-
try {
|
|
1079
|
-
const raw = await readFile2(filePath, "utf8");
|
|
1080
|
-
const obj = JSON.parse(raw);
|
|
1081
|
-
if (!obj || typeof obj !== "object") parsed = null;
|
|
1082
|
-
else if (typeof obj.cookie !== "string" || !obj.cookie) parsed = null;
|
|
1083
|
-
else if (!obj.session || typeof obj.session !== "object") parsed = null;
|
|
1084
|
-
else parsed = obj;
|
|
1085
|
-
} catch {
|
|
1086
|
-
parsed = null;
|
|
1087
|
-
}
|
|
1088
|
-
if (parsed && expMs(parsed.session) < Date.now()) {
|
|
1089
|
-
cachedState = { state: null };
|
|
1090
|
-
return null;
|
|
1091
|
-
}
|
|
1092
|
-
cachedState = { state: parsed };
|
|
1093
|
-
return parsed;
|
|
1094
|
-
}
|
|
1095
|
-
async function saveStoredState(filePath, state) {
|
|
1096
|
-
cachedState = { state };
|
|
1097
|
-
try {
|
|
1098
|
-
await atomicWriteFile(filePath, JSON.stringify(state, null, 2));
|
|
1099
|
-
if (process.platform !== "win32") {
|
|
1100
|
-
try {
|
|
1101
|
-
await chmod(filePath, 384);
|
|
1102
|
-
} catch {
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
} catch (err) {
|
|
1106
|
-
log("warn", "Failed to persist team session", {
|
|
1107
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
async function clearStoredState(filePath) {
|
|
1112
|
-
invalidateState();
|
|
1113
|
-
try {
|
|
1114
|
-
if (existsSync(filePath)) await unlink2(filePath);
|
|
1115
|
-
} catch {
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
function parseOneSetCookie(line) {
|
|
1119
|
-
const semi = line.indexOf(";");
|
|
1120
|
-
const pair = semi >= 0 ? line.slice(0, semi) : line;
|
|
1121
|
-
const eq = pair.indexOf("=");
|
|
1122
|
-
if (eq < 1) return null;
|
|
1123
|
-
return { name: pair.slice(0, eq).trim(), value: pair.slice(eq + 1).trim() };
|
|
1124
|
-
}
|
|
1125
|
-
function parseSetCookie(headers) {
|
|
1126
|
-
const getAll = headers.getSetCookie;
|
|
1127
|
-
if (typeof getAll === "function") {
|
|
1128
|
-
for (const line of getAll.call(headers)) {
|
|
1129
|
-
const parsed2 = parseOneSetCookie(line);
|
|
1130
|
-
if (parsed2 && parsed2.name === COOKIE_NAME) return parsed2.value;
|
|
1131
|
-
}
|
|
1132
|
-
return null;
|
|
1133
|
-
}
|
|
1134
|
-
const headerValue = headers.get("set-cookie");
|
|
1135
|
-
if (!headerValue) return null;
|
|
1136
|
-
const parsed = parseOneSetCookie(headerValue);
|
|
1137
|
-
return parsed && parsed.name === COOKIE_NAME ? parsed.value : null;
|
|
1138
|
-
}
|
|
1139
|
-
async function httpJson(opts) {
|
|
1140
|
-
const baseUrl = opts.baseUrl ?? resolveBaseUrl();
|
|
1141
|
-
const headers = { "Content-Type": "application/json" };
|
|
1142
|
-
if (opts.cookie) headers.Cookie = `${COOKIE_NAME}=${opts.cookie}`;
|
|
1143
|
-
const res = await fetch(`${baseUrl}${opts.path}`, {
|
|
1144
|
-
method: opts.method,
|
|
1145
|
-
headers,
|
|
1146
|
-
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
1147
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
1148
|
-
});
|
|
1149
|
-
let body;
|
|
1150
|
-
try {
|
|
1151
|
-
body = await res.json();
|
|
1152
|
-
} catch {
|
|
1153
|
-
body = {};
|
|
1154
|
-
}
|
|
1155
|
-
const cookie = parseSetCookie(res.headers);
|
|
1156
|
-
return { status: res.status, body, cookie };
|
|
1157
|
-
}
|
|
1158
|
-
async function signIn(key, opts = {}) {
|
|
1159
|
-
const trimmed = key.trim();
|
|
1160
|
-
if (!trimmed) throw new Error("License key is required.");
|
|
1161
|
-
const baseUrl = opts.baseUrl;
|
|
1162
|
-
const post = await httpJson({
|
|
1163
|
-
method: "POST",
|
|
1164
|
-
path: "/api/team/session",
|
|
1165
|
-
body: { key: trimmed },
|
|
1166
|
-
baseUrl
|
|
1167
|
-
});
|
|
1168
|
-
if (post.status !== 200 || !post.cookie || !post.body.email || !post.body.role || !post.body.order_id) {
|
|
1169
|
-
if (post.body.error) log("warn", "team sign-in failed", { status: post.status, error: post.body.error });
|
|
1170
|
-
throw new TeamSyncAuthError("Sign in failed. Check your license key and try again.");
|
|
1171
|
-
}
|
|
1172
|
-
const get = await httpJson({
|
|
1173
|
-
method: "GET",
|
|
1174
|
-
path: "/api/team/session",
|
|
1175
|
-
cookie: post.cookie,
|
|
1176
|
-
baseUrl
|
|
1177
|
-
});
|
|
1178
|
-
if (get.status !== 200 || !get.body.email || !get.body.role || !get.body.order_id || typeof get.body.exp !== "number") {
|
|
1179
|
-
await httpJson({ method: "POST", path: "/api/team/session/logout", cookie: post.cookie, baseUrl }).catch(
|
|
1180
|
-
() => void 0
|
|
1181
|
-
);
|
|
1182
|
-
throw new TeamSyncAuthError("Sign in succeeded but session check failed.");
|
|
1183
|
-
}
|
|
1184
|
-
const session = {
|
|
1185
|
-
email: get.body.email,
|
|
1186
|
-
role: get.body.role,
|
|
1187
|
-
order_id: get.body.order_id,
|
|
1188
|
-
exp: get.body.exp,
|
|
1189
|
-
can_edit: get.body.can_edit
|
|
1190
|
-
};
|
|
1191
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1192
|
-
await saveStoredState(filePath, { cookie: post.cookie, session });
|
|
1193
|
-
return session;
|
|
1194
|
-
}
|
|
1195
|
-
async function signOut(opts = {}) {
|
|
1196
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1197
|
-
const state = await loadStoredState(filePath);
|
|
1198
|
-
if (state) {
|
|
1199
|
-
try {
|
|
1200
|
-
await httpJson({
|
|
1201
|
-
method: "POST",
|
|
1202
|
-
path: "/api/team/session/logout",
|
|
1203
|
-
cookie: state.cookie,
|
|
1204
|
-
baseUrl: opts.baseUrl
|
|
1205
|
-
});
|
|
1206
|
-
} catch {
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
await clearStoredState(filePath);
|
|
1210
|
-
}
|
|
1211
|
-
async function getSession(opts = {}) {
|
|
1212
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1213
|
-
const state = await loadStoredState(filePath);
|
|
1214
|
-
return state?.session ?? null;
|
|
1215
|
-
}
|
|
1216
|
-
async function getResource(name, opts = {}) {
|
|
1217
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1218
|
-
const state = await loadStoredState(filePath);
|
|
1219
|
-
if (!state) throw new TeamSyncAuthError();
|
|
1220
|
-
const res = await httpJson({
|
|
1221
|
-
method: "GET",
|
|
1222
|
-
path: `/api/team/resource/${encodeURIComponent(name)}`,
|
|
1223
|
-
cookie: state.cookie,
|
|
1224
|
-
baseUrl: opts.baseUrl
|
|
1225
|
-
});
|
|
1226
|
-
if (res.status === 401) {
|
|
1227
|
-
await clearStoredState(filePath);
|
|
1228
|
-
throw new TeamSyncAuthError();
|
|
1229
|
-
}
|
|
1230
|
-
if (res.status === 403) throw new TeamSyncForbiddenError();
|
|
1231
|
-
if (res.status !== 200) {
|
|
1232
|
-
if (res.body.error) log("warn", "team fetch failed", { name, status: res.status, error: res.body.error });
|
|
1233
|
-
throw new Error(`Team fetch failed (${res.status}).`);
|
|
1234
|
-
}
|
|
1235
|
-
return {
|
|
1236
|
-
version: res.body.version,
|
|
1237
|
-
data: res.body.data,
|
|
1238
|
-
updated_at: res.body.updated_at,
|
|
1239
|
-
updated_by: res.body.updated_by
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
async function putResource(name, version, data, opts = {}) {
|
|
1243
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1244
|
-
const state = await loadStoredState(filePath);
|
|
1245
|
-
if (!state) throw new TeamSyncAuthError();
|
|
1246
|
-
const res = await httpJson({
|
|
1247
|
-
method: "PUT",
|
|
1248
|
-
path: `/api/team/resource/${encodeURIComponent(name)}`,
|
|
1249
|
-
body: { version, data },
|
|
1250
|
-
cookie: state.cookie,
|
|
1251
|
-
baseUrl: opts.baseUrl
|
|
1252
|
-
});
|
|
1253
|
-
if (res.status === 401) {
|
|
1254
|
-
await clearStoredState(filePath);
|
|
1255
|
-
throw new TeamSyncAuthError();
|
|
1256
|
-
}
|
|
1257
|
-
if (res.status === 403) throw new TeamSyncForbiddenError();
|
|
1258
|
-
if (res.status === 409) throw new TeamSyncStaleVersionError(res.body.current_version ?? 0);
|
|
1259
|
-
if (res.status !== 200) {
|
|
1260
|
-
if (res.body.error) log("warn", "team write failed", { name, status: res.status, error: res.body.error });
|
|
1261
|
-
throw new Error(`Team write failed (${res.status}).`);
|
|
1262
|
-
}
|
|
1263
|
-
return {
|
|
1264
|
-
version: res.body.version,
|
|
1265
|
-
data: res.body.data,
|
|
1266
|
-
updated_at: res.body.updated_at,
|
|
1267
|
-
updated_by: res.body.updated_by
|
|
1268
|
-
};
|
|
1269
|
-
}
|
|
1270
|
-
async function postAnalyticsEvent(event, opts = {}) {
|
|
1271
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1272
|
-
const state = await loadStoredState(filePath);
|
|
1273
|
-
if (!state) return { ok: false };
|
|
1274
|
-
const res = await httpJson({
|
|
1275
|
-
method: "POST",
|
|
1276
|
-
path: "/api/team/analytics/event",
|
|
1277
|
-
body: event,
|
|
1278
|
-
cookie: state.cookie,
|
|
1279
|
-
baseUrl: opts.baseUrl
|
|
1280
|
-
});
|
|
1281
|
-
if (res.status === 401) {
|
|
1282
|
-
await clearStoredState(filePath);
|
|
1283
|
-
return { ok: false };
|
|
1284
|
-
}
|
|
1285
|
-
return { ok: res.status === 200 };
|
|
1286
|
-
}
|
|
1287
|
-
async function listAnalyticsEvents(opts = {}) {
|
|
1288
|
-
const filePath = opts.filePath ?? sessionStatePath(opts.home);
|
|
1289
|
-
const state = await loadStoredState(filePath);
|
|
1290
|
-
if (!state) throw new TeamSyncAuthError();
|
|
1291
|
-
const res = await httpJson({
|
|
1292
|
-
method: "GET",
|
|
1293
|
-
path: "/api/team/analytics",
|
|
1294
|
-
cookie: state.cookie,
|
|
1295
|
-
baseUrl: opts.baseUrl
|
|
1296
|
-
});
|
|
1297
|
-
if (res.status === 401) {
|
|
1298
|
-
await clearStoredState(filePath);
|
|
1299
|
-
throw new TeamSyncAuthError();
|
|
1300
|
-
}
|
|
1301
|
-
if (res.status !== 200) {
|
|
1302
|
-
if (res.body.error) log("warn", "team analytics list failed", { status: res.status, error: res.body.error });
|
|
1303
|
-
throw new Error(`Team analytics fetch failed (${res.status}).`);
|
|
1304
|
-
}
|
|
1305
|
-
return { events: res.body.events ?? [], cap: res.body.cap ?? 0, order_id: res.body.order_id ?? "" };
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
// src/analytics.ts
|
|
1309
970
|
var FLUSH_INTERVAL = 3e4;
|
|
1310
971
|
var FLUSH_SIZE = 50;
|
|
1311
972
|
var MAX_BUFFER = 5e3;
|
|
@@ -1609,8 +1270,8 @@ function formatShadowLine(server) {
|
|
|
1609
1270
|
}
|
|
1610
1271
|
|
|
1611
1272
|
// src/install-targets.ts
|
|
1612
|
-
import { homedir as
|
|
1613
|
-
import { join as
|
|
1273
|
+
import { homedir as homedir2 } from "os";
|
|
1274
|
+
import { join as join3 } from "path";
|
|
1614
1275
|
var CURRENT_OS = process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : "linux";
|
|
1615
1276
|
var INSTALL_TARGETS = [
|
|
1616
1277
|
{
|
|
@@ -1691,8 +1352,8 @@ var INSTALL_TARGETS = [
|
|
|
1691
1352
|
}
|
|
1692
1353
|
];
|
|
1693
1354
|
function resolveInstallPath(opts) {
|
|
1694
|
-
const home = opts.home ??
|
|
1695
|
-
const appData = opts.appData ?? process.env.APPDATA ??
|
|
1355
|
+
const home = opts.home ?? homedir2();
|
|
1356
|
+
const appData = opts.appData ?? process.env.APPDATA ?? join3(home, "AppData", "Roaming");
|
|
1696
1357
|
const { clientId, scope, os, projectDir, claudeConfigDir } = opts;
|
|
1697
1358
|
const target = INSTALL_TARGETS.find((t) => t.clientId === clientId);
|
|
1698
1359
|
if (!target) throw new Error(`Unknown client: ${clientId}`);
|
|
@@ -1719,25 +1380,25 @@ function pathFor(client, scope, os, base) {
|
|
|
1719
1380
|
if (client === "claude-code") {
|
|
1720
1381
|
if (scope === "user") {
|
|
1721
1382
|
if (claudeConfigDir) {
|
|
1722
|
-
const absolute =
|
|
1383
|
+
const absolute = join3(claudeConfigDir, ".claude.json");
|
|
1723
1384
|
return { absolute, display: absolute, containerPath: ["mcpServers"] };
|
|
1724
1385
|
}
|
|
1725
1386
|
const display = os === "windows" ? "%USERPROFILE%\\.claude.json" : "~/.claude.json";
|
|
1726
|
-
return { absolute:
|
|
1387
|
+
return { absolute: join3(home, ".claude.json"), display, containerPath: ["mcpServers"] };
|
|
1727
1388
|
}
|
|
1728
1389
|
if (scope === "project") {
|
|
1729
1390
|
return {
|
|
1730
|
-
absolute:
|
|
1391
|
+
absolute: join3(projectDir, ".mcp.json"),
|
|
1731
1392
|
display: joinPath("<project folder>", ".mcp.json"),
|
|
1732
1393
|
containerPath: ["mcpServers"]
|
|
1733
1394
|
};
|
|
1734
1395
|
}
|
|
1735
1396
|
if (claudeConfigDir) {
|
|
1736
|
-
const absolute =
|
|
1397
|
+
const absolute = join3(claudeConfigDir, ".claude.json");
|
|
1737
1398
|
return { absolute, display: absolute, containerPath: ["projects", projectDir, "mcpServers"] };
|
|
1738
1399
|
}
|
|
1739
1400
|
return {
|
|
1740
|
-
absolute:
|
|
1401
|
+
absolute: join3(home, ".claude.json"),
|
|
1741
1402
|
display: os === "windows" ? "%USERPROFILE%\\.claude.json" : "~/.claude.json",
|
|
1742
1403
|
containerPath: ["projects", projectDir, "mcpServers"]
|
|
1743
1404
|
};
|
|
@@ -1745,14 +1406,14 @@ function pathFor(client, scope, os, base) {
|
|
|
1745
1406
|
if (client === "claude-desktop") {
|
|
1746
1407
|
if (os === "macos") {
|
|
1747
1408
|
return {
|
|
1748
|
-
absolute:
|
|
1409
|
+
absolute: join3(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
1749
1410
|
display: "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
1750
1411
|
containerPath: ["mcpServers"]
|
|
1751
1412
|
};
|
|
1752
1413
|
}
|
|
1753
1414
|
if (os === "windows") {
|
|
1754
1415
|
return {
|
|
1755
|
-
absolute:
|
|
1416
|
+
absolute: join3(appData, "Claude", "claude_desktop_config.json"),
|
|
1756
1417
|
display: "%APPDATA%\\Claude\\claude_desktop_config.json",
|
|
1757
1418
|
containerPath: ["mcpServers"]
|
|
1758
1419
|
};
|
|
@@ -1762,17 +1423,17 @@ function pathFor(client, scope, os, base) {
|
|
|
1762
1423
|
if (client === "cursor") {
|
|
1763
1424
|
if (scope === "user") {
|
|
1764
1425
|
const display = os === "windows" ? "%USERPROFILE%\\.cursor\\mcp.json" : "~/.cursor/mcp.json";
|
|
1765
|
-
return { absolute:
|
|
1426
|
+
return { absolute: join3(home, ".cursor", "mcp.json"), display, containerPath: ["mcpServers"] };
|
|
1766
1427
|
}
|
|
1767
1428
|
return {
|
|
1768
|
-
absolute:
|
|
1429
|
+
absolute: join3(projectDir, ".cursor", "mcp.json"),
|
|
1769
1430
|
display: joinPath("<project folder>", ".cursor", "mcp.json"),
|
|
1770
1431
|
containerPath: ["mcpServers"]
|
|
1771
1432
|
};
|
|
1772
1433
|
}
|
|
1773
1434
|
if (client === "vscode") {
|
|
1774
1435
|
return {
|
|
1775
|
-
absolute:
|
|
1436
|
+
absolute: join3(projectDir, ".vscode", "mcp.json"),
|
|
1776
1437
|
display: joinPath("<project folder>", ".vscode", "mcp.json"),
|
|
1777
1438
|
containerPath: ["servers"]
|
|
1778
1439
|
};
|
|
@@ -1801,26 +1462,26 @@ var CLAUDE_CODE_ALLOW_PATTERN = "mcp__mcp_hosting__*";
|
|
|
1801
1462
|
function resolveClaudeCodeSettingsPath(scope, opts) {
|
|
1802
1463
|
const { home, projectDir, claudeConfigDir } = opts;
|
|
1803
1464
|
const cfgDir = claudeConfigDir && claudeConfigDir.length > 0 ? claudeConfigDir : null;
|
|
1804
|
-
if (scope === "user") return cfgDir ?
|
|
1805
|
-
if (scope === "project" && projectDir) return
|
|
1806
|
-
if (scope === "local" && projectDir) return
|
|
1465
|
+
if (scope === "user") return cfgDir ? join3(cfgDir, "settings.json") : join3(home, ".claude", "settings.json");
|
|
1466
|
+
if (scope === "project" && projectDir) return join3(projectDir, ".claude", "settings.json");
|
|
1467
|
+
if (scope === "local" && projectDir) return join3(projectDir, ".claude", "settings.local.json");
|
|
1807
1468
|
return null;
|
|
1808
1469
|
}
|
|
1809
1470
|
|
|
1810
1471
|
// src/persistence.ts
|
|
1811
|
-
import { readFile as
|
|
1812
|
-
import
|
|
1472
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1473
|
+
import path from "path";
|
|
1813
1474
|
var STATE_SCHEMA_VERSION = 1;
|
|
1814
1475
|
var STATE_FILENAME = "state.json";
|
|
1815
1476
|
function statePath(configDir = userConfigDir()) {
|
|
1816
|
-
return
|
|
1477
|
+
return path.join(configDir, STATE_FILENAME);
|
|
1817
1478
|
}
|
|
1818
1479
|
function emptyState() {
|
|
1819
1480
|
return { version: STATE_SCHEMA_VERSION, savedAt: 0, learning: {}, packHistory: [] };
|
|
1820
1481
|
}
|
|
1821
1482
|
async function loadState(filePath = statePath()) {
|
|
1822
1483
|
try {
|
|
1823
|
-
const raw = await
|
|
1484
|
+
const raw = await readFile2(filePath, "utf8");
|
|
1824
1485
|
const parsed = JSON.parse(raw);
|
|
1825
1486
|
if (!parsed || typeof parsed !== "object") return emptyState();
|
|
1826
1487
|
if (parsed.version !== STATE_SCHEMA_VERSION) return emptyState();
|
|
@@ -1931,17 +1592,17 @@ async function reportTools(serverId, tools) {
|
|
|
1931
1592
|
|
|
1932
1593
|
// src/try-cmd.ts
|
|
1933
1594
|
import { createHash } from "crypto";
|
|
1934
|
-
import { existsSync as
|
|
1935
|
-
import { chmod as
|
|
1936
|
-
import { homedir as
|
|
1937
|
-
import { join as
|
|
1595
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1596
|
+
import { chmod as chmod2, mkdir as mkdir2, readFile as readFile4, readdir, unlink } from "fs/promises";
|
|
1597
|
+
import { homedir as homedir4, hostname, userInfo } from "os";
|
|
1598
|
+
import { join as join5, resolve as resolve3 } from "path";
|
|
1938
1599
|
import { request as request5 } from "undici";
|
|
1939
1600
|
|
|
1940
1601
|
// src/install-cmd.ts
|
|
1941
|
-
import { existsSync
|
|
1942
|
-
import { chmod
|
|
1943
|
-
import { homedir as
|
|
1944
|
-
import { join as
|
|
1602
|
+
import { existsSync } from "fs";
|
|
1603
|
+
import { chmod, readFile as readFile3 } from "fs/promises";
|
|
1604
|
+
import { homedir as homedir3 } from "os";
|
|
1605
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
1945
1606
|
import { createInterface } from "readline/promises";
|
|
1946
1607
|
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)";
|
|
1947
1608
|
async function runInstall(opts) {
|
|
@@ -2011,12 +1672,12 @@ ${USAGE}`);
|
|
|
2011
1672
|
}
|
|
2012
1673
|
log2(`Target: ${target.label} (${scope})`);
|
|
2013
1674
|
log2(`File: ${resolved.absolute}`);
|
|
2014
|
-
let
|
|
2015
|
-
if (!
|
|
1675
|
+
let token5 = opts.token ?? null;
|
|
1676
|
+
if (!token5) {
|
|
2016
1677
|
const cfg = await loadMcphConfig({ home: opts.home, cwd: process.cwd(), env: {} });
|
|
2017
|
-
|
|
1678
|
+
token5 = cfg.token;
|
|
2018
1679
|
}
|
|
2019
|
-
if (!
|
|
1680
|
+
if (!token5) {
|
|
2020
1681
|
log2(
|
|
2021
1682
|
"yaw-mcp install: no token resolved -- configuring for local mode (Free).\n Add servers by editing ~/.yaw-mcp/bundles.json, or re-run with --token mcp_pat_... to use a Yaw MCP account."
|
|
2022
1683
|
);
|
|
@@ -2025,10 +1686,10 @@ ${USAGE}`);
|
|
|
2025
1686
|
const containerPath = resolved.containerPath;
|
|
2026
1687
|
let existing = {};
|
|
2027
1688
|
let existingHasEntry = false;
|
|
2028
|
-
if (
|
|
1689
|
+
if (existsSync(resolved.absolute)) {
|
|
2029
1690
|
let raw;
|
|
2030
1691
|
try {
|
|
2031
|
-
raw = await
|
|
1692
|
+
raw = await readFile3(resolved.absolute, "utf8");
|
|
2032
1693
|
} catch (e) {
|
|
2033
1694
|
err(`yaw-mcp install: cannot read ${resolved.absolute}: ${e.message}`);
|
|
2034
1695
|
return { written: [], wouldWrite: [], messages, exitCode: 1 };
|
|
@@ -2082,10 +1743,10 @@ ${USAGE}`);
|
|
|
2082
1743
|
const merged = mergeClientConfig(existing, containerPath, newEntry);
|
|
2083
1744
|
const clientJson = `${JSON.stringify(merged, null, 2)}
|
|
2084
1745
|
`;
|
|
2085
|
-
const writeMcphConfig = !opts.skipMcphConfig &&
|
|
2086
|
-
const home = opts.home ??
|
|
2087
|
-
const mcphConfigPath =
|
|
2088
|
-
const mcphConfigComposed = writeMcphConfig ? await composeMcphConfig(mcphConfigPath,
|
|
1746
|
+
const writeMcphConfig = !opts.skipMcphConfig && token5 !== null;
|
|
1747
|
+
const home = opts.home ?? homedir3();
|
|
1748
|
+
const mcphConfigPath = join4(home, CONFIG_DIRNAME, CONFIG_FILENAME);
|
|
1749
|
+
const mcphConfigComposed = writeMcphConfig ? await composeMcphConfig(mcphConfigPath, token5) : { json: "" };
|
|
2089
1750
|
if ("backupPath" in mcphConfigComposed && mcphConfigComposed.backupPath) {
|
|
2090
1751
|
log2(
|
|
2091
1752
|
`yaw-mcp install: existing ${mcphConfigPath} was malformed; original bytes backed up to ${mcphConfigComposed.backupPath} before overwriting.`
|
|
@@ -2120,7 +1781,7 @@ ${settingsPatch.nextJson}`);
|
|
|
2120
1781
|
await atomicWriteFile(mcphConfigPath, mcphConfigJson);
|
|
2121
1782
|
if (process.platform !== "win32") {
|
|
2122
1783
|
try {
|
|
2123
|
-
await
|
|
1784
|
+
await chmod(mcphConfigPath, 384);
|
|
2124
1785
|
} catch {
|
|
2125
1786
|
}
|
|
2126
1787
|
}
|
|
@@ -2156,34 +1817,34 @@ ${settingsPatch.nextJson}`);
|
|
|
2156
1817
|
return { written, wouldWrite: [], messages, exitCode: 0 };
|
|
2157
1818
|
}
|
|
2158
1819
|
async function prepareClaudeCodeSettingsPatch(opts) {
|
|
2159
|
-
const
|
|
1820
|
+
const path3 = resolveClaudeCodeSettingsPath(opts.scope, {
|
|
2160
1821
|
home: opts.home,
|
|
2161
1822
|
projectDir: opts.projectDir,
|
|
2162
1823
|
os: opts.os,
|
|
2163
1824
|
claudeConfigDir: opts.claudeConfigDir
|
|
2164
1825
|
});
|
|
2165
|
-
if (!
|
|
1826
|
+
if (!path3) return null;
|
|
2166
1827
|
let existing = {};
|
|
2167
|
-
if (
|
|
1828
|
+
if (existsSync(path3)) {
|
|
2168
1829
|
try {
|
|
2169
|
-
const raw = await
|
|
1830
|
+
const raw = await readFile3(path3, "utf8");
|
|
2170
1831
|
if (raw.trim().length > 0) {
|
|
2171
1832
|
const parsed = parseJsonc(raw);
|
|
2172
1833
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2173
1834
|
existing = parsed;
|
|
2174
1835
|
} else {
|
|
2175
|
-
return { path:
|
|
1836
|
+
return { path: path3, nextJson: "", changed: false };
|
|
2176
1837
|
}
|
|
2177
1838
|
}
|
|
2178
1839
|
} catch {
|
|
2179
|
-
return { path:
|
|
1840
|
+
return { path: path3, nextJson: "", changed: false };
|
|
2180
1841
|
}
|
|
2181
1842
|
}
|
|
2182
1843
|
const merged = mergePermissionsAllow(existing, [CLAUDE_CODE_ALLOW_PATTERN]);
|
|
2183
1844
|
const before = JSON.stringify(existing);
|
|
2184
1845
|
const after = JSON.stringify(merged);
|
|
2185
|
-
if (before === after) return { path:
|
|
2186
|
-
return { path:
|
|
1846
|
+
if (before === after) return { path: path3, nextJson: "", changed: false };
|
|
1847
|
+
return { path: path3, nextJson: `${JSON.stringify(merged, null, 2)}
|
|
2187
1848
|
`, changed: true };
|
|
2188
1849
|
}
|
|
2189
1850
|
function mergePermissionsAllow(existing, patterns) {
|
|
@@ -2199,13 +1860,13 @@ function mergePermissionsAllow(existing, patterns) {
|
|
|
2199
1860
|
out.permissions = perms;
|
|
2200
1861
|
return out;
|
|
2201
1862
|
}
|
|
2202
|
-
async function promptCollision(
|
|
1863
|
+
async function promptCollision(path3, io) {
|
|
2203
1864
|
const stdin = io?.stdin ?? process.stdin;
|
|
2204
1865
|
const stdout = io?.stdout ?? process.stdout;
|
|
2205
1866
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
2206
1867
|
try {
|
|
2207
1868
|
const answer = (await rl.question(
|
|
2208
|
-
`${
|
|
1869
|
+
`${path3} already has an "${ENTRY_NAME}" entry.
|
|
2209
1870
|
[o]verwrite, [s]kip, or [a]bort? (default: skip) `
|
|
2210
1871
|
)).trim().toLowerCase();
|
|
2211
1872
|
if (answer.startsWith("o")) return "overwrite";
|
|
@@ -2265,13 +1926,13 @@ function removeFromClientConfig(existing, containerPath, entryName) {
|
|
|
2265
1926
|
parent[leafKey] = container;
|
|
2266
1927
|
return out;
|
|
2267
1928
|
}
|
|
2268
|
-
async function composeMcphConfig(
|
|
1929
|
+
async function composeMcphConfig(path3, token5) {
|
|
2269
1930
|
let existing = {};
|
|
2270
1931
|
let backupPath;
|
|
2271
|
-
if (
|
|
1932
|
+
if (existsSync(path3)) {
|
|
2272
1933
|
let raw = "";
|
|
2273
1934
|
try {
|
|
2274
|
-
raw = await
|
|
1935
|
+
raw = await readFile3(path3, "utf8");
|
|
2275
1936
|
} catch {
|
|
2276
1937
|
raw = "";
|
|
2277
1938
|
}
|
|
@@ -2282,7 +1943,7 @@ async function composeMcphConfig(path5, token6) {
|
|
|
2282
1943
|
existing = parsed;
|
|
2283
1944
|
}
|
|
2284
1945
|
} catch {
|
|
2285
|
-
const candidate = `${
|
|
1946
|
+
const candidate = `${path3}.bak-${Date.now()}`;
|
|
2286
1947
|
try {
|
|
2287
1948
|
await atomicWriteFile(candidate, raw);
|
|
2288
1949
|
backupPath = candidate;
|
|
@@ -2292,7 +1953,7 @@ async function composeMcphConfig(path5, token6) {
|
|
|
2292
1953
|
}
|
|
2293
1954
|
}
|
|
2294
1955
|
const next = { version: CURRENT_SCHEMA_VERSION, ...existing };
|
|
2295
|
-
next.token =
|
|
1956
|
+
next.token = token5;
|
|
2296
1957
|
if (typeof next.version !== "number") next.version = CURRENT_SCHEMA_VERSION;
|
|
2297
1958
|
return { json: `${JSON.stringify(next, null, 2)}
|
|
2298
1959
|
`, backupPath };
|
|
@@ -2382,7 +2043,7 @@ ${USAGE}` };
|
|
|
2382
2043
|
return { ok: true, options: opts };
|
|
2383
2044
|
}
|
|
2384
2045
|
async function runInstallList(opts, log2) {
|
|
2385
|
-
const home = opts.home ??
|
|
2046
|
+
const home = opts.home ?? homedir3();
|
|
2386
2047
|
const cwd = opts.cwd ?? process.cwd();
|
|
2387
2048
|
const os = opts.os ?? CURRENT_OS;
|
|
2388
2049
|
const probes = await probeClientsAsync({ home, os, cwd, claudeConfigDir: opts.claudeConfigDir });
|
|
@@ -2628,14 +2289,14 @@ function parseDurationMs(s) {
|
|
|
2628
2289
|
const factor = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
|
|
2629
2290
|
return n * factor;
|
|
2630
2291
|
}
|
|
2631
|
-
function trialsDir(home =
|
|
2632
|
-
return
|
|
2292
|
+
function trialsDir(home = homedir4()) {
|
|
2293
|
+
return join5(home, CONFIG_DIRNAME, TRIALS_DIRNAME);
|
|
2633
2294
|
}
|
|
2634
|
-
function trialMarkerPath(slug, home =
|
|
2635
|
-
return
|
|
2295
|
+
function trialMarkerPath(slug, home = homedir4()) {
|
|
2296
|
+
return join5(trialsDir(home), `${slug}.json`);
|
|
2636
2297
|
}
|
|
2637
|
-
function anonIdPath(home =
|
|
2638
|
-
return
|
|
2298
|
+
function anonIdPath(home = homedir4()) {
|
|
2299
|
+
return join5(trialsDir(home), ANON_FILENAME);
|
|
2639
2300
|
}
|
|
2640
2301
|
function computeAnonId() {
|
|
2641
2302
|
const h = createHash("sha256");
|
|
@@ -2646,23 +2307,23 @@ function computeAnonId() {
|
|
|
2646
2307
|
}
|
|
2647
2308
|
return h.digest("hex").slice(0, 16);
|
|
2648
2309
|
}
|
|
2649
|
-
async function loadOrCreateAnonId(home =
|
|
2650
|
-
const
|
|
2651
|
-
if (
|
|
2310
|
+
async function loadOrCreateAnonId(home = homedir4()) {
|
|
2311
|
+
const path3 = anonIdPath(home);
|
|
2312
|
+
if (existsSync2(path3)) {
|
|
2652
2313
|
try {
|
|
2653
|
-
const raw = (await
|
|
2314
|
+
const raw = (await readFile4(path3, "utf8")).trim();
|
|
2654
2315
|
if (/^[0-9a-f]{16}$/.test(raw)) return raw;
|
|
2655
2316
|
} catch {
|
|
2656
2317
|
}
|
|
2657
2318
|
}
|
|
2658
2319
|
const id = computeAnonId();
|
|
2659
2320
|
try {
|
|
2660
|
-
await
|
|
2661
|
-
await atomicWriteFile(
|
|
2321
|
+
await mkdir2(trialsDir(home), { recursive: true });
|
|
2322
|
+
await atomicWriteFile(path3, `${id}
|
|
2662
2323
|
`);
|
|
2663
2324
|
if (process.platform !== "win32") {
|
|
2664
2325
|
try {
|
|
2665
|
-
await
|
|
2326
|
+
await chmod2(path3, 384);
|
|
2666
2327
|
} catch {
|
|
2667
2328
|
}
|
|
2668
2329
|
}
|
|
@@ -2758,7 +2419,7 @@ async function runTry(opts) {
|
|
|
2758
2419
|
return { exitCode: 2, written: [] };
|
|
2759
2420
|
}
|
|
2760
2421
|
const env = opts.env ?? process.env;
|
|
2761
|
-
const home = opts.home ??
|
|
2422
|
+
const home = opts.home ?? homedir4();
|
|
2762
2423
|
const cwd = opts.cwd ?? process.cwd();
|
|
2763
2424
|
const os = opts.os ?? CURRENT_OS;
|
|
2764
2425
|
const now = opts.now ? opts.now() : Date.now();
|
|
@@ -2825,9 +2486,9 @@ async function runTry(opts) {
|
|
|
2825
2486
|
createdAt: now
|
|
2826
2487
|
};
|
|
2827
2488
|
let existing = {};
|
|
2828
|
-
if (
|
|
2489
|
+
if (existsSync2(resolved.absolute)) {
|
|
2829
2490
|
try {
|
|
2830
|
-
const raw = await
|
|
2491
|
+
const raw = await readFile4(resolved.absolute, "utf8");
|
|
2831
2492
|
if (raw.trim().length > 0) {
|
|
2832
2493
|
const parsed = parseJsonc(raw);
|
|
2833
2494
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -2858,7 +2519,7 @@ async function runTry(opts) {
|
|
|
2858
2519
|
}
|
|
2859
2520
|
const written = [];
|
|
2860
2521
|
try {
|
|
2861
|
-
await
|
|
2522
|
+
await mkdir2(trialsDir(home), { recursive: true });
|
|
2862
2523
|
await atomicWriteFile(trialMarkerPath(slug, home), markerJson);
|
|
2863
2524
|
written.push(trialMarkerPath(slug, home));
|
|
2864
2525
|
} catch (e) {
|
|
@@ -2870,7 +2531,7 @@ async function runTry(opts) {
|
|
|
2870
2531
|
written.push(resolved.absolute);
|
|
2871
2532
|
} catch (e) {
|
|
2872
2533
|
printErr(`yaw-mcp try: failed to write ${resolved.absolute}: ${e.message}`);
|
|
2873
|
-
await
|
|
2534
|
+
await unlink(trialMarkerPath(slug, home)).catch(() => void 0);
|
|
2874
2535
|
return { exitCode: 1, written: [] };
|
|
2875
2536
|
}
|
|
2876
2537
|
const anonId = await loadOrCreateAnonId(home);
|
|
@@ -2899,16 +2560,16 @@ async function runTryCleanup(opts) {
|
|
|
2899
2560
|
return { exitCode: 2, written: [] };
|
|
2900
2561
|
}
|
|
2901
2562
|
const env = opts.env ?? process.env;
|
|
2902
|
-
const home = opts.home ??
|
|
2563
|
+
const home = opts.home ?? homedir4();
|
|
2903
2564
|
const baseUrl = opts.baseUrl ?? env.YAW_MCP_BASE_URL ?? DEFAULT_BASE_URL;
|
|
2904
2565
|
const markerPath = trialMarkerPath(slug, home);
|
|
2905
|
-
if (!
|
|
2566
|
+
if (!existsSync2(markerPath)) {
|
|
2906
2567
|
print(`yaw-mcp try-cleanup: no trial marker for "${slug}" (nothing to do).`);
|
|
2907
2568
|
return { exitCode: 0, written: [] };
|
|
2908
2569
|
}
|
|
2909
2570
|
let marker;
|
|
2910
2571
|
try {
|
|
2911
|
-
const raw = await
|
|
2572
|
+
const raw = await readFile4(markerPath, "utf8");
|
|
2912
2573
|
const parsed = JSON.parse(raw);
|
|
2913
2574
|
if (!parsed || typeof parsed !== "object" || typeof parsed.entryName !== "string") {
|
|
2914
2575
|
throw new Error("marker is missing required fields");
|
|
@@ -2918,9 +2579,9 @@ async function runTryCleanup(opts) {
|
|
|
2918
2579
|
printErr(`yaw-mcp try-cleanup: marker at ${markerPath} is unreadable (${e.message}).`);
|
|
2919
2580
|
return { exitCode: 1, written: [] };
|
|
2920
2581
|
}
|
|
2921
|
-
if (
|
|
2582
|
+
if (existsSync2(marker.clientPath)) {
|
|
2922
2583
|
try {
|
|
2923
|
-
const raw = await
|
|
2584
|
+
const raw = await readFile4(marker.clientPath, "utf8");
|
|
2924
2585
|
if (raw.trim().length > 0) {
|
|
2925
2586
|
const parsed = parseJsonc(raw);
|
|
2926
2587
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -2943,7 +2604,7 @@ async function runTryCleanup(opts) {
|
|
|
2943
2604
|
}
|
|
2944
2605
|
}
|
|
2945
2606
|
try {
|
|
2946
|
-
await
|
|
2607
|
+
await unlink(markerPath);
|
|
2947
2608
|
} catch (e) {
|
|
2948
2609
|
printErr(`yaw-mcp try-cleanup: couldn't delete marker ${markerPath} (${e.message}).`);
|
|
2949
2610
|
return { exitCode: 1, written: [] };
|
|
@@ -2962,11 +2623,11 @@ function formatTtl(ms) {
|
|
|
2962
2623
|
return `${Math.round(clamped / 864e5)}d`;
|
|
2963
2624
|
}
|
|
2964
2625
|
async function scanTrials(opts = {}) {
|
|
2965
|
-
const home = opts.home ??
|
|
2626
|
+
const home = opts.home ?? homedir4();
|
|
2966
2627
|
const now = opts.now ? opts.now() : Date.now();
|
|
2967
2628
|
const dir = trialsDir(home);
|
|
2968
2629
|
const result = { live: [], expired: [], malformed: [] };
|
|
2969
|
-
if (!
|
|
2630
|
+
if (!existsSync2(dir)) return result;
|
|
2970
2631
|
let entries;
|
|
2971
2632
|
try {
|
|
2972
2633
|
entries = await readdir(dir);
|
|
@@ -2975,12 +2636,12 @@ async function scanTrials(opts = {}) {
|
|
|
2975
2636
|
}
|
|
2976
2637
|
for (const filename of entries) {
|
|
2977
2638
|
if (!filename.endsWith(".json")) continue;
|
|
2978
|
-
const
|
|
2639
|
+
const path3 = join5(dir, filename);
|
|
2979
2640
|
try {
|
|
2980
|
-
const raw = await
|
|
2641
|
+
const raw = await readFile4(path3, "utf8");
|
|
2981
2642
|
const parsed = JSON.parse(raw);
|
|
2982
2643
|
if (!parsed || typeof parsed !== "object" || typeof parsed.slug !== "string" || typeof parsed.expiresAt !== "number" || typeof parsed.clientPath !== "string" || !Array.isArray(parsed.containerPath) || typeof parsed.entryName !== "string") {
|
|
2983
|
-
result.malformed.push(
|
|
2644
|
+
result.malformed.push(path3);
|
|
2984
2645
|
continue;
|
|
2985
2646
|
}
|
|
2986
2647
|
const msUntilExpiry = parsed.expiresAt - now;
|
|
@@ -2989,13 +2650,13 @@ async function scanTrials(opts = {}) {
|
|
|
2989
2650
|
if (expired) result.expired.push(entry);
|
|
2990
2651
|
else result.live.push(entry);
|
|
2991
2652
|
} catch {
|
|
2992
|
-
result.malformed.push(
|
|
2653
|
+
result.malformed.push(path3);
|
|
2993
2654
|
}
|
|
2994
2655
|
}
|
|
2995
2656
|
return result;
|
|
2996
2657
|
}
|
|
2997
2658
|
async function gcExpiredTrials(opts) {
|
|
2998
|
-
const home = opts.home ??
|
|
2659
|
+
const home = opts.home ?? homedir4();
|
|
2999
2660
|
const env = opts.env ?? process.env;
|
|
3000
2661
|
const baseUrl = opts.baseUrl ?? env.YAW_MCP_BASE_URL ?? DEFAULT_BASE_URL;
|
|
3001
2662
|
const postEvent = opts.postEvent ?? defaultPostEvent;
|
|
@@ -3006,8 +2667,8 @@ async function gcExpiredTrials(opts) {
|
|
|
3006
2667
|
let failed = 0;
|
|
3007
2668
|
for (const { marker } of scan.expired) {
|
|
3008
2669
|
try {
|
|
3009
|
-
if (
|
|
3010
|
-
const raw = await
|
|
2670
|
+
if (existsSync2(marker.clientPath)) {
|
|
2671
|
+
const raw = await readFile4(marker.clientPath, "utf8");
|
|
3011
2672
|
if (raw.trim().length > 0) {
|
|
3012
2673
|
const parsed = parseJsonc(raw);
|
|
3013
2674
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -3023,7 +2684,7 @@ async function gcExpiredTrials(opts) {
|
|
|
3023
2684
|
}
|
|
3024
2685
|
}
|
|
3025
2686
|
}
|
|
3026
|
-
await
|
|
2687
|
+
await unlink(trialMarkerPath(marker.slug, home));
|
|
3027
2688
|
postEvent(baseUrl, { slug: marker.slug, action: "expiry-gc", anonId }).catch(() => void 0);
|
|
3028
2689
|
cleared++;
|
|
3029
2690
|
} catch (e) {
|
|
@@ -3093,7 +2754,7 @@ function selectFlakyNamespaces(entries, limit) {
|
|
|
3093
2754
|
}
|
|
3094
2755
|
|
|
3095
2756
|
// src/doctor-cmd.ts
|
|
3096
|
-
var VERSION = true ? "0.58.
|
|
2757
|
+
var VERSION = true ? "0.58.1" : "dev";
|
|
3097
2758
|
async function runDoctor(opts = {}) {
|
|
3098
2759
|
if (opts.json) return runDoctorJson(opts);
|
|
3099
2760
|
const lines = [];
|
|
@@ -3104,7 +2765,7 @@ async function runDoctor(opts = {}) {
|
|
|
3104
2765
|
`);
|
|
3105
2766
|
};
|
|
3106
2767
|
const cwd = opts.cwd ?? process.cwd();
|
|
3107
|
-
const home = opts.home ??
|
|
2768
|
+
const home = opts.home ?? homedir5();
|
|
3108
2769
|
const os = opts.os ?? CURRENT_OS;
|
|
3109
2770
|
const env = opts.env ?? process.env;
|
|
3110
2771
|
print(`yaw-mcp doctor \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
@@ -3192,7 +2853,7 @@ async function runDoctorJson(opts) {
|
|
|
3192
2853
|
const lines = [];
|
|
3193
2854
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
3194
2855
|
const cwd = opts.cwd ?? process.cwd();
|
|
3195
|
-
const home = opts.home ??
|
|
2856
|
+
const home = opts.home ?? homedir5();
|
|
3196
2857
|
const os = opts.os ?? CURRENT_OS;
|
|
3197
2858
|
const env = opts.env ?? process.env;
|
|
3198
2859
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3215,7 +2876,7 @@ async function runDoctorJson(opts) {
|
|
|
3215
2876
|
const persistRaw = env.YAW_MCP_DISABLE_PERSISTENCE;
|
|
3216
2877
|
const persistDisabled = persistRaw !== void 0 && persistRaw !== "" && (persistRaw === "1" || persistRaw.toLowerCase() === "true");
|
|
3217
2878
|
const state = persistDisabled ? { disabled: true, path: null, savedAt: null, learningEntries: null, packHistoryEntries: null } : await (async () => {
|
|
3218
|
-
const filePath =
|
|
2879
|
+
const filePath = join6(userConfigDir(home), STATE_FILENAME);
|
|
3219
2880
|
const persisted = await loadState(filePath);
|
|
3220
2881
|
const fresh = persisted.savedAt === 0;
|
|
3221
2882
|
return {
|
|
@@ -3228,7 +2889,7 @@ async function runDoctorJson(opts) {
|
|
|
3228
2889
|
})();
|
|
3229
2890
|
const reliability = [];
|
|
3230
2891
|
if (!persistDisabled) {
|
|
3231
|
-
const filePath =
|
|
2892
|
+
const filePath = join6(userConfigDir(home), STATE_FILENAME);
|
|
3232
2893
|
const persisted = await loadState(filePath);
|
|
3233
2894
|
if (persisted.savedAt !== 0) {
|
|
3234
2895
|
const entries = Object.entries(persisted.learning).map(([namespace, usage]) => ({ namespace, usage }));
|
|
@@ -3314,7 +2975,7 @@ async function renderStateSection(opts) {
|
|
|
3314
2975
|
print("");
|
|
3315
2976
|
return;
|
|
3316
2977
|
}
|
|
3317
|
-
const filePath =
|
|
2978
|
+
const filePath = join6(userConfigDir(home), STATE_FILENAME);
|
|
3318
2979
|
print(` path: ${filePath}`);
|
|
3319
2980
|
const peek = await peekStateFile(filePath);
|
|
3320
2981
|
if (peek.kind === "malformed") {
|
|
@@ -3348,7 +3009,7 @@ async function renderStateSection(opts) {
|
|
|
3348
3009
|
async function peekStateFile(filePath) {
|
|
3349
3010
|
let raw;
|
|
3350
3011
|
try {
|
|
3351
|
-
raw = await
|
|
3012
|
+
raw = await readFile5(filePath, "utf8");
|
|
3352
3013
|
} catch (err) {
|
|
3353
3014
|
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
3354
3015
|
return { kind: "missing" };
|
|
@@ -3373,7 +3034,7 @@ async function renderReliabilitySection(opts) {
|
|
|
3373
3034
|
const raw = env.YAW_MCP_DISABLE_PERSISTENCE;
|
|
3374
3035
|
const disabled = raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
|
|
3375
3036
|
if (disabled) return;
|
|
3376
|
-
const filePath =
|
|
3037
|
+
const filePath = join6(userConfigDir(home), STATE_FILENAME);
|
|
3377
3038
|
const persisted = await loadState(filePath);
|
|
3378
3039
|
if (persisted.savedAt === 0) return;
|
|
3379
3040
|
const entries = Object.entries(persisted.learning).map(([namespace, usage]) => ({ namespace, usage }));
|
|
@@ -3400,8 +3061,8 @@ async function renderTrialsSection(opts) {
|
|
|
3400
3061
|
for (const { marker, msUntilExpiry } of scan.live) {
|
|
3401
3062
|
print(` ${marker.slug} -> ${marker.clientName} (${marker.clientPath}) \u2014 expires in ${formatTtl(msUntilExpiry)}`);
|
|
3402
3063
|
}
|
|
3403
|
-
for (const
|
|
3404
|
-
print(` ! malformed marker at ${
|
|
3064
|
+
for (const path3 of scan.malformed) {
|
|
3065
|
+
print(` ! malformed marker at ${path3} (delete by hand)`);
|
|
3405
3066
|
}
|
|
3406
3067
|
print("");
|
|
3407
3068
|
}
|
|
@@ -3464,7 +3125,7 @@ function probeClients(opts) {
|
|
|
3464
3125
|
} catch {
|
|
3465
3126
|
continue;
|
|
3466
3127
|
}
|
|
3467
|
-
const exists3 =
|
|
3128
|
+
const exists3 = existsSync3(resolved.absolute);
|
|
3468
3129
|
let hasMcphEntry = false;
|
|
3469
3130
|
let malformed = false;
|
|
3470
3131
|
if (exists3) {
|
|
@@ -3497,9 +3158,9 @@ function probeClients(opts) {
|
|
|
3497
3158
|
}
|
|
3498
3159
|
return out;
|
|
3499
3160
|
}
|
|
3500
|
-
function walkContainer(root,
|
|
3161
|
+
function walkContainer(root, path3) {
|
|
3501
3162
|
let cur = root;
|
|
3502
|
-
for (const key of
|
|
3163
|
+
for (const key of path3) {
|
|
3503
3164
|
if (typeof cur !== "object" || cur === null || Array.isArray(cur)) return null;
|
|
3504
3165
|
cur = cur[key];
|
|
3505
3166
|
}
|
|
@@ -3531,12 +3192,12 @@ async function probeClientsAsync(opts) {
|
|
|
3531
3192
|
projectDir: scope.requiresProjectDir ? opts.cwd : void 0,
|
|
3532
3193
|
claudeConfigDir: opts.claudeConfigDir
|
|
3533
3194
|
});
|
|
3534
|
-
const exists3 =
|
|
3195
|
+
const exists3 = existsSync3(resolved.absolute);
|
|
3535
3196
|
let hasMcphEntry = false;
|
|
3536
3197
|
let malformed = false;
|
|
3537
3198
|
if (exists3) {
|
|
3538
3199
|
try {
|
|
3539
|
-
const raw = await
|
|
3200
|
+
const raw = await readFile5(resolved.absolute, "utf8");
|
|
3540
3201
|
if (raw.trim().length > 0) {
|
|
3541
3202
|
const parsed = parseJsonc(raw);
|
|
3542
3203
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -3612,9 +3273,9 @@ function scanShellHistoryForShadows(opts) {
|
|
|
3612
3273
|
}
|
|
3613
3274
|
function shellHistorySources(opts) {
|
|
3614
3275
|
const sources = [];
|
|
3615
|
-
sources.push({ path:
|
|
3276
|
+
sources.push({ path: join6(opts.home, ".bash_history"), extractCommand: (l) => l.trim() || null });
|
|
3616
3277
|
sources.push({
|
|
3617
|
-
path:
|
|
3278
|
+
path: join6(opts.home, ".zsh_history"),
|
|
3618
3279
|
// Zsh extended-history lines look like `: 1700000000:0;npm audit`.
|
|
3619
3280
|
// Strip the metadata prefix so we get just the command.
|
|
3620
3281
|
extractCommand: (l) => {
|
|
@@ -3630,15 +3291,15 @@ function shellHistorySources(opts) {
|
|
|
3630
3291
|
const appData = opts.env.APPDATA;
|
|
3631
3292
|
if (appData) {
|
|
3632
3293
|
sources.push({
|
|
3633
|
-
path:
|
|
3294
|
+
path: join6(appData, "Microsoft", "Windows", "PowerShell", "PSReadLine", "ConsoleHost_history.txt"),
|
|
3634
3295
|
extractCommand: (l) => l.trim() || null
|
|
3635
3296
|
});
|
|
3636
3297
|
}
|
|
3637
3298
|
return sources;
|
|
3638
3299
|
}
|
|
3639
|
-
function readTailLines(
|
|
3300
|
+
function readTailLines(path3, n) {
|
|
3640
3301
|
try {
|
|
3641
|
-
const raw = readFileSync(
|
|
3302
|
+
const raw = readFileSync(path3, "utf8");
|
|
3642
3303
|
const all = raw.split(/\r?\n/);
|
|
3643
3304
|
return all.length <= n ? all : all.slice(all.length - n);
|
|
3644
3305
|
} catch {
|
|
@@ -3855,9 +3516,9 @@ async function runLogout(opts = {}, io = {
|
|
|
3855
3516
|
}
|
|
3856
3517
|
|
|
3857
3518
|
// src/nag.ts
|
|
3858
|
-
import { readFile as
|
|
3859
|
-
import { homedir as
|
|
3860
|
-
import { join as
|
|
3519
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
3520
|
+
import { homedir as homedir6 } from "os";
|
|
3521
|
+
import { join as join7 } from "path";
|
|
3861
3522
|
var NAG_STATE_FILENAME = "nag-state.json";
|
|
3862
3523
|
var MIN_THRESHOLD = 2;
|
|
3863
3524
|
var MAX_THRESHOLD = 4;
|
|
@@ -3881,15 +3542,15 @@ var NAG_ELIGIBLE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
3881
3542
|
function emptyNagState() {
|
|
3882
3543
|
return { touchPoints: 0, nextThreshold: MIN_THRESHOLD, lastShownAt: 0 };
|
|
3883
3544
|
}
|
|
3884
|
-
function nagStatePath(home =
|
|
3885
|
-
return
|
|
3545
|
+
function nagStatePath(home = homedir6()) {
|
|
3546
|
+
return join7(home, CONFIG_DIRNAME, NAG_STATE_FILENAME);
|
|
3886
3547
|
}
|
|
3887
3548
|
function isFileNotFound2(err) {
|
|
3888
3549
|
return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
|
|
3889
3550
|
}
|
|
3890
3551
|
async function loadNagState(filePath = nagStatePath()) {
|
|
3891
3552
|
try {
|
|
3892
|
-
const raw = await
|
|
3553
|
+
const raw = await readFile6(filePath, "utf8");
|
|
3893
3554
|
const parsed = JSON.parse(raw);
|
|
3894
3555
|
if (!parsed || typeof parsed !== "object") return emptyNagState();
|
|
3895
3556
|
const p = parsed;
|
|
@@ -3994,9 +3655,9 @@ async function showNagInterstitial(opts = {}) {
|
|
|
3994
3655
|
}
|
|
3995
3656
|
|
|
3996
3657
|
// src/reset-learning-cmd.ts
|
|
3997
|
-
import { unlink as
|
|
3998
|
-
import { homedir as
|
|
3999
|
-
import { join as
|
|
3658
|
+
import { unlink as unlink2 } from "fs/promises";
|
|
3659
|
+
import { homedir as homedir7 } from "os";
|
|
3660
|
+
import { join as join8 } from "path";
|
|
4000
3661
|
var RESET_LEARNING_USAGE = `Usage: yaw-mcp reset-learning
|
|
4001
3662
|
|
|
4002
3663
|
Delete ~/.yaw-mcp/state.json so cross-session learning starts fresh.
|
|
@@ -4018,7 +3679,7 @@ ${RESET_LEARNING_USAGE}`
|
|
|
4018
3679
|
return { kind: "ok", options: {} };
|
|
4019
3680
|
}
|
|
4020
3681
|
async function runResetLearning(opts = {}) {
|
|
4021
|
-
const home = opts.home ??
|
|
3682
|
+
const home = opts.home ?? homedir7();
|
|
4022
3683
|
const env = opts.env ?? process.env;
|
|
4023
3684
|
const write = opts.out ?? ((s) => process.stdout.write(s));
|
|
4024
3685
|
const writeErr = opts.err ?? ((s) => process.stderr.write(s));
|
|
@@ -4033,7 +3694,7 @@ async function runResetLearning(opts = {}) {
|
|
|
4033
3694
|
writeErr(`${s}
|
|
4034
3695
|
`);
|
|
4035
3696
|
};
|
|
4036
|
-
const filePath =
|
|
3697
|
+
const filePath = join8(userConfigDir(home), STATE_FILENAME);
|
|
4037
3698
|
const raw = env.YAW_MCP_DISABLE_PERSISTENCE;
|
|
4038
3699
|
const disabled = raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
|
|
4039
3700
|
if (disabled) {
|
|
@@ -4044,7 +3705,7 @@ async function runResetLearning(opts = {}) {
|
|
|
4044
3705
|
const learningCount = Object.keys(persisted.learning).length;
|
|
4045
3706
|
const packCount = persisted.packHistory.length;
|
|
4046
3707
|
try {
|
|
4047
|
-
await
|
|
3708
|
+
await unlink2(filePath);
|
|
4048
3709
|
} catch (err) {
|
|
4049
3710
|
if (isFileNotFound3(err)) {
|
|
4050
3711
|
print("yaw-mcp reset-learning: no persisted state to reset.");
|
|
@@ -4066,16 +3727,16 @@ function isFileNotFound3(err) {
|
|
|
4066
3727
|
}
|
|
4067
3728
|
|
|
4068
3729
|
// src/secrets-cmd.ts
|
|
4069
|
-
import { existsSync as
|
|
4070
|
-
import { mkdir as
|
|
4071
|
-
import { homedir as
|
|
3730
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3731
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
3732
|
+
import { homedir as homedir9 } from "os";
|
|
4072
3733
|
import { dirname as dirname3 } from "path";
|
|
4073
3734
|
|
|
4074
3735
|
// src/secrets-vault.ts
|
|
4075
|
-
import { existsSync as
|
|
4076
|
-
import { chmod as
|
|
4077
|
-
import { homedir as
|
|
4078
|
-
import { dirname as dirname2, join as
|
|
3736
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3737
|
+
import { chmod as chmod3, readFile as readFile7 } from "fs/promises";
|
|
3738
|
+
import { homedir as homedir8 } from "os";
|
|
3739
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
4079
3740
|
|
|
4080
3741
|
// src/secrets-crypto.ts
|
|
4081
3742
|
import { createCipheriv, createDecipheriv, randomBytes, scrypt as scryptCb } from "crypto";
|
|
@@ -4133,8 +3794,8 @@ function decryptEntry(entry, key) {
|
|
|
4133
3794
|
// src/secrets-vault.ts
|
|
4134
3795
|
var SECRETS_FILENAME = "secrets.json";
|
|
4135
3796
|
var SECRETS_SCHEMA_VERSION = 1;
|
|
4136
|
-
function vaultPath(home =
|
|
4137
|
-
return
|
|
3797
|
+
function vaultPath(home = homedir8()) {
|
|
3798
|
+
return join9(home, CONFIG_DIRNAME, SECRETS_FILENAME);
|
|
4138
3799
|
}
|
|
4139
3800
|
function emptyVault() {
|
|
4140
3801
|
return {
|
|
@@ -4143,20 +3804,20 @@ function emptyVault() {
|
|
|
4143
3804
|
entries: {}
|
|
4144
3805
|
};
|
|
4145
3806
|
}
|
|
4146
|
-
async function loadVault(
|
|
4147
|
-
if (!
|
|
3807
|
+
async function loadVault(path3) {
|
|
3808
|
+
if (!existsSync4(path3)) return null;
|
|
4148
3809
|
let raw;
|
|
4149
3810
|
try {
|
|
4150
|
-
raw = await
|
|
3811
|
+
raw = await readFile7(path3, "utf8");
|
|
4151
3812
|
} catch (err) {
|
|
4152
|
-
log("warn", "Failed to read vault", { path:
|
|
3813
|
+
log("warn", "Failed to read vault", { path: path3, error: err instanceof Error ? err.message : String(err) });
|
|
4153
3814
|
return null;
|
|
4154
3815
|
}
|
|
4155
3816
|
let parsed;
|
|
4156
3817
|
try {
|
|
4157
3818
|
parsed = JSON.parse(raw);
|
|
4158
3819
|
} catch (err) {
|
|
4159
|
-
log("warn", "Vault file is not valid JSON", { path:
|
|
3820
|
+
log("warn", "Vault file is not valid JSON", { path: path3, error: err instanceof Error ? err.message : String(err) });
|
|
4160
3821
|
return null;
|
|
4161
3822
|
}
|
|
4162
3823
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
@@ -4168,14 +3829,14 @@ async function loadVault(path5) {
|
|
|
4168
3829
|
entries: obj.entries
|
|
4169
3830
|
};
|
|
4170
3831
|
}
|
|
4171
|
-
async function saveVault(
|
|
4172
|
-
const tmpDir = dirname2(
|
|
3832
|
+
async function saveVault(path3, vault) {
|
|
3833
|
+
const tmpDir = dirname2(path3);
|
|
4173
3834
|
void tmpDir;
|
|
4174
|
-
await atomicWriteFile(
|
|
3835
|
+
await atomicWriteFile(path3, `${JSON.stringify(vault, null, 2)}
|
|
4175
3836
|
`);
|
|
4176
3837
|
if (process.platform !== "win32") {
|
|
4177
3838
|
try {
|
|
4178
|
-
await
|
|
3839
|
+
await chmod3(path3, 384);
|
|
4179
3840
|
} catch {
|
|
4180
3841
|
}
|
|
4181
3842
|
}
|
|
@@ -4399,16 +4060,16 @@ async function readStdinValue(io) {
|
|
|
4399
4060
|
for await (const chunk of stdin) chunks.push(chunk);
|
|
4400
4061
|
return chunks.join("").replace(/\r?\n$/, "");
|
|
4401
4062
|
}
|
|
4402
|
-
async function ensureVaultDir(
|
|
4403
|
-
const dir = dirname3(
|
|
4404
|
-
if (!
|
|
4063
|
+
async function ensureVaultDir(path3) {
|
|
4064
|
+
const dir = dirname3(path3);
|
|
4065
|
+
if (!existsSync5(dir)) await mkdir3(dir, { recursive: true });
|
|
4405
4066
|
}
|
|
4406
4067
|
async function runSecrets(opts, io = {
|
|
4407
4068
|
out: (s) => process.stdout.write(s),
|
|
4408
4069
|
err: (s) => process.stderr.write(s)
|
|
4409
4070
|
}) {
|
|
4410
|
-
const home = opts.home ??
|
|
4411
|
-
const
|
|
4071
|
+
const home = opts.home ?? homedir9();
|
|
4072
|
+
const path3 = vaultPath(home);
|
|
4412
4073
|
if (opts.action === "lock") {
|
|
4413
4074
|
lock();
|
|
4414
4075
|
if (opts.json) io.out(`${JSON.stringify({ ok: true, locked: true })}
|
|
@@ -4423,24 +4084,24 @@ async function runSecrets(opts, io = {
|
|
|
4423
4084
|
return await runSecretsPull(opts, io);
|
|
4424
4085
|
}
|
|
4425
4086
|
if (opts.action === "list") {
|
|
4426
|
-
const vault2 = await loadVault(
|
|
4087
|
+
const vault2 = await loadVault(path3);
|
|
4427
4088
|
const keys = vault2 ? listKeys(vault2) : [];
|
|
4428
|
-
if (opts.json) io.out(`${JSON.stringify({ ok: true, vault:
|
|
4089
|
+
if (opts.json) io.out(`${JSON.stringify({ ok: true, vault: existsSync5(path3), keys }, null, 2)}
|
|
4429
4090
|
`);
|
|
4430
|
-
else if (!vault2) io.out(`No vault at ${
|
|
4091
|
+
else if (!vault2) io.out(`No vault at ${path3}. Run \`yaw-mcp secrets set <name>\` to create one.
|
|
4431
4092
|
`);
|
|
4432
|
-
else if (keys.length === 0) io.out(`Vault at ${
|
|
4093
|
+
else if (keys.length === 0) io.out(`Vault at ${path3} is empty.
|
|
4433
4094
|
`);
|
|
4434
4095
|
else {
|
|
4435
|
-
io.out(`Vault at ${
|
|
4096
|
+
io.out(`Vault at ${path3}
|
|
4436
4097
|
`);
|
|
4437
4098
|
for (const k of keys) io.out(` ${k}
|
|
4438
4099
|
`);
|
|
4439
4100
|
}
|
|
4440
4101
|
return { exitCode: 0 };
|
|
4441
4102
|
}
|
|
4442
|
-
let vault = await loadVault(
|
|
4443
|
-
const isFresh = !
|
|
4103
|
+
let vault = await loadVault(path3) ?? newVault();
|
|
4104
|
+
const isFresh = !existsSync5(path3);
|
|
4444
4105
|
const passphrase = await resolvePassphrase(opts);
|
|
4445
4106
|
if (passphrase === null) {
|
|
4446
4107
|
const msg = "Passphrase required. Set YAW_MCP_VAULT_PASSPHRASE or run from a TTY so we can prompt.";
|
|
@@ -4475,8 +4136,8 @@ async function runSecrets(opts, io = {
|
|
|
4475
4136
|
return { exitCode: 1 };
|
|
4476
4137
|
}
|
|
4477
4138
|
vault = setSecret(vault, key, name, value);
|
|
4478
|
-
await ensureVaultDir(
|
|
4479
|
-
await saveVault(
|
|
4139
|
+
await ensureVaultDir(path3);
|
|
4140
|
+
await saveVault(path3, vault);
|
|
4480
4141
|
if (opts.json) io.out(`${JSON.stringify({ ok: true, name, fresh_vault: isFresh })}
|
|
4481
4142
|
`);
|
|
4482
4143
|
else io.out(`${isFresh ? "Created vault and " : ""}Stored secret "${name}".
|
|
@@ -4522,7 +4183,7 @@ async function runSecrets(opts, io = {
|
|
|
4522
4183
|
return { exitCode: 1 };
|
|
4523
4184
|
}
|
|
4524
4185
|
vault = removeSecret(vault, name);
|
|
4525
|
-
await saveVault(
|
|
4186
|
+
await saveVault(path3, vault);
|
|
4526
4187
|
if (opts.json) io.out(`${JSON.stringify({ ok: true, removed: name })}
|
|
4527
4188
|
`);
|
|
4528
4189
|
else io.out(`Removed "${name}".
|
|
@@ -4535,8 +4196,8 @@ async function runSecrets(opts, io = {
|
|
|
4535
4196
|
}
|
|
4536
4197
|
var MCP_SECRETS_RESOURCE = "mcp_secrets";
|
|
4537
4198
|
async function runSecretsPush(opts, io) {
|
|
4538
|
-
const home = opts.home ??
|
|
4539
|
-
const
|
|
4199
|
+
const home = opts.home ?? homedir9();
|
|
4200
|
+
const path3 = vaultPath(home);
|
|
4540
4201
|
const session = await getSession({ home, baseUrl: opts.baseUrl });
|
|
4541
4202
|
if (!session) {
|
|
4542
4203
|
const msg = "Not signed in. Run `yaw-mcp login --key <license-key>` first.";
|
|
@@ -4546,9 +4207,9 @@ async function runSecretsPush(opts, io) {
|
|
|
4546
4207
|
`);
|
|
4547
4208
|
return { exitCode: 1 };
|
|
4548
4209
|
}
|
|
4549
|
-
const vault = await loadVault(
|
|
4210
|
+
const vault = await loadVault(path3);
|
|
4550
4211
|
if (!vault) {
|
|
4551
|
-
const msg = `No local vault at ${
|
|
4212
|
+
const msg = `No local vault at ${path3} to push. Run \`yaw-mcp secrets set <name>\` first.`;
|
|
4552
4213
|
if (opts.json) io.err(`${JSON.stringify({ ok: false, error: msg })}
|
|
4553
4214
|
`);
|
|
4554
4215
|
else io.err(`yaw-mcp secrets push: ${msg}
|
|
@@ -4598,8 +4259,8 @@ async function runSecretsPush(opts, io) {
|
|
|
4598
4259
|
}
|
|
4599
4260
|
}
|
|
4600
4261
|
async function runSecretsPull(opts, io) {
|
|
4601
|
-
const home = opts.home ??
|
|
4602
|
-
const
|
|
4262
|
+
const home = opts.home ?? homedir9();
|
|
4263
|
+
const path3 = vaultPath(home);
|
|
4603
4264
|
const session = await getSession({ home, baseUrl: opts.baseUrl });
|
|
4604
4265
|
if (!session) {
|
|
4605
4266
|
const msg = "Not signed in. Run `yaw-mcp login --key <license-key>` first.";
|
|
@@ -4619,17 +4280,17 @@ async function runSecretsPull(opts, io) {
|
|
|
4619
4280
|
`);
|
|
4620
4281
|
return { exitCode: 0 };
|
|
4621
4282
|
}
|
|
4622
|
-
await ensureVaultDir(
|
|
4623
|
-
await saveVault(
|
|
4283
|
+
await ensureVaultDir(path3);
|
|
4284
|
+
await saveVault(path3, remote.data);
|
|
4624
4285
|
lock();
|
|
4625
4286
|
const count = Object.keys(remote.data.entries).length;
|
|
4626
4287
|
if (opts.json) {
|
|
4627
4288
|
io.out(
|
|
4628
|
-
`${JSON.stringify({ ok: true, secret_count: count, remote_version: remote.version, written:
|
|
4289
|
+
`${JSON.stringify({ ok: true, secret_count: count, remote_version: remote.version, written: path3 }, null, 2)}
|
|
4629
4290
|
`
|
|
4630
4291
|
);
|
|
4631
4292
|
} else {
|
|
4632
|
-
io.out(`Pulled ${count} secret${count === 1 ? "" : "s"} (encrypted) -> ${
|
|
4293
|
+
io.out(`Pulled ${count} secret${count === 1 ? "" : "s"} (encrypted) -> ${path3}
|
|
4633
4294
|
`);
|
|
4634
4295
|
io.out("Vault locked -- next secrets command will prompt for the passphrase.\n");
|
|
4635
4296
|
}
|
|
@@ -4653,8 +4314,8 @@ async function runSecretsPull(opts, io) {
|
|
|
4653
4314
|
}
|
|
4654
4315
|
|
|
4655
4316
|
// src/server.ts
|
|
4656
|
-
import { readFile as
|
|
4657
|
-
import { homedir as
|
|
4317
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
4318
|
+
import { homedir as homedir11 } from "os";
|
|
4658
4319
|
import { isAbsolute, relative, resolve as resolve4 } from "path";
|
|
4659
4320
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4660
4321
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -4854,7 +4515,7 @@ Or re-run with --run to upgrade in place.`);
|
|
|
4854
4515
|
return { exitCode: 3, lines };
|
|
4855
4516
|
}
|
|
4856
4517
|
function readCurrentVersion() {
|
|
4857
|
-
return true ? "0.58.
|
|
4518
|
+
return true ? "0.58.1" : "dev";
|
|
4858
4519
|
}
|
|
4859
4520
|
|
|
4860
4521
|
// src/auto-upgrade.ts
|
|
@@ -4902,7 +4563,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
4902
4563
|
async function maybeAutoUpgrade(deps = {}) {
|
|
4903
4564
|
const optOut = process.env.YAW_MCP_AUTO_UPGRADE;
|
|
4904
4565
|
if (optOut === "0" || optOut?.toLowerCase() === "false") return;
|
|
4905
|
-
const current = deps.currentVersion ?? (true ? "0.58.
|
|
4566
|
+
const current = deps.currentVersion ?? (true ? "0.58.1" : "dev");
|
|
4906
4567
|
if (current === "dev") return;
|
|
4907
4568
|
const method = detectInstallMethod(deps.argvPath ?? process.argv[1]);
|
|
4908
4569
|
const latest = await (deps.fetchLatestImpl ?? fetchLatestVersion2)();
|
|
@@ -5273,20 +4934,20 @@ function stepBindingKey(step, index) {
|
|
|
5273
4934
|
}
|
|
5274
4935
|
|
|
5275
4936
|
// src/guide.ts
|
|
5276
|
-
import { readFile as
|
|
4937
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
5277
4938
|
var GUIDE_READ_TIMEOUT_MS = 1e3;
|
|
5278
|
-
async function readGuide(
|
|
4939
|
+
async function readGuide(path3, scope) {
|
|
5279
4940
|
let raw;
|
|
5280
4941
|
try {
|
|
5281
4942
|
raw = await Promise.race([
|
|
5282
|
-
|
|
4943
|
+
readFile8(path3, "utf8"),
|
|
5283
4944
|
new Promise(
|
|
5284
4945
|
(_, reject) => setTimeout(() => reject(new Error("guide read timeout")), GUIDE_READ_TIMEOUT_MS)
|
|
5285
4946
|
)
|
|
5286
4947
|
]);
|
|
5287
4948
|
} catch (err) {
|
|
5288
4949
|
if (err instanceof Error && err.message === "guide read timeout") {
|
|
5289
|
-
log("warn", "Guide read timed out", { path:
|
|
4950
|
+
log("warn", "Guide read timed out", { path: path3 });
|
|
5290
4951
|
}
|
|
5291
4952
|
return null;
|
|
5292
4953
|
}
|
|
@@ -5294,7 +4955,7 @@ async function readGuide(path5, scope) {
|
|
|
5294
4955
|
if (content.length === 0) {
|
|
5295
4956
|
return null;
|
|
5296
4957
|
}
|
|
5297
|
-
return { scope, path:
|
|
4958
|
+
return { scope, path: path3, content };
|
|
5298
4959
|
}
|
|
5299
4960
|
async function loadUserGuide(home) {
|
|
5300
4961
|
const p = guidePath(userConfigDir(home));
|
|
@@ -5563,13 +5224,13 @@ var LearningStore = class {
|
|
|
5563
5224
|
|
|
5564
5225
|
// src/local-bundles.ts
|
|
5565
5226
|
import { createHash as createHash2 } from "crypto";
|
|
5566
|
-
import { readFile as
|
|
5567
|
-
import { homedir as
|
|
5568
|
-
import { join as
|
|
5227
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
5228
|
+
import { homedir as homedir10 } from "os";
|
|
5229
|
+
import { join as join10 } from "path";
|
|
5569
5230
|
var BUNDLES_FILENAME = "bundles.json";
|
|
5570
5231
|
var CURRENT_BUNDLES_SCHEMA_VERSION = 1;
|
|
5571
5232
|
function localBundlesPath(configDir) {
|
|
5572
|
-
return
|
|
5233
|
+
return join10(configDir, BUNDLES_FILENAME);
|
|
5573
5234
|
}
|
|
5574
5235
|
var NAMESPACE_RE = /^[a-z][a-z0-9_]{0,29}$/;
|
|
5575
5236
|
function validateEntry(entry, warnings) {
|
|
@@ -5609,10 +5270,10 @@ function validateEntry(entry, warnings) {
|
|
|
5609
5270
|
description
|
|
5610
5271
|
};
|
|
5611
5272
|
}
|
|
5612
|
-
async function readBundlesAt(
|
|
5273
|
+
async function readBundlesAt(path3, warnings) {
|
|
5613
5274
|
let raw;
|
|
5614
5275
|
try {
|
|
5615
|
-
raw = await
|
|
5276
|
+
raw = await readFile9(path3, "utf8");
|
|
5616
5277
|
} catch {
|
|
5617
5278
|
return { exists: false, file: null };
|
|
5618
5279
|
}
|
|
@@ -5621,24 +5282,24 @@ async function readBundlesAt(path5, warnings) {
|
|
|
5621
5282
|
parsed = parseJsonc(raw);
|
|
5622
5283
|
} catch (err) {
|
|
5623
5284
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5624
|
-
warnings.push(`${
|
|
5625
|
-
log("warn", "bundles.json is not valid JSON; ignoring", { path:
|
|
5285
|
+
warnings.push(`${path3}: invalid JSON (${msg}) -- file ignored`);
|
|
5286
|
+
log("warn", "bundles.json is not valid JSON; ignoring", { path: path3, error: msg });
|
|
5626
5287
|
return { exists: true, file: null };
|
|
5627
5288
|
}
|
|
5628
5289
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5629
|
-
warnings.push(`${
|
|
5290
|
+
warnings.push(`${path3}: root must be a JSON object -- file ignored`);
|
|
5630
5291
|
return { exists: true, file: null };
|
|
5631
5292
|
}
|
|
5632
5293
|
const obj = parsed;
|
|
5633
5294
|
const version = typeof obj.version === "number" ? obj.version : void 0;
|
|
5634
5295
|
if (version !== void 0 && version > CURRENT_BUNDLES_SCHEMA_VERSION) {
|
|
5635
5296
|
warnings.push(
|
|
5636
|
-
`${
|
|
5297
|
+
`${path3}: schema version ${version} is newer than this yaw-mcp (${CURRENT_BUNDLES_SCHEMA_VERSION}); upgrade with \`npm i -g @yawlabs/mcp@latest\`. Loading best-effort.`
|
|
5637
5298
|
);
|
|
5638
5299
|
}
|
|
5639
5300
|
const rawServers = obj.servers;
|
|
5640
5301
|
if (!Array.isArray(rawServers)) {
|
|
5641
|
-
warnings.push(`${
|
|
5302
|
+
warnings.push(`${path3}: 'servers' must be an array -- file ignored`);
|
|
5642
5303
|
return { exists: true, file: null };
|
|
5643
5304
|
}
|
|
5644
5305
|
return {
|
|
@@ -5653,11 +5314,11 @@ function hashContent(servers) {
|
|
|
5653
5314
|
}
|
|
5654
5315
|
async function loadLocalBundles(opts = {}) {
|
|
5655
5316
|
const cwd = opts.cwd ?? process.cwd();
|
|
5656
|
-
const home = opts.home ??
|
|
5317
|
+
const home = opts.home ?? homedir10();
|
|
5657
5318
|
const warnings = [];
|
|
5658
5319
|
const projectDir = await findProjectConfigDir(cwd, home).catch(() => null);
|
|
5659
5320
|
const projectPath = projectDir ? localBundlesPath(projectDir) : null;
|
|
5660
|
-
const globalPath = localBundlesPath(
|
|
5321
|
+
const globalPath = localBundlesPath(join10(home, CONFIG_DIRNAME));
|
|
5661
5322
|
const projectResult = projectPath ? await readBundlesAt(projectPath, warnings) : { exists: false, file: null };
|
|
5662
5323
|
let file;
|
|
5663
5324
|
let sourcePath;
|
|
@@ -6178,9 +5839,9 @@ var PackDetector = class {
|
|
|
6178
5839
|
|
|
6179
5840
|
// src/progress.ts
|
|
6180
5841
|
function createProgressReporter(extra) {
|
|
6181
|
-
const
|
|
5842
|
+
const token5 = extra?._meta?.progressToken;
|
|
6182
5843
|
const send = extra?.sendNotification;
|
|
6183
|
-
if (
|
|
5844
|
+
if (token5 === void 0 || token5 === null || !send) {
|
|
6184
5845
|
return () => {
|
|
6185
5846
|
};
|
|
6186
5847
|
}
|
|
@@ -6188,7 +5849,7 @@ function createProgressReporter(extra) {
|
|
|
6188
5849
|
return (message, progress, total) => {
|
|
6189
5850
|
step += 1;
|
|
6190
5851
|
const params = {
|
|
6191
|
-
progressToken:
|
|
5852
|
+
progressToken: token5,
|
|
6192
5853
|
progress: progress ?? step,
|
|
6193
5854
|
message
|
|
6194
5855
|
};
|
|
@@ -6643,25 +6304,76 @@ function rankServers(context, servers) {
|
|
|
6643
6304
|
|
|
6644
6305
|
// src/rerank.ts
|
|
6645
6306
|
import { request as request7 } from "undici";
|
|
6646
|
-
var apiUrl4 = "";
|
|
6647
|
-
var token4 = "";
|
|
6648
6307
|
var RERANK_TIMEOUT_MS = 2e3;
|
|
6308
|
+
var DEFAULT_TEAM_BASE_URL = "https://yaw.sh";
|
|
6309
|
+
var legacyApiUrl = "";
|
|
6310
|
+
var legacyToken = "";
|
|
6649
6311
|
function initRerank(url, tok) {
|
|
6650
|
-
|
|
6651
|
-
|
|
6312
|
+
legacyApiUrl = url;
|
|
6313
|
+
legacyToken = tok;
|
|
6652
6314
|
}
|
|
6653
6315
|
async function rerank(intent, candidateIds, limit) {
|
|
6654
|
-
if (!apiUrl4 || !token4) return null;
|
|
6655
6316
|
if (!intent?.trim()) return null;
|
|
6656
6317
|
if (candidateIds !== void 0 && candidateIds.length === 0) return null;
|
|
6657
6318
|
const payload = { intent: intent.trim() };
|
|
6658
6319
|
if (candidateIds && candidateIds.length > 0) payload.candidateIds = candidateIds;
|
|
6659
6320
|
if (typeof limit === "number" && limit > 0) payload.limit = limit;
|
|
6321
|
+
const session = await getSession().catch(() => null);
|
|
6322
|
+
if (session) {
|
|
6323
|
+
const result = await callTeamRerank(payload);
|
|
6324
|
+
if (result !== null) return result;
|
|
6325
|
+
return null;
|
|
6326
|
+
}
|
|
6327
|
+
if (!legacyApiUrl || !legacyToken) return null;
|
|
6328
|
+
return callLegacyRerank(payload);
|
|
6329
|
+
}
|
|
6330
|
+
async function callTeamRerank(payload) {
|
|
6331
|
+
const base = (process.env.YAW_MCP_TEAM_BASE_URL?.replace(/\/$/, "") || DEFAULT_TEAM_BASE_URL).replace(/\/$/, "");
|
|
6332
|
+
const cookie = await readTeamCookie();
|
|
6333
|
+
if (!cookie) return null;
|
|
6660
6334
|
try {
|
|
6661
|
-
const res = await request7(`${
|
|
6335
|
+
const res = await request7(`${base}/api/team/rerank`, {
|
|
6662
6336
|
method: "POST",
|
|
6663
6337
|
headers: {
|
|
6664
|
-
|
|
6338
|
+
Cookie: `yaw_team=${cookie}`,
|
|
6339
|
+
"Content-Type": "application/json"
|
|
6340
|
+
},
|
|
6341
|
+
body: JSON.stringify(payload),
|
|
6342
|
+
headersTimeout: RERANK_TIMEOUT_MS,
|
|
6343
|
+
bodyTimeout: RERANK_TIMEOUT_MS
|
|
6344
|
+
});
|
|
6345
|
+
if (res.statusCode === 503) {
|
|
6346
|
+
await res.body.text().catch(() => {
|
|
6347
|
+
});
|
|
6348
|
+
return null;
|
|
6349
|
+
}
|
|
6350
|
+
if (res.statusCode === 401) {
|
|
6351
|
+
await res.body.text().catch(() => {
|
|
6352
|
+
});
|
|
6353
|
+
log("debug", "team-rerank 401; session likely expired", {});
|
|
6354
|
+
return null;
|
|
6355
|
+
}
|
|
6356
|
+
if (res.statusCode !== 200) {
|
|
6357
|
+
await res.body.text().catch(() => {
|
|
6358
|
+
});
|
|
6359
|
+
log("warn", "team-rerank request failed", { status: res.statusCode });
|
|
6360
|
+
return null;
|
|
6361
|
+
}
|
|
6362
|
+
const body = await res.body.json();
|
|
6363
|
+
if (!body || !Array.isArray(body.results)) return null;
|
|
6364
|
+
if (body.results.length === 0) return null;
|
|
6365
|
+
return body.results;
|
|
6366
|
+
} catch (err) {
|
|
6367
|
+
log("debug", "team-rerank request errored", { error: err instanceof Error ? err.message : String(err) });
|
|
6368
|
+
return null;
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
6371
|
+
async function callLegacyRerank(payload) {
|
|
6372
|
+
try {
|
|
6373
|
+
const res = await request7(`${legacyApiUrl.replace(/\/$/, "")}/api/connect/rerank`, {
|
|
6374
|
+
method: "POST",
|
|
6375
|
+
headers: {
|
|
6376
|
+
Authorization: `Bearer ${legacyToken}`,
|
|
6665
6377
|
"Content-Type": "application/json"
|
|
6666
6378
|
},
|
|
6667
6379
|
body: JSON.stringify(payload),
|
|
@@ -6684,7 +6396,20 @@ async function rerank(intent, candidateIds, limit) {
|
|
|
6684
6396
|
if (body.results.length === 0) return null;
|
|
6685
6397
|
return body.results;
|
|
6686
6398
|
} catch (err) {
|
|
6687
|
-
log("debug", "Rerank request errored", { error: err
|
|
6399
|
+
log("debug", "Rerank request errored", { error: err instanceof Error ? err.message : String(err) });
|
|
6400
|
+
return null;
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
async function readTeamCookie() {
|
|
6404
|
+
const teamSync = await import("./team-sync-4JF5LBRB.js");
|
|
6405
|
+
const session = await teamSync.getSession();
|
|
6406
|
+
if (!session) return null;
|
|
6407
|
+
const { readFile: readFile12 } = await import("fs/promises");
|
|
6408
|
+
try {
|
|
6409
|
+
const raw = await readFile12(teamSync.sessionStatePath(), "utf8");
|
|
6410
|
+
const parsed = JSON.parse(raw);
|
|
6411
|
+
return typeof parsed.cookie === "string" && parsed.cookie ? parsed.cookie : null;
|
|
6412
|
+
} catch {
|
|
6688
6413
|
return null;
|
|
6689
6414
|
}
|
|
6690
6415
|
}
|
|
@@ -6694,11 +6419,11 @@ import { spawn as spawn4 } from "child_process";
|
|
|
6694
6419
|
import { request as request8 } from "undici";
|
|
6695
6420
|
var PROBE_TIMEOUT_MS = 3e3;
|
|
6696
6421
|
var RUNTIME_REPORT_PATH = "/api/connect/runtimes";
|
|
6697
|
-
var
|
|
6698
|
-
var
|
|
6422
|
+
var apiUrl4 = "";
|
|
6423
|
+
var token4 = "";
|
|
6699
6424
|
function initRuntimeDetect(url, tok) {
|
|
6700
|
-
|
|
6701
|
-
|
|
6425
|
+
apiUrl4 = url;
|
|
6426
|
+
token4 = tok;
|
|
6702
6427
|
}
|
|
6703
6428
|
var PROBES = {
|
|
6704
6429
|
node: {
|
|
@@ -6810,7 +6535,7 @@ async function detectRuntimes() {
|
|
|
6810
6535
|
return out;
|
|
6811
6536
|
}
|
|
6812
6537
|
async function reportRuntimes() {
|
|
6813
|
-
if (!
|
|
6538
|
+
if (!apiUrl4 || !token4) return;
|
|
6814
6539
|
let runtimes;
|
|
6815
6540
|
try {
|
|
6816
6541
|
runtimes = await detectRuntimes();
|
|
@@ -6819,10 +6544,10 @@ async function reportRuntimes() {
|
|
|
6819
6544
|
return;
|
|
6820
6545
|
}
|
|
6821
6546
|
try {
|
|
6822
|
-
const res = await request8(`${
|
|
6547
|
+
const res = await request8(`${apiUrl4.replace(/\/$/, "")}${RUNTIME_REPORT_PATH}`, {
|
|
6823
6548
|
method: "POST",
|
|
6824
6549
|
headers: {
|
|
6825
|
-
Authorization: `Bearer ${
|
|
6550
|
+
Authorization: `Bearer ${token4}`,
|
|
6826
6551
|
"Content-Type": "application/json"
|
|
6827
6552
|
},
|
|
6828
6553
|
body: JSON.stringify({ runtimes }),
|
|
@@ -6977,7 +6702,7 @@ import { spawn as spawn5 } from "child_process";
|
|
|
6977
6702
|
import { createHash as createHash3 } from "crypto";
|
|
6978
6703
|
import { createWriteStream } from "fs";
|
|
6979
6704
|
import fs from "fs/promises";
|
|
6980
|
-
import
|
|
6705
|
+
import path2 from "path";
|
|
6981
6706
|
import { pipeline } from "stream/promises";
|
|
6982
6707
|
import { request as request9 } from "undici";
|
|
6983
6708
|
var UV_VERSION = "0.11.7";
|
|
@@ -7106,7 +6831,7 @@ function runCommand(cmd, args) {
|
|
|
7106
6831
|
async function findBinary(root, name) {
|
|
7107
6832
|
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
7108
6833
|
for (const e of entries) {
|
|
7109
|
-
const full =
|
|
6834
|
+
const full = path2.join(root, e.name);
|
|
7110
6835
|
if (e.isFile() && e.name === name) return full;
|
|
7111
6836
|
if (e.isDirectory()) {
|
|
7112
6837
|
const found = await findBinary(full, name);
|
|
@@ -7128,8 +6853,8 @@ async function resolveUv() {
|
|
|
7128
6853
|
`No prebuilt uv binary for ${process.platform}/${process.arch}. Install uv manually: https://docs.astral.sh/uv/`
|
|
7129
6854
|
);
|
|
7130
6855
|
}
|
|
7131
|
-
const installDir =
|
|
7132
|
-
const finalBin =
|
|
6856
|
+
const installDir = path2.join(cacheDir(), "uv", UV_VERSION);
|
|
6857
|
+
const finalBin = path2.join(installDir, binName());
|
|
7133
6858
|
if (await exists2(finalBin)) return finalBin;
|
|
7134
6859
|
await fs.mkdir(installDir, { recursive: true });
|
|
7135
6860
|
log("info", "Bootstrapping uv", { version: UV_VERSION, target, cache: installDir });
|
|
@@ -7142,11 +6867,11 @@ async function resolveUv() {
|
|
|
7142
6867
|
if (!expected || expected.toLowerCase() !== actual.toLowerCase()) {
|
|
7143
6868
|
throw new Error(`uv archive checksum mismatch (expected ${expected}, got ${actual})`);
|
|
7144
6869
|
}
|
|
7145
|
-
const archivePath =
|
|
6870
|
+
const archivePath = path2.join(installDir, archiveName);
|
|
7146
6871
|
await pipeline(async function* () {
|
|
7147
6872
|
yield archiveBuf;
|
|
7148
6873
|
}, createWriteStream(archivePath));
|
|
7149
|
-
const extractDir =
|
|
6874
|
+
const extractDir = path2.join(installDir, "extract");
|
|
7150
6875
|
await fs.rm(extractDir, { recursive: true, force: true });
|
|
7151
6876
|
await extractArchive(archivePath, extractDir);
|
|
7152
6877
|
const extracted = await findBinary(extractDir, binName());
|
|
@@ -7233,7 +6958,7 @@ function categorizeSpawnError(err) {
|
|
|
7233
6958
|
}
|
|
7234
6959
|
async function connectToUpstream(config, onDisconnect, onListChanged) {
|
|
7235
6960
|
const client = new Client(
|
|
7236
|
-
{ name: "yaw-mcp", version: true ? "0.58.
|
|
6961
|
+
{ name: "yaw-mcp", version: true ? "0.58.1" : "dev" },
|
|
7237
6962
|
{ capabilities: {} }
|
|
7238
6963
|
);
|
|
7239
6964
|
let transport;
|
|
@@ -7538,11 +7263,11 @@ function computeToolOverlaps(connections) {
|
|
|
7538
7263
|
return overlaps;
|
|
7539
7264
|
}
|
|
7540
7265
|
var ConnectServer = class _ConnectServer {
|
|
7541
|
-
constructor(
|
|
7542
|
-
this.apiUrl =
|
|
7543
|
-
this.token =
|
|
7266
|
+
constructor(apiUrl5, token5) {
|
|
7267
|
+
this.apiUrl = apiUrl5;
|
|
7268
|
+
this.token = token5;
|
|
7544
7269
|
this.server = new Server(
|
|
7545
|
-
{ name: "yaw-mcp", version: true ? "0.58.
|
|
7270
|
+
{ name: "yaw-mcp", version: true ? "0.58.1" : "dev" },
|
|
7546
7271
|
{
|
|
7547
7272
|
capabilities: {
|
|
7548
7273
|
tools: { listChanged: true },
|
|
@@ -9073,7 +8798,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
9073
8798
|
}
|
|
9074
8799
|
const ALLOWED_FILENAMES = ["claude_desktop_config.json", "mcp.json", "settings.json", "mcp_config.json"];
|
|
9075
8800
|
try {
|
|
9076
|
-
const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve4(
|
|
8801
|
+
const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve4(homedir11(), filepath.slice(2)) : resolve4(filepath);
|
|
9077
8802
|
const resolvedBasename = resolved.split(/[/\\]/).pop() || "";
|
|
9078
8803
|
if (!ALLOWED_FILENAMES.includes(resolvedBasename)) {
|
|
9079
8804
|
return {
|
|
@@ -9090,7 +8815,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
9090
8815
|
const rel = relative(base, p);
|
|
9091
8816
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
9092
8817
|
};
|
|
9093
|
-
if (!isUnder(
|
|
8818
|
+
if (!isUnder(homedir11(), resolved) && !isUnder(process.cwd(), resolved)) {
|
|
9094
8819
|
return {
|
|
9095
8820
|
content: [
|
|
9096
8821
|
{ type: "text", text: "Import path must be under your home directory or the current working directory." }
|
|
@@ -9098,7 +8823,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
|
|
|
9098
8823
|
isError: true
|
|
9099
8824
|
};
|
|
9100
8825
|
}
|
|
9101
|
-
const raw = await
|
|
8826
|
+
const raw = await readFile10(resolved, "utf-8");
|
|
9102
8827
|
const parsed = JSON.parse(raw);
|
|
9103
8828
|
if (!parsed.mcpServers || typeof parsed.mcpServers !== "object" || Array.isArray(parsed.mcpServers)) {
|
|
9104
8829
|
return {
|
|
@@ -9824,7 +9549,7 @@ function truncateVersion(v) {
|
|
|
9824
9549
|
}
|
|
9825
9550
|
|
|
9826
9551
|
// src/stats-cmd.ts
|
|
9827
|
-
import { homedir as
|
|
9552
|
+
import { homedir as homedir12 } from "os";
|
|
9828
9553
|
var STATS_USAGE = `Usage: yaw-mcp stats [--json] [--limit N] [--days N]
|
|
9829
9554
|
|
|
9830
9555
|
Print a digest of recent AI tool calls recorded against your Yaw MCP
|
|
@@ -9939,7 +9664,7 @@ async function runStats(opts, io = {
|
|
|
9939
9664
|
out: (s) => process.stdout.write(s),
|
|
9940
9665
|
err: (s) => process.stderr.write(s)
|
|
9941
9666
|
}) {
|
|
9942
|
-
const home = opts.home ??
|
|
9667
|
+
const home = opts.home ?? homedir12();
|
|
9943
9668
|
const session = await getSession({ home, baseUrl: opts.baseUrl });
|
|
9944
9669
|
if (!session) {
|
|
9945
9670
|
const msg = "Not signed in. Yaw MCP analytics requires a Pro or Yaw Business account.\n - Pro: $9/mo or $90/yr -- https://yaw.sh/mcp#pricing\n - Yaw Business: $10/seat/mo (includes Yaw Terminal Business)\nSign in with: yaw-mcp login --key <license-key>";
|
|
@@ -9996,10 +9721,10 @@ async function runStats(opts, io = {
|
|
|
9996
9721
|
}
|
|
9997
9722
|
|
|
9998
9723
|
// src/sync-cmd.ts
|
|
9999
|
-
import { existsSync as
|
|
10000
|
-
import { mkdir as
|
|
10001
|
-
import { homedir as
|
|
10002
|
-
import { dirname as dirname4, join as
|
|
9724
|
+
import { existsSync as existsSync6 } from "fs";
|
|
9725
|
+
import { mkdir as mkdir4, readFile as readFile11 } from "fs/promises";
|
|
9726
|
+
import { homedir as homedir13 } from "os";
|
|
9727
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
10003
9728
|
var SYNC_USAGE = `Usage: yaw-mcp sync <push|pull|status> [--json]
|
|
10004
9729
|
|
|
10005
9730
|
Replicate ~/.yaw-mcp/bundles.json across machines via your Yaw
|
|
@@ -10042,24 +9767,24 @@ ${SYNC_USAGE}` };
|
|
|
10042
9767
|
return { ok: true, options: opts };
|
|
10043
9768
|
}
|
|
10044
9769
|
function bundlesPath(home) {
|
|
10045
|
-
return
|
|
9770
|
+
return join11(home, CONFIG_DIRNAME, BUNDLES_FILENAME2);
|
|
10046
9771
|
}
|
|
10047
9772
|
async function readLocalBundles(home) {
|
|
10048
|
-
const
|
|
10049
|
-
if (!
|
|
10050
|
-
const raw = await
|
|
9773
|
+
const path3 = bundlesPath(home);
|
|
9774
|
+
if (!existsSync6(path3)) return { version: 1, servers: [] };
|
|
9775
|
+
const raw = await readFile11(path3, "utf8");
|
|
10051
9776
|
const parsed = JSON.parse(raw);
|
|
10052
9777
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.servers)) {
|
|
10053
|
-
throw new Error(`${
|
|
9778
|
+
throw new Error(`${path3}: malformed -- expected { servers: [...] }`);
|
|
10054
9779
|
}
|
|
10055
9780
|
return parsed;
|
|
10056
9781
|
}
|
|
10057
9782
|
async function writeLocalBundles(home, file) {
|
|
10058
|
-
const
|
|
10059
|
-
await
|
|
10060
|
-
await atomicWriteFile(
|
|
9783
|
+
const path3 = bundlesPath(home);
|
|
9784
|
+
await mkdir4(dirname4(path3), { recursive: true });
|
|
9785
|
+
await atomicWriteFile(path3, `${JSON.stringify(file, null, 2)}
|
|
10061
9786
|
`);
|
|
10062
|
-
return
|
|
9787
|
+
return path3;
|
|
10063
9788
|
}
|
|
10064
9789
|
function stripEnvValues(server) {
|
|
10065
9790
|
if (!server.env || typeof server.env !== "object") return server;
|
|
@@ -10084,7 +9809,7 @@ async function runSync(opts, io = {
|
|
|
10084
9809
|
out: (s) => process.stdout.write(s),
|
|
10085
9810
|
err: (s) => process.stderr.write(s)
|
|
10086
9811
|
}) {
|
|
10087
|
-
const home = opts.home ??
|
|
9812
|
+
const home = opts.home ?? homedir13();
|
|
10088
9813
|
const session = await getSession({ home, baseUrl: opts.baseUrl });
|
|
10089
9814
|
if (!session) {
|
|
10090
9815
|
const msg = "Not signed in. Run `yaw-mcp login --key <license-key>` first.";
|
|
@@ -10489,7 +10214,7 @@ if (subcommand === "compliance") {
|
|
|
10489
10214
|
`);
|
|
10490
10215
|
process.exit(0);
|
|
10491
10216
|
} else if (subcommand === "--version" || subcommand === "-V") {
|
|
10492
|
-
process.stdout.write(`yaw-mcp ${true ? "0.58.
|
|
10217
|
+
process.stdout.write(`yaw-mcp ${true ? "0.58.1" : "dev"}
|
|
10493
10218
|
`);
|
|
10494
10219
|
process.exit(0);
|
|
10495
10220
|
} else if (subcommand && !subcommand.startsWith("-")) {
|