ofw-mcp 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/auth.js +86 -56
- package/dist/bundle.js +630 -224
- package/dist/client.js +24 -12
- package/dist/index.js +1 -1
- package/dist/session-cache.js +78 -0
- package/mint.yaml +143 -0
- package/package.json +9 -7
- package/server.json +14 -2
- package/skills/ofw/SKILL.md +1 -1
package/dist/bundle.js
CHANGED
|
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
|
|
|
2991
2991
|
const schOrFunc = root.refs[ref];
|
|
2992
2992
|
if (schOrFunc)
|
|
2993
2993
|
return schOrFunc;
|
|
2994
|
-
let _sch =
|
|
2994
|
+
let _sch = resolve3.call(this, root, ref);
|
|
2995
2995
|
if (_sch === void 0) {
|
|
2996
2996
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2997
2997
|
const { schemaId } = this.opts;
|
|
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
|
|
|
3018
3018
|
function sameSchemaEnv(s1, s2) {
|
|
3019
3019
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3020
3020
|
}
|
|
3021
|
-
function
|
|
3021
|
+
function resolve3(root, ref) {
|
|
3022
3022
|
let sch;
|
|
3023
3023
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3024
3024
|
ref = sch;
|
|
@@ -3314,8 +3314,8 @@ var require_utils = __commonJS({
|
|
|
3314
3314
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
3315
3315
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
3316
3316
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
3317
|
-
function reescapeHostDelimiters(host,
|
|
3318
|
-
const re =
|
|
3317
|
+
function reescapeHostDelimiters(host, isIP2) {
|
|
3318
|
+
const re = isIP2 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
3319
3319
|
re.lastIndex = 0;
|
|
3320
3320
|
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
3321
3321
|
}
|
|
@@ -3649,7 +3649,7 @@ var require_fast_uri = __commonJS({
|
|
|
3649
3649
|
}
|
|
3650
3650
|
return uri;
|
|
3651
3651
|
}
|
|
3652
|
-
function
|
|
3652
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
3653
3653
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3654
3654
|
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3655
3655
|
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
@@ -3804,7 +3804,7 @@ var require_fast_uri = __commonJS({
|
|
|
3804
3804
|
fragment: void 0
|
|
3805
3805
|
};
|
|
3806
3806
|
let malformedAuthorityOrPort = false;
|
|
3807
|
-
let
|
|
3807
|
+
let isIP2 = false;
|
|
3808
3808
|
if (options.reference === "suffix") {
|
|
3809
3809
|
if (options.scheme) {
|
|
3810
3810
|
uri = options.scheme + ":" + uri;
|
|
@@ -3853,9 +3853,9 @@ var require_fast_uri = __commonJS({
|
|
|
3853
3853
|
if (ipv4result === false) {
|
|
3854
3854
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
3855
3855
|
parsed.host = ipv6result.host.toLowerCase();
|
|
3856
|
-
|
|
3856
|
+
isIP2 = ipv6result.isIPV6;
|
|
3857
3857
|
} else {
|
|
3858
|
-
|
|
3858
|
+
isIP2 = true;
|
|
3859
3859
|
}
|
|
3860
3860
|
}
|
|
3861
3861
|
if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) {
|
|
@@ -3872,7 +3872,7 @@ var require_fast_uri = __commonJS({
|
|
|
3872
3872
|
}
|
|
3873
3873
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
3874
3874
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
|
|
3875
|
-
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) &&
|
|
3875
|
+
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
|
|
3876
3876
|
try {
|
|
3877
3877
|
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
3878
3878
|
} catch (e) {
|
|
@@ -3886,7 +3886,7 @@ var require_fast_uri = __commonJS({
|
|
|
3886
3886
|
parsed.scheme = unescape(parsed.scheme);
|
|
3887
3887
|
}
|
|
3888
3888
|
if (parsed.host !== void 0) {
|
|
3889
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host),
|
|
3889
|
+
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP2);
|
|
3890
3890
|
}
|
|
3891
3891
|
}
|
|
3892
3892
|
if (parsed.path) {
|
|
@@ -3933,7 +3933,7 @@ var require_fast_uri = __commonJS({
|
|
|
3933
3933
|
var fastUri = {
|
|
3934
3934
|
SCHEMES,
|
|
3935
3935
|
normalize,
|
|
3936
|
-
resolve:
|
|
3936
|
+
resolve: resolve3,
|
|
3937
3937
|
resolveComponent,
|
|
3938
3938
|
equal,
|
|
3939
3939
|
serialize,
|
|
@@ -7199,7 +7199,7 @@ var require_permessage_deflate = __commonJS({
|
|
|
7199
7199
|
acceptAsServer(offers) {
|
|
7200
7200
|
const opts = this._options;
|
|
7201
7201
|
const accepted = offers.find((params) => {
|
|
7202
|
-
if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) {
|
|
7202
|
+
if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && (typeof params.client_max_window_bits === "number" ? opts.clientMaxWindowBits > params.client_max_window_bits : !params.client_max_window_bits)) {
|
|
7203
7203
|
return false;
|
|
7204
7204
|
}
|
|
7205
7205
|
return true;
|
|
@@ -9161,7 +9161,7 @@ var require_websocket = __commonJS({
|
|
|
9161
9161
|
var http = __require("http");
|
|
9162
9162
|
var net = __require("net");
|
|
9163
9163
|
var tls = __require("tls");
|
|
9164
|
-
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
9164
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = __require("crypto");
|
|
9165
9165
|
var { Duplex, Readable } = __require("stream");
|
|
9166
9166
|
var { URL: URL2 } = __require("url");
|
|
9167
9167
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -9699,7 +9699,7 @@ var require_websocket = __commonJS({
|
|
|
9699
9699
|
}
|
|
9700
9700
|
}
|
|
9701
9701
|
const defaultPort = isSecure ? 443 : 80;
|
|
9702
|
-
const key =
|
|
9702
|
+
const key = randomBytes2(16).toString("base64");
|
|
9703
9703
|
const request = isSecure ? https.request : http.request;
|
|
9704
9704
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
9705
9705
|
let perMessageDeflate;
|
|
@@ -32550,7 +32550,7 @@ var Protocol = class {
|
|
|
32550
32550
|
return;
|
|
32551
32551
|
}
|
|
32552
32552
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
32553
|
-
await new Promise((
|
|
32553
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
32554
32554
|
options?.signal?.throwIfAborted();
|
|
32555
32555
|
}
|
|
32556
32556
|
} catch (error51) {
|
|
@@ -32567,7 +32567,7 @@ var Protocol = class {
|
|
|
32567
32567
|
*/
|
|
32568
32568
|
request(request, resultSchema, options) {
|
|
32569
32569
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
32570
|
-
return new Promise((
|
|
32570
|
+
return new Promise((resolve3, reject) => {
|
|
32571
32571
|
const earlyReject = (error51) => {
|
|
32572
32572
|
reject(error51);
|
|
32573
32573
|
};
|
|
@@ -32645,7 +32645,7 @@ var Protocol = class {
|
|
|
32645
32645
|
if (!parseResult.success) {
|
|
32646
32646
|
reject(parseResult.error);
|
|
32647
32647
|
} else {
|
|
32648
|
-
|
|
32648
|
+
resolve3(parseResult.data);
|
|
32649
32649
|
}
|
|
32650
32650
|
} catch (error51) {
|
|
32651
32651
|
reject(error51);
|
|
@@ -32906,12 +32906,12 @@ var Protocol = class {
|
|
|
32906
32906
|
}
|
|
32907
32907
|
} catch {
|
|
32908
32908
|
}
|
|
32909
|
-
return new Promise((
|
|
32909
|
+
return new Promise((resolve3, reject) => {
|
|
32910
32910
|
if (signal.aborted) {
|
|
32911
32911
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
32912
32912
|
return;
|
|
32913
32913
|
}
|
|
32914
|
-
const timeoutId = setTimeout(
|
|
32914
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
32915
32915
|
signal.addEventListener("abort", () => {
|
|
32916
32916
|
clearTimeout(timeoutId);
|
|
32917
32917
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -34002,7 +34002,7 @@ var McpServer = class {
|
|
|
34002
34002
|
let task = createTaskResult.task;
|
|
34003
34003
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
34004
34004
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
34005
|
-
await new Promise((
|
|
34005
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
34006
34006
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
34007
34007
|
if (!updatedTask) {
|
|
34008
34008
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -34666,64 +34666,17 @@ var StdioServerTransport = class {
|
|
|
34666
34666
|
this.onclose?.();
|
|
34667
34667
|
}
|
|
34668
34668
|
send(message) {
|
|
34669
|
-
return new Promise((
|
|
34669
|
+
return new Promise((resolve3) => {
|
|
34670
34670
|
const json2 = serializeMessage(message);
|
|
34671
34671
|
if (this._stdout.write(json2)) {
|
|
34672
|
-
|
|
34672
|
+
resolve3();
|
|
34673
34673
|
} else {
|
|
34674
|
-
this._stdout.once("drain",
|
|
34674
|
+
this._stdout.once("drain", resolve3);
|
|
34675
34675
|
}
|
|
34676
34676
|
});
|
|
34677
34677
|
}
|
|
34678
34678
|
};
|
|
34679
34679
|
|
|
34680
|
-
// node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34681
|
-
async function createMcpServer(opts) {
|
|
34682
|
-
const server = new McpServer({ name: opts.name, version: opts.version });
|
|
34683
|
-
if (opts.banner !== void 0) {
|
|
34684
|
-
console.error(opts.banner);
|
|
34685
|
-
}
|
|
34686
|
-
const deps = opts.deps;
|
|
34687
|
-
for (const register of opts.tools) {
|
|
34688
|
-
await register(server, deps);
|
|
34689
|
-
}
|
|
34690
|
-
return server;
|
|
34691
|
-
}
|
|
34692
|
-
function withGracefulShutdown(server, opts = {}) {
|
|
34693
|
-
const shouldExit = opts.exit ?? true;
|
|
34694
|
-
let shuttingDown = false;
|
|
34695
|
-
const handler = (signal) => {
|
|
34696
|
-
if (shuttingDown)
|
|
34697
|
-
return;
|
|
34698
|
-
shuttingDown = true;
|
|
34699
|
-
void (async () => {
|
|
34700
|
-
try {
|
|
34701
|
-
if (opts.onSignal)
|
|
34702
|
-
await opts.onSignal(signal);
|
|
34703
|
-
await server.close();
|
|
34704
|
-
} catch (err) {
|
|
34705
|
-
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34706
|
-
} finally {
|
|
34707
|
-
if (shouldExit)
|
|
34708
|
-
process.exit(0);
|
|
34709
|
-
}
|
|
34710
|
-
})();
|
|
34711
|
-
};
|
|
34712
|
-
process.on("SIGINT", () => handler("SIGINT"));
|
|
34713
|
-
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
34714
|
-
}
|
|
34715
|
-
async function runMcp(opts) {
|
|
34716
|
-
const server = await createMcpServer(opts);
|
|
34717
|
-
const shutdown = opts.shutdown ?? true;
|
|
34718
|
-
if (shutdown !== false) {
|
|
34719
|
-
withGracefulShutdown(server, shutdown === true ? {} : shutdown);
|
|
34720
|
-
}
|
|
34721
|
-
const spec = opts.transport ?? "stdio";
|
|
34722
|
-
const transport = spec === "stdio" ? new StdioServerTransport() : spec;
|
|
34723
|
-
await server.connect(transport);
|
|
34724
|
-
return server;
|
|
34725
|
-
}
|
|
34726
|
-
|
|
34727
34680
|
// node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
34728
34681
|
var McpToolError = class extends Error {
|
|
34729
34682
|
/** Actionable remediation text, when one applies. */
|
|
@@ -34736,6 +34689,9 @@ var McpToolError = class extends Error {
|
|
|
34736
34689
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
34737
34690
|
}
|
|
34738
34691
|
};
|
|
34692
|
+
function createHelpfulError(message, opts) {
|
|
34693
|
+
return new McpToolError(message, opts);
|
|
34694
|
+
}
|
|
34739
34695
|
var BEARER_RE = /(bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
34740
34696
|
var JWT_RE = /\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{8,}\b/g;
|
|
34741
34697
|
var BASIC_AUTH_RE = /(authorization\s*:\s*basic\s+)[A-Za-z0-9+/=_-]{6,}/gi;
|
|
@@ -34773,6 +34729,81 @@ function textResult(data) {
|
|
|
34773
34729
|
function rawTextResult(text) {
|
|
34774
34730
|
return { content: [{ type: "text", text }] };
|
|
34775
34731
|
}
|
|
34732
|
+
function errorResult(message) {
|
|
34733
|
+
return {
|
|
34734
|
+
content: [{ type: "text", text: redactSecrets(message) }],
|
|
34735
|
+
isError: true
|
|
34736
|
+
};
|
|
34737
|
+
}
|
|
34738
|
+
|
|
34739
|
+
// node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34740
|
+
function hintResultOrRethrow(err) {
|
|
34741
|
+
if (err instanceof McpToolError && err.hint) {
|
|
34742
|
+
return errorResult(`${err.message}
|
|
34743
|
+
|
|
34744
|
+
Hint: ${err.hint}`);
|
|
34745
|
+
}
|
|
34746
|
+
throw err;
|
|
34747
|
+
}
|
|
34748
|
+
function surfaceToolHints(server) {
|
|
34749
|
+
const register = server.registerTool.bind(server);
|
|
34750
|
+
server.registerTool = (name, config2, cb) => register(name, config2, (...args) => {
|
|
34751
|
+
let result;
|
|
34752
|
+
try {
|
|
34753
|
+
result = cb(...args);
|
|
34754
|
+
} catch (err) {
|
|
34755
|
+
return hintResultOrRethrow(err);
|
|
34756
|
+
}
|
|
34757
|
+
return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
|
|
34758
|
+
});
|
|
34759
|
+
}
|
|
34760
|
+
async function createMcpServer(opts) {
|
|
34761
|
+
const server = new McpServer({ name: opts.name, version: opts.version });
|
|
34762
|
+
if (opts.surfaceHints !== false)
|
|
34763
|
+
surfaceToolHints(server);
|
|
34764
|
+
if (opts.banner !== void 0) {
|
|
34765
|
+
console.error(opts.banner);
|
|
34766
|
+
}
|
|
34767
|
+
const deps = opts.deps;
|
|
34768
|
+
for (const register of opts.tools) {
|
|
34769
|
+
await register(server, deps);
|
|
34770
|
+
}
|
|
34771
|
+
return server;
|
|
34772
|
+
}
|
|
34773
|
+
function withGracefulShutdown(server, opts = {}) {
|
|
34774
|
+
const shouldExit = opts.exit ?? true;
|
|
34775
|
+
let shuttingDown = false;
|
|
34776
|
+
const handler = (signal) => {
|
|
34777
|
+
if (shuttingDown)
|
|
34778
|
+
return;
|
|
34779
|
+
shuttingDown = true;
|
|
34780
|
+
void (async () => {
|
|
34781
|
+
try {
|
|
34782
|
+
if (opts.onSignal)
|
|
34783
|
+
await opts.onSignal(signal);
|
|
34784
|
+
await server.close();
|
|
34785
|
+
} catch (err) {
|
|
34786
|
+
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34787
|
+
} finally {
|
|
34788
|
+
if (shouldExit)
|
|
34789
|
+
process.exit(0);
|
|
34790
|
+
}
|
|
34791
|
+
})();
|
|
34792
|
+
};
|
|
34793
|
+
process.on("SIGINT", () => handler("SIGINT"));
|
|
34794
|
+
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
34795
|
+
}
|
|
34796
|
+
async function runMcp(opts) {
|
|
34797
|
+
const server = await createMcpServer(opts);
|
|
34798
|
+
const shutdown = opts.shutdown ?? true;
|
|
34799
|
+
if (shutdown !== false) {
|
|
34800
|
+
withGracefulShutdown(server, shutdown === true ? {} : shutdown);
|
|
34801
|
+
}
|
|
34802
|
+
const spec = opts.transport ?? "stdio";
|
|
34803
|
+
const transport = spec === "stdio" ? new StdioServerTransport() : spec;
|
|
34804
|
+
await server.connect(transport);
|
|
34805
|
+
return server;
|
|
34806
|
+
}
|
|
34776
34807
|
|
|
34777
34808
|
// node_modules/@chrischall/mcp-utils/dist/config/index.js
|
|
34778
34809
|
import { homedir } from "node:os";
|
|
@@ -34844,6 +34875,34 @@ async function fileBlob(path, opts = {}) {
|
|
|
34844
34875
|
return blob;
|
|
34845
34876
|
}
|
|
34846
34877
|
|
|
34878
|
+
// node_modules/@chrischall/mcp-utils/dist/http/index.js
|
|
34879
|
+
var RateLimitedError = class extends Error {
|
|
34880
|
+
status = 429;
|
|
34881
|
+
constructor(service) {
|
|
34882
|
+
super(`Rate limited (429) by ${service} after retries.`);
|
|
34883
|
+
this.name = "RateLimitedError";
|
|
34884
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
34885
|
+
}
|
|
34886
|
+
};
|
|
34887
|
+
var RequestTimeoutError = class extends Error {
|
|
34888
|
+
timeoutMs;
|
|
34889
|
+
constructor(service, timeoutMs) {
|
|
34890
|
+
super(`Request to ${service} timed out after ${timeoutMs}ms.`);
|
|
34891
|
+
this.name = "RequestTimeoutError";
|
|
34892
|
+
this.timeoutMs = timeoutMs;
|
|
34893
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
34894
|
+
}
|
|
34895
|
+
};
|
|
34896
|
+
var ApiError = class extends Error {
|
|
34897
|
+
status;
|
|
34898
|
+
constructor(status, message) {
|
|
34899
|
+
super(message);
|
|
34900
|
+
this.name = "ApiError";
|
|
34901
|
+
this.status = status;
|
|
34902
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
34903
|
+
}
|
|
34904
|
+
};
|
|
34905
|
+
|
|
34847
34906
|
// node_modules/@chrischall/mcp-utils/dist/zod/parse-lenient.js
|
|
34848
34907
|
function parseLenient(schema, raw, opts) {
|
|
34849
34908
|
const result = schema.safeParse(raw);
|
|
@@ -34878,58 +34937,329 @@ var pageSchema = {
|
|
|
34878
34937
|
page_size: external_exports.number().int().min(1).max(200).default(50).describe("Number of items per page (1-200).")
|
|
34879
34938
|
};
|
|
34880
34939
|
|
|
34940
|
+
// node_modules/@chrischall/mcp-utils/dist/auth/index.js
|
|
34941
|
+
async function resolveAuthPattern(pattern) {
|
|
34942
|
+
const ordered = [
|
|
34943
|
+
pattern.token,
|
|
34944
|
+
pattern.oauth,
|
|
34945
|
+
pattern.sessionScrape,
|
|
34946
|
+
pattern.fetchproxy
|
|
34947
|
+
];
|
|
34948
|
+
for (const resolver of ordered) {
|
|
34949
|
+
if (resolver)
|
|
34950
|
+
return resolver();
|
|
34951
|
+
}
|
|
34952
|
+
throw createHelpfulError("No auth configured. Provide a token, OAuth credentials, login credentials, or sign in in your browser (fetchproxy fallback).", { hint: "Set one of the supported credential env vars, or sign in in your browser." });
|
|
34953
|
+
}
|
|
34954
|
+
|
|
34881
34955
|
// node_modules/@chrischall/mcp-utils/dist/session/index.js
|
|
34956
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, renameSync, unlinkSync } from "node:fs";
|
|
34957
|
+
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
34958
|
+
import { homedir as homedir2 } from "node:os";
|
|
34959
|
+
import { randomBytes, createHmac } from "node:crypto";
|
|
34960
|
+
function bindingDigest(secret, salt) {
|
|
34961
|
+
return createHmac("sha256", salt).update(secret).digest("hex").slice(0, 32);
|
|
34962
|
+
}
|
|
34963
|
+
function isEnvelope(raw) {
|
|
34964
|
+
return raw !== null && typeof raw === "object" && raw.v === 1 && "state" in raw;
|
|
34965
|
+
}
|
|
34966
|
+
var StatePersistenceError = class extends Error {
|
|
34967
|
+
cause;
|
|
34968
|
+
constructor(cause) {
|
|
34969
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
34970
|
+
this.name = "StatePersistenceError";
|
|
34971
|
+
this.cause = cause;
|
|
34972
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
34973
|
+
}
|
|
34974
|
+
};
|
|
34975
|
+
function createFileStatePersistence(opts) {
|
|
34976
|
+
const { filePath, validate } = opts;
|
|
34977
|
+
const secret = opts.boundTo;
|
|
34978
|
+
return {
|
|
34979
|
+
load() {
|
|
34980
|
+
if (!existsSync(filePath))
|
|
34981
|
+
return null;
|
|
34982
|
+
try {
|
|
34983
|
+
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
34984
|
+
let state;
|
|
34985
|
+
if (isEnvelope(raw)) {
|
|
34986
|
+
const b = raw.boundTo;
|
|
34987
|
+
if (secret === void 0) {
|
|
34988
|
+
if (b !== void 0)
|
|
34989
|
+
return null;
|
|
34990
|
+
} else {
|
|
34991
|
+
if (b === void 0)
|
|
34992
|
+
return null;
|
|
34993
|
+
if (bindingDigest(secret, b.salt) !== b.digest)
|
|
34994
|
+
return null;
|
|
34995
|
+
}
|
|
34996
|
+
state = raw.state;
|
|
34997
|
+
} else {
|
|
34998
|
+
if (secret !== void 0)
|
|
34999
|
+
return null;
|
|
35000
|
+
state = raw;
|
|
35001
|
+
}
|
|
35002
|
+
if (validate !== void 0)
|
|
35003
|
+
return validate(state);
|
|
35004
|
+
return state;
|
|
35005
|
+
} catch {
|
|
35006
|
+
return null;
|
|
35007
|
+
}
|
|
35008
|
+
},
|
|
35009
|
+
save(state) {
|
|
35010
|
+
const dir = dirname(filePath);
|
|
35011
|
+
const tmp = `${filePath}.tmp-${randomBytes(6).toString("hex")}`;
|
|
35012
|
+
const dirExisted = existsSync(dir);
|
|
35013
|
+
try {
|
|
35014
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
35015
|
+
if (!dirExisted)
|
|
35016
|
+
chmodSync(dir, 448);
|
|
35017
|
+
let boundTo;
|
|
35018
|
+
if (secret !== void 0) {
|
|
35019
|
+
const salt = randomBytes(16).toString("hex");
|
|
35020
|
+
boundTo = { salt, digest: bindingDigest(secret, salt) };
|
|
35021
|
+
}
|
|
35022
|
+
const envelope = { v: 1, ...boundTo !== void 0 ? { boundTo } : {}, state };
|
|
35023
|
+
writeFileSync(tmp, JSON.stringify(envelope, null, 2), { mode: 384 });
|
|
35024
|
+
chmodSync(tmp, 384);
|
|
35025
|
+
renameSync(tmp, filePath);
|
|
35026
|
+
chmodSync(filePath, 384);
|
|
35027
|
+
} catch (err) {
|
|
35028
|
+
try {
|
|
35029
|
+
if (existsSync(tmp))
|
|
35030
|
+
unlinkSync(tmp);
|
|
35031
|
+
} catch {
|
|
35032
|
+
}
|
|
35033
|
+
throw err;
|
|
35034
|
+
}
|
|
35035
|
+
},
|
|
35036
|
+
clear() {
|
|
35037
|
+
try {
|
|
35038
|
+
if (existsSync(filePath))
|
|
35039
|
+
unlinkSync(filePath);
|
|
35040
|
+
} catch {
|
|
35041
|
+
}
|
|
35042
|
+
}
|
|
35043
|
+
};
|
|
35044
|
+
}
|
|
35045
|
+
function homeOf(env) {
|
|
35046
|
+
return readEnvVar("HOME", { env }) ?? homedir2();
|
|
35047
|
+
}
|
|
35048
|
+
function resolveStateDir(opts = {}) {
|
|
35049
|
+
const env = opts.env;
|
|
35050
|
+
return join2(readEnvVar("MCP_DATA_DIR", { env }) ?? homeOf(env), opts.subdir ?? "");
|
|
35051
|
+
}
|
|
35052
|
+
function resolveStateFile(opts) {
|
|
35053
|
+
const override = opts.envVar !== void 0 ? readEnvVar(opts.envVar, { env: opts.env }) : void 0;
|
|
35054
|
+
if (override !== void 0) {
|
|
35055
|
+
if (override === "~")
|
|
35056
|
+
return homeOf(opts.env);
|
|
35057
|
+
if (override.startsWith("~/"))
|
|
35058
|
+
return join2(homeOf(opts.env), override.slice(2));
|
|
35059
|
+
return resolve2(override);
|
|
35060
|
+
}
|
|
35061
|
+
const dirOpts = { env: opts.env };
|
|
35062
|
+
if (opts.subdir !== void 0)
|
|
35063
|
+
dirOpts.subdir = opts.subdir;
|
|
35064
|
+
return join2(resolveStateDir(dirOpts), opts.fileName);
|
|
35065
|
+
}
|
|
34882
35066
|
var TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1e3;
|
|
35067
|
+
function defaultIsRefreshRevoked(err) {
|
|
35068
|
+
if (err instanceof RateLimitedError || err instanceof RequestTimeoutError)
|
|
35069
|
+
return false;
|
|
35070
|
+
if (err instanceof ApiError && err.status >= 500)
|
|
35071
|
+
return false;
|
|
35072
|
+
return true;
|
|
35073
|
+
}
|
|
35074
|
+
function isBearerTokens(raw) {
|
|
35075
|
+
if (raw === null || typeof raw !== "object")
|
|
35076
|
+
return false;
|
|
35077
|
+
const t = raw;
|
|
35078
|
+
if (typeof t.accessToken !== "string" || t.accessToken === "")
|
|
35079
|
+
return false;
|
|
35080
|
+
if (typeof t.expiresAt !== "number" || !Number.isFinite(t.expiresAt))
|
|
35081
|
+
return false;
|
|
35082
|
+
return t.refreshToken === void 0 || typeof t.refreshToken === "string";
|
|
35083
|
+
}
|
|
34883
35084
|
var TokenManager = class {
|
|
34884
|
-
|
|
34885
|
-
|
|
34886
|
-
expiresAt;
|
|
35085
|
+
tokens;
|
|
35086
|
+
bootstrapFn;
|
|
34887
35087
|
refreshFn;
|
|
34888
35088
|
skewMs;
|
|
35089
|
+
persistence;
|
|
35090
|
+
now;
|
|
35091
|
+
isRefreshRevokedFn;
|
|
35092
|
+
onPersistErrorFn;
|
|
34889
35093
|
inFlight;
|
|
35094
|
+
bootstrapInFlight;
|
|
35095
|
+
/**
|
|
35096
|
+
* Persistence is consulted at most once per process. Without this the
|
|
35097
|
+
* revoked-token recovery below re-reads the SAME rejected record — `clear()`
|
|
35098
|
+
* is optional on {@link StatePersistence} and its failures are swallowed, so
|
|
35099
|
+
* recovery must not depend on it. After the first read the in-memory tokens
|
|
35100
|
+
* (or their deliberate absence) are the truth.
|
|
35101
|
+
*/
|
|
35102
|
+
persistenceRead = false;
|
|
34890
35103
|
constructor(opts) {
|
|
34891
|
-
|
|
34892
|
-
|
|
34893
|
-
|
|
35104
|
+
if (typeof opts.initial === "function") {
|
|
35105
|
+
this.bootstrapFn = opts.initial;
|
|
35106
|
+
} else {
|
|
35107
|
+
this.tokens = { ...opts.initial };
|
|
35108
|
+
}
|
|
34894
35109
|
this.refreshFn = opts.refresh;
|
|
34895
35110
|
this.skewMs = opts.skewMs ?? TOKEN_REFRESH_SKEW_MS;
|
|
35111
|
+
this.persistence = opts.persistence;
|
|
35112
|
+
this.now = opts.now ?? Date.now;
|
|
35113
|
+
this.isRefreshRevokedFn = opts.isRefreshRevoked ?? defaultIsRefreshRevoked;
|
|
35114
|
+
this.onPersistErrorFn = opts.onPersistError;
|
|
34896
35115
|
}
|
|
34897
35116
|
/** Whether the token is within the skew window of (or past) expiry. */
|
|
34898
35117
|
needsRefresh() {
|
|
34899
|
-
|
|
35118
|
+
if (this.tokens === void 0)
|
|
35119
|
+
return false;
|
|
35120
|
+
return this.now() >= this.tokens.expiresAt - this.skewMs;
|
|
35121
|
+
}
|
|
35122
|
+
/**
|
|
35123
|
+
* A stored token is worth using when it is still valid, OR when it carries a
|
|
35124
|
+
* refresh token — an expired-but-refreshable token still saves the login,
|
|
35125
|
+
* which is the expensive half.
|
|
35126
|
+
*/
|
|
35127
|
+
isUsable(t) {
|
|
35128
|
+
return this.now() < t.expiresAt - this.skewMs || t.refreshToken !== void 0;
|
|
35129
|
+
}
|
|
35130
|
+
/** Read persisted tokens, guarding shape and usability. Never throws. */
|
|
35131
|
+
async loadPersisted() {
|
|
35132
|
+
if (this.persistence === void 0 || this.persistenceRead)
|
|
35133
|
+
return null;
|
|
35134
|
+
this.persistenceRead = true;
|
|
35135
|
+
try {
|
|
35136
|
+
const raw = await this.persistence.load();
|
|
35137
|
+
if (!isBearerTokens(raw) || !this.isUsable(raw))
|
|
35138
|
+
return null;
|
|
35139
|
+
return raw;
|
|
35140
|
+
} catch {
|
|
35141
|
+
return null;
|
|
35142
|
+
}
|
|
35143
|
+
}
|
|
35144
|
+
/**
|
|
35145
|
+
* Write tokens. Silent by default (a lost write costs a future login, not this
|
|
35146
|
+
* request); throws a {@link StatePersistenceError} when `onPersistError` does.
|
|
35147
|
+
*/
|
|
35148
|
+
async persist(t) {
|
|
35149
|
+
if (this.persistence === void 0)
|
|
35150
|
+
return;
|
|
35151
|
+
try {
|
|
35152
|
+
await this.persistence.save(t);
|
|
35153
|
+
} catch (err) {
|
|
35154
|
+
if (this.onPersistErrorFn === void 0)
|
|
35155
|
+
return;
|
|
35156
|
+
try {
|
|
35157
|
+
this.onPersistErrorFn(err);
|
|
35158
|
+
} catch (hookErr) {
|
|
35159
|
+
throw new StatePersistenceError(hookErr);
|
|
35160
|
+
}
|
|
35161
|
+
}
|
|
35162
|
+
}
|
|
35163
|
+
/** Discard persisted tokens (a refresh they could not satisfy). Never throws. */
|
|
35164
|
+
async clearPersisted() {
|
|
35165
|
+
if (this.persistence?.clear === void 0)
|
|
35166
|
+
return;
|
|
35167
|
+
try {
|
|
35168
|
+
await this.persistence.clear();
|
|
35169
|
+
} catch {
|
|
35170
|
+
}
|
|
35171
|
+
}
|
|
35172
|
+
/** The current tokens, single-flighting the bootstrap if there are none. */
|
|
35173
|
+
ensureTokens() {
|
|
35174
|
+
if (this.tokens !== void 0)
|
|
35175
|
+
return Promise.resolve(this.tokens);
|
|
35176
|
+
if (this.bootstrapInFlight === void 0) {
|
|
35177
|
+
this.bootstrapInFlight = this.runBootstrap().finally(() => {
|
|
35178
|
+
this.bootstrapInFlight = void 0;
|
|
35179
|
+
});
|
|
35180
|
+
}
|
|
35181
|
+
return this.bootstrapInFlight;
|
|
35182
|
+
}
|
|
35183
|
+
/** One bootstrap attempt: persisted tokens if usable, else the login. */
|
|
35184
|
+
async runBootstrap() {
|
|
35185
|
+
const stored = await this.loadPersisted();
|
|
35186
|
+
if (stored !== null) {
|
|
35187
|
+
this.tokens = stored;
|
|
35188
|
+
return stored;
|
|
35189
|
+
}
|
|
35190
|
+
if (this.bootstrapFn === void 0) {
|
|
35191
|
+
throw new Error("TokenManager: no tokens and no bootstrap function to mint them.");
|
|
35192
|
+
}
|
|
35193
|
+
const fresh = await this.bootstrapFn();
|
|
35194
|
+
this.tokens = fresh;
|
|
35195
|
+
try {
|
|
35196
|
+
await this.persist(fresh);
|
|
35197
|
+
} catch (err) {
|
|
35198
|
+
throw err instanceof StatePersistenceError ? err.cause : err;
|
|
35199
|
+
}
|
|
35200
|
+
return fresh;
|
|
34900
35201
|
}
|
|
34901
35202
|
/**
|
|
34902
35203
|
* Single-flight refresh. Concurrent callers share one in-flight promise; it is
|
|
34903
35204
|
* cleared on settle (success or failure) so a subsequent refresh can proceed.
|
|
34904
35205
|
*/
|
|
34905
35206
|
refreshNow() {
|
|
34906
|
-
if (
|
|
34907
|
-
|
|
34908
|
-
if (rt === void 0) {
|
|
34909
|
-
return Promise.reject(new Error("TokenManager: cannot refresh \u2014 no refresh token is available."));
|
|
34910
|
-
}
|
|
34911
|
-
this.inFlight = (async () => {
|
|
34912
|
-
const tok = await this.refreshFn(rt);
|
|
34913
|
-
this.accessToken = tok.accessToken;
|
|
34914
|
-
if (tok.refreshToken !== void 0 && tok.refreshToken !== "") {
|
|
34915
|
-
this.refreshToken = tok.refreshToken;
|
|
34916
|
-
}
|
|
34917
|
-
this.expiresAt = tok.expiresAt;
|
|
34918
|
-
})().finally(() => {
|
|
35207
|
+
if (this.inFlight === void 0) {
|
|
35208
|
+
this.inFlight = this.runRefresh().finally(() => {
|
|
34919
35209
|
this.inFlight = void 0;
|
|
34920
35210
|
});
|
|
34921
35211
|
}
|
|
34922
35212
|
return this.inFlight;
|
|
34923
35213
|
}
|
|
35214
|
+
/** One refresh attempt against the current refresh token. */
|
|
35215
|
+
async runRefresh() {
|
|
35216
|
+
const current = this.tokens ?? await this.ensureTokens();
|
|
35217
|
+
const rt = current.refreshToken;
|
|
35218
|
+
if (rt === void 0) {
|
|
35219
|
+
throw new Error("TokenManager: cannot refresh \u2014 no refresh token is available.");
|
|
35220
|
+
}
|
|
35221
|
+
const tok = await this.refreshFn(rt);
|
|
35222
|
+
this.tokens = {
|
|
35223
|
+
accessToken: tok.accessToken,
|
|
35224
|
+
// Rotation is optional: keep the current refresh token when none comes back.
|
|
35225
|
+
refreshToken: tok.refreshToken !== void 0 && tok.refreshToken !== "" ? tok.refreshToken : rt,
|
|
35226
|
+
expiresAt: tok.expiresAt
|
|
35227
|
+
};
|
|
35228
|
+
await this.persist(this.tokens);
|
|
35229
|
+
}
|
|
35230
|
+
/**
|
|
35231
|
+
* Recover from a refresh the current credential could not satisfy — commonly
|
|
35232
|
+
* a refresh token restored from a previous process and revoked since. Without
|
|
35233
|
+
* a bootstrap to fall back on this is terminal; with one, re-minting beats
|
|
35234
|
+
* staying broken forever. Shared so the two entry points cannot diverge.
|
|
35235
|
+
*/
|
|
35236
|
+
async reBootstrap(err) {
|
|
35237
|
+
if (err instanceof StatePersistenceError)
|
|
35238
|
+
throw err.cause;
|
|
35239
|
+
if (this.bootstrapFn === void 0)
|
|
35240
|
+
throw err;
|
|
35241
|
+
if (!this.isRefreshRevokedFn(err))
|
|
35242
|
+
throw err;
|
|
35243
|
+
this.tokens = void 0;
|
|
35244
|
+
await this.clearPersisted();
|
|
35245
|
+
return this.ensureTokens();
|
|
35246
|
+
}
|
|
34924
35247
|
/** Get a valid access token, refreshing proactively inside the skew window. */
|
|
34925
35248
|
async getAccessToken() {
|
|
34926
|
-
|
|
34927
|
-
|
|
34928
|
-
|
|
35249
|
+
let tokens = this.tokens ?? await this.ensureTokens();
|
|
35250
|
+
if (this.needsRefresh()) {
|
|
35251
|
+
try {
|
|
35252
|
+
await this.refreshNow();
|
|
35253
|
+
} catch (err) {
|
|
35254
|
+
return (await this.reBootstrap(err)).accessToken;
|
|
35255
|
+
}
|
|
35256
|
+
tokens = this.tokens ?? tokens;
|
|
35257
|
+
}
|
|
35258
|
+
return tokens.accessToken;
|
|
34929
35259
|
}
|
|
34930
|
-
/** Current absolute expiry (epoch ms). */
|
|
35260
|
+
/** Current absolute expiry (epoch ms), or `0` before the first bootstrap. */
|
|
34931
35261
|
getExpiresAt() {
|
|
34932
|
-
return this.expiresAt;
|
|
35262
|
+
return this.tokens?.expiresAt ?? 0;
|
|
34933
35263
|
}
|
|
34934
35264
|
/**
|
|
34935
35265
|
* Run an authenticated request with reactive 401-replay. `call` receives a
|
|
@@ -34947,16 +35277,21 @@ var TokenManager = class {
|
|
|
34947
35277
|
const usedToken = await this.getAccessToken();
|
|
34948
35278
|
let res = await call(usedToken);
|
|
34949
35279
|
if (res.status === 401) {
|
|
34950
|
-
if (this.accessToken === usedToken)
|
|
34951
|
-
|
|
34952
|
-
|
|
35280
|
+
if (this.tokens?.accessToken === usedToken) {
|
|
35281
|
+
try {
|
|
35282
|
+
await this.refreshNow();
|
|
35283
|
+
} catch (err) {
|
|
35284
|
+
await this.reBootstrap(err);
|
|
35285
|
+
}
|
|
35286
|
+
}
|
|
35287
|
+
res = await call(this.tokens?.accessToken ?? usedToken);
|
|
34953
35288
|
}
|
|
34954
35289
|
return res;
|
|
34955
35290
|
}
|
|
34956
35291
|
};
|
|
34957
35292
|
|
|
34958
35293
|
// src/client.ts
|
|
34959
|
-
import { dirname, join as
|
|
35294
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
34960
35295
|
import { fileURLToPath } from "url";
|
|
34961
35296
|
|
|
34962
35297
|
// node_modules/@fetchproxy/protocol/dist/frames.js
|
|
@@ -36133,7 +36468,7 @@ var DEFAULT_BIND_TIMEOUT_MS = 5e3;
|
|
|
36133
36468
|
async function electRole(opts) {
|
|
36134
36469
|
const server = createServer();
|
|
36135
36470
|
const bindTimeoutMs = opts.bindTimeoutMs ?? DEFAULT_BIND_TIMEOUT_MS;
|
|
36136
|
-
return new Promise((
|
|
36471
|
+
return new Promise((resolve3, reject) => {
|
|
36137
36472
|
let timer;
|
|
36138
36473
|
const clearBindTimer = () => {
|
|
36139
36474
|
if (timer) {
|
|
@@ -36149,7 +36484,7 @@ async function electRole(opts) {
|
|
|
36149
36484
|
server.close();
|
|
36150
36485
|
} catch {
|
|
36151
36486
|
}
|
|
36152
|
-
|
|
36487
|
+
resolve3({ role: "peer" });
|
|
36153
36488
|
} else {
|
|
36154
36489
|
reject(e);
|
|
36155
36490
|
}
|
|
@@ -36157,7 +36492,7 @@ async function electRole(opts) {
|
|
|
36157
36492
|
const onListening = () => {
|
|
36158
36493
|
clearBindTimer();
|
|
36159
36494
|
server.removeListener("error", onError);
|
|
36160
|
-
|
|
36495
|
+
resolve3({ role: "host", server });
|
|
36161
36496
|
};
|
|
36162
36497
|
server.once("error", onError);
|
|
36163
36498
|
server.once("listening", onListening);
|
|
@@ -36319,16 +36654,16 @@ async function awaitSessionReady(ready, opts) {
|
|
|
36319
36654
|
|
|
36320
36655
|
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
36321
36656
|
import { readFile as readFile2, writeFile as writeFile2, rename, unlink, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
|
|
36322
|
-
import { join as
|
|
36657
|
+
import { join as join4 } from "node:path";
|
|
36323
36658
|
|
|
36324
36659
|
// node_modules/@fetchproxy/server/dist/identity.js
|
|
36325
36660
|
import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
|
|
36326
|
-
import { join as
|
|
36327
|
-
import { homedir as
|
|
36661
|
+
import { join as join3 } from "node:path";
|
|
36662
|
+
import { homedir as homedir3 } from "node:os";
|
|
36328
36663
|
var SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
|
|
36329
36664
|
var SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
36330
36665
|
function defaultIdentityDir() {
|
|
36331
|
-
return
|
|
36666
|
+
return join3(homedir3(), ".fetchproxy", "identity");
|
|
36332
36667
|
}
|
|
36333
36668
|
function safeIdentityFileBase(serverName) {
|
|
36334
36669
|
if (!serverName || serverName === ".." || serverName.includes("..") || !SAFE_PLAIN.test(serverName) && !SAFE_SCOPED.test(serverName)) {
|
|
@@ -36338,7 +36673,7 @@ function safeIdentityFileBase(serverName) {
|
|
|
36338
36673
|
}
|
|
36339
36674
|
async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
36340
36675
|
const safeFile = safeIdentityFileBase(serverName);
|
|
36341
|
-
const path =
|
|
36676
|
+
const path = join3(dir, `${safeFile}.json`);
|
|
36342
36677
|
await mkdir(dir, { recursive: true, mode: 448 });
|
|
36343
36678
|
try {
|
|
36344
36679
|
const raw = await readFile(path, "utf8");
|
|
@@ -36410,13 +36745,13 @@ function decideExtensionTrust(args) {
|
|
|
36410
36745
|
};
|
|
36411
36746
|
}
|
|
36412
36747
|
function extensionTrustPath(serverName, dir = defaultIdentityDir()) {
|
|
36413
|
-
return
|
|
36748
|
+
return join4(dir, `${safeIdentityFileBase(serverName)}.extension-trust.json`);
|
|
36414
36749
|
}
|
|
36415
36750
|
function extensionTrustPathHint(serverName) {
|
|
36416
36751
|
try {
|
|
36417
36752
|
return extensionTrustPath(serverName);
|
|
36418
36753
|
} catch {
|
|
36419
|
-
return
|
|
36754
|
+
return join4(defaultIdentityDir(), "<server-name>.extension-trust.json");
|
|
36420
36755
|
}
|
|
36421
36756
|
}
|
|
36422
36757
|
function isPin(x) {
|
|
@@ -36503,8 +36838,8 @@ async function startHost(opts) {
|
|
|
36503
36838
|
let rejectOwnSession;
|
|
36504
36839
|
let ownSessionReady;
|
|
36505
36840
|
function resetSessionPromise() {
|
|
36506
|
-
ownSessionReady = new Promise((
|
|
36507
|
-
resolveOwnSession =
|
|
36841
|
+
ownSessionReady = new Promise((resolve3, reject) => {
|
|
36842
|
+
resolveOwnSession = resolve3;
|
|
36508
36843
|
rejectOwnSession = reject;
|
|
36509
36844
|
});
|
|
36510
36845
|
ownSessionReady.catch(() => {
|
|
@@ -36725,7 +37060,7 @@ async function startHost(opts) {
|
|
|
36725
37060
|
});
|
|
36726
37061
|
});
|
|
36727
37062
|
return {
|
|
36728
|
-
close: () => new Promise((
|
|
37063
|
+
close: () => new Promise((resolve3) => {
|
|
36729
37064
|
for (const client2 of wss.clients) {
|
|
36730
37065
|
try {
|
|
36731
37066
|
client2.terminate();
|
|
@@ -36733,7 +37068,7 @@ async function startHost(opts) {
|
|
|
36733
37068
|
}
|
|
36734
37069
|
}
|
|
36735
37070
|
wss.close(() => {
|
|
36736
|
-
opts.httpServer.close(() =>
|
|
37071
|
+
opts.httpServer.close(() => resolve3());
|
|
36737
37072
|
});
|
|
36738
37073
|
}),
|
|
36739
37074
|
sendOwnInner: async (inner) => {
|
|
@@ -36763,8 +37098,8 @@ async function startHost(opts) {
|
|
|
36763
37098
|
var enc3 = new TextEncoder();
|
|
36764
37099
|
async function startPeer(opts) {
|
|
36765
37100
|
const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`);
|
|
36766
|
-
await new Promise((
|
|
36767
|
-
ws.once("open", () =>
|
|
37101
|
+
await new Promise((resolve3, reject) => {
|
|
37102
|
+
ws.once("open", () => resolve3());
|
|
36768
37103
|
ws.once("error", reject);
|
|
36769
37104
|
});
|
|
36770
37105
|
const hello = await buildServerHello({
|
|
@@ -36794,8 +37129,8 @@ async function startPeer(opts) {
|
|
|
36794
37129
|
let pendingPairCode = null;
|
|
36795
37130
|
let resolveFirstReady;
|
|
36796
37131
|
let rejectFirstReady;
|
|
36797
|
-
const sessionPromise = new Promise((
|
|
36798
|
-
resolveFirstReady =
|
|
37132
|
+
const sessionPromise = new Promise((resolve3, reject) => {
|
|
37133
|
+
resolveFirstReady = resolve3;
|
|
36799
37134
|
rejectFirstReady = reject;
|
|
36800
37135
|
});
|
|
36801
37136
|
let extensionHello = null;
|
|
@@ -36996,6 +37331,7 @@ function classifyBridgeError(err) {
|
|
|
36996
37331
|
}
|
|
36997
37332
|
|
|
36998
37333
|
// node_modules/@fetchproxy/server/dist/ws-server.js
|
|
37334
|
+
import { isIP } from "node:net";
|
|
36999
37335
|
function envWsPort() {
|
|
37000
37336
|
const raw = process.env.FETCHPROXY_WS_PORT;
|
|
37001
37337
|
if (raw === void 0 || raw.trim() === "")
|
|
@@ -37007,6 +37343,15 @@ function envWsPort() {
|
|
|
37007
37343
|
return void 0;
|
|
37008
37344
|
return port;
|
|
37009
37345
|
}
|
|
37346
|
+
function envWsHost() {
|
|
37347
|
+
const raw = process.env.FETCHPROXY_WS_HOST;
|
|
37348
|
+
if (raw === void 0)
|
|
37349
|
+
return void 0;
|
|
37350
|
+
const host = raw.trim();
|
|
37351
|
+
if (host === "" || isIP(host) === 0)
|
|
37352
|
+
return void 0;
|
|
37353
|
+
return host;
|
|
37354
|
+
}
|
|
37010
37355
|
var FetchproxyProtocolError = class extends Error {
|
|
37011
37356
|
constructor(message) {
|
|
37012
37357
|
super(message);
|
|
@@ -37255,7 +37600,7 @@ var FetchproxyServer = class {
|
|
|
37255
37600
|
}
|
|
37256
37601
|
this.opts = {
|
|
37257
37602
|
port: opts.port ?? envWsPort() ?? 37149,
|
|
37258
|
-
host: opts.host ?? "127.0.0.1",
|
|
37603
|
+
host: opts.host ?? envWsHost() ?? "127.0.0.1",
|
|
37259
37604
|
serverName: opts.serverName,
|
|
37260
37605
|
version: opts.version,
|
|
37261
37606
|
domains: [...opts.domains],
|
|
@@ -37541,6 +37886,7 @@ var FetchproxyServer = class {
|
|
|
37541
37886
|
return {
|
|
37542
37887
|
role: this.role,
|
|
37543
37888
|
port: this.opts.port,
|
|
37889
|
+
host: this.opts.host,
|
|
37544
37890
|
serverVersion: this.opts.version,
|
|
37545
37891
|
fetchTimeoutMs: this.opts.fetchTimeoutMs ?? 0,
|
|
37546
37892
|
bridgeReviveDelayMs: this.opts.bridgeReviveDelayMs ?? 0,
|
|
@@ -37684,8 +38030,8 @@ var FetchproxyServer = class {
|
|
|
37684
38030
|
async _fetchOnceWithTimeout(init) {
|
|
37685
38031
|
const id = this.nextRequestId++;
|
|
37686
38032
|
const inner = { type: "request", id, op: "fetch", init };
|
|
37687
|
-
const pending = new Promise((
|
|
37688
|
-
this.pending.set(id,
|
|
38033
|
+
const pending = new Promise((resolve3) => {
|
|
38034
|
+
this.pending.set(id, resolve3);
|
|
37689
38035
|
});
|
|
37690
38036
|
await this.sendInnerFrame(inner);
|
|
37691
38037
|
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
@@ -37696,12 +38042,12 @@ var FetchproxyServer = class {
|
|
|
37696
38042
|
try {
|
|
37697
38043
|
return await Promise.race([
|
|
37698
38044
|
pending,
|
|
37699
|
-
new Promise((
|
|
38045
|
+
new Promise((resolve3) => {
|
|
37700
38046
|
timer = setTimeout(() => {
|
|
37701
38047
|
this.pending.delete(id);
|
|
37702
38048
|
const elapsedMs = Date.now() - start;
|
|
37703
38049
|
const error51 = `fetchproxy: ${init.url} did not respond within ${timeoutMs}ms`;
|
|
37704
|
-
|
|
38050
|
+
resolve3({
|
|
37705
38051
|
ok: false,
|
|
37706
38052
|
error: error51,
|
|
37707
38053
|
kind: "timeout",
|
|
@@ -38063,8 +38409,8 @@ var FetchproxyServer = class {
|
|
|
38063
38409
|
const tabUrl = `https://${host}/`;
|
|
38064
38410
|
inner = { type: "request", id, op: "read_cookies", init: { tabUrl } };
|
|
38065
38411
|
}
|
|
38066
|
-
const pending = new Promise((
|
|
38067
|
-
this.pendingReadCookies.set(id,
|
|
38412
|
+
const pending = new Promise((resolve3) => {
|
|
38413
|
+
this.pendingReadCookies.set(id, resolve3);
|
|
38068
38414
|
});
|
|
38069
38415
|
await this.sendInnerFrame(inner);
|
|
38070
38416
|
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
@@ -38122,8 +38468,8 @@ var FetchproxyServer = class {
|
|
|
38122
38468
|
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
38123
38469
|
}
|
|
38124
38470
|
};
|
|
38125
|
-
const pending = new Promise((
|
|
38126
|
-
this.pendingWriteCookies.set(id, { resolve:
|
|
38471
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38472
|
+
this.pendingWriteCookies.set(id, { resolve: resolve3, reject });
|
|
38127
38473
|
});
|
|
38128
38474
|
await this.sendInnerFrame(inner);
|
|
38129
38475
|
return this._withVerbTimeout(pending, this.pendingWriteCookies, id, `https://${host}`);
|
|
@@ -38185,8 +38531,8 @@ var FetchproxyServer = class {
|
|
|
38185
38531
|
...opts.pointers ? { pointers: { ...opts.pointers } } : {}
|
|
38186
38532
|
}
|
|
38187
38533
|
};
|
|
38188
|
-
const pending = new Promise((
|
|
38189
|
-
this.pendingStorage.set(id, { resolve:
|
|
38534
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38535
|
+
this.pendingStorage.set(id, { resolve: resolve3, reject });
|
|
38190
38536
|
});
|
|
38191
38537
|
await this.sendInnerFrame(inner);
|
|
38192
38538
|
return this._withVerbTimeout(pending, this.pendingStorage, id, `https://${host}`);
|
|
@@ -38290,8 +38636,8 @@ var FetchproxyServer = class {
|
|
|
38290
38636
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38291
38637
|
}
|
|
38292
38638
|
};
|
|
38293
|
-
const pending = new Promise((
|
|
38294
|
-
this.pendingCapture.set(id, { resolve:
|
|
38639
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38640
|
+
this.pendingCapture.set(id, { resolve: resolve3, reject });
|
|
38295
38641
|
});
|
|
38296
38642
|
await this.sendInnerFrame(inner);
|
|
38297
38643
|
return this._withVerbTimeout(pending, this.pendingCapture, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
@@ -38375,8 +38721,8 @@ var FetchproxyServer = class {
|
|
|
38375
38721
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38376
38722
|
}
|
|
38377
38723
|
};
|
|
38378
|
-
const pending = new Promise((
|
|
38379
|
-
this.pendingRedirect.set(id, { resolve:
|
|
38724
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38725
|
+
this.pendingRedirect.set(id, { resolve: resolve3, reject });
|
|
38380
38726
|
});
|
|
38381
38727
|
await this.sendInnerFrame(inner);
|
|
38382
38728
|
return this._withVerbTimeout(pending, this.pendingRedirect, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
@@ -38457,8 +38803,8 @@ var FetchproxyServer = class {
|
|
|
38457
38803
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38458
38804
|
}
|
|
38459
38805
|
};
|
|
38460
|
-
const pending = new Promise((
|
|
38461
|
-
this.pendingDownload.set(id, { resolve:
|
|
38806
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38807
|
+
this.pendingDownload.set(id, { resolve: resolve3, reject });
|
|
38462
38808
|
});
|
|
38463
38809
|
await this.sendInnerFrame(inner);
|
|
38464
38810
|
return this._withVerbTimeout(pending, this.pendingDownload, id, opts.url);
|
|
@@ -38505,8 +38851,8 @@ var FetchproxyServer = class {
|
|
|
38505
38851
|
keys: [...opts.keys]
|
|
38506
38852
|
}
|
|
38507
38853
|
};
|
|
38508
|
-
const pending = new Promise((
|
|
38509
|
-
this.pendingIdb.set(id, { resolve:
|
|
38854
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38855
|
+
this.pendingIdb.set(id, { resolve: resolve3, reject });
|
|
38510
38856
|
});
|
|
38511
38857
|
await this.sendInnerFrame(inner);
|
|
38512
38858
|
return this._withVerbTimeout(pending, this.pendingIdb, id, origin);
|
|
@@ -38545,8 +38891,8 @@ var FetchproxyServer = class {
|
|
|
38545
38891
|
op: "read_dom",
|
|
38546
38892
|
init: { origin, names: [...opts.names] }
|
|
38547
38893
|
};
|
|
38548
|
-
const pending = new Promise((
|
|
38549
|
-
this.pendingStorage.set(id, { resolve:
|
|
38894
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38895
|
+
this.pendingStorage.set(id, { resolve: resolve3, reject });
|
|
38550
38896
|
});
|
|
38551
38897
|
await this.sendInnerFrame(inner);
|
|
38552
38898
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
@@ -38591,8 +38937,8 @@ var FetchproxyServer = class {
|
|
|
38591
38937
|
...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
|
|
38592
38938
|
}
|
|
38593
38939
|
};
|
|
38594
|
-
const pending = new Promise((
|
|
38595
|
-
this.pendingGraphql.set(id, { resolve:
|
|
38940
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38941
|
+
this.pendingGraphql.set(id, { resolve: resolve3, reject });
|
|
38596
38942
|
});
|
|
38597
38943
|
await this.sendInnerFrame(inner);
|
|
38598
38944
|
return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name);
|
|
@@ -39085,7 +39431,7 @@ async function loginWithPassword(username, password) {
|
|
|
39085
39431
|
// package.json
|
|
39086
39432
|
var package_default = {
|
|
39087
39433
|
name: "ofw-mcp",
|
|
39088
|
-
version: "2.
|
|
39434
|
+
version: "2.13.0",
|
|
39089
39435
|
license: "MIT",
|
|
39090
39436
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
39091
39437
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
@@ -39106,19 +39452,21 @@ var package_default = {
|
|
|
39106
39452
|
".claude-plugin",
|
|
39107
39453
|
"skills",
|
|
39108
39454
|
".mcp.json",
|
|
39109
|
-
"server.json"
|
|
39455
|
+
"server.json",
|
|
39456
|
+
"mint.yaml"
|
|
39110
39457
|
],
|
|
39111
39458
|
scripts: {
|
|
39112
39459
|
build: "tsc && npm run bundle",
|
|
39113
39460
|
bundle: `esbuild src/index.ts --bundle --platform=node --format=esm --external:dotenv --banner:js='import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);' --outfile=dist/bundle.js`,
|
|
39114
39461
|
dev: "node --env-file=.env dist/index.js",
|
|
39115
|
-
test: "vitest run",
|
|
39116
|
-
"test:coverage": "vitest run --coverage",
|
|
39117
|
-
"test:watch": "vitest"
|
|
39462
|
+
test: "npm run typecheck && vitest run",
|
|
39463
|
+
"test:coverage": "npm run typecheck && vitest run --coverage",
|
|
39464
|
+
"test:watch": "vitest",
|
|
39465
|
+
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
39118
39466
|
},
|
|
39119
39467
|
dependencies: {
|
|
39120
|
-
"@chrischall/mcp-utils": "^0.
|
|
39121
|
-
"@fetchproxy/bootstrap": "^2.
|
|
39468
|
+
"@chrischall/mcp-utils": "^0.18.0",
|
|
39469
|
+
"@fetchproxy/bootstrap": "^2.2.0",
|
|
39122
39470
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39123
39471
|
dotenv: "^17.4.2",
|
|
39124
39472
|
zod: "^4.4.3"
|
|
@@ -39137,66 +39485,118 @@ function fetchproxyDisabled() {
|
|
|
39137
39485
|
return parseBoolEnv("OFW_DISABLE_FETCHPROXY");
|
|
39138
39486
|
}
|
|
39139
39487
|
async function resolveAuth() {
|
|
39488
|
+
const pattern = {};
|
|
39140
39489
|
const username = readEnvVar("OFW_USERNAME");
|
|
39141
39490
|
const password = readEnvVar("OFW_PASSWORD");
|
|
39142
39491
|
if (username && password) {
|
|
39143
|
-
|
|
39144
|
-
|
|
39492
|
+
pattern.sessionScrape = async () => {
|
|
39493
|
+
const { token, expiresAt: expiresAt2 } = await loginWithPassword(username, password);
|
|
39494
|
+
return { credential: token, source: "env", expiresAt: expiresAt2 };
|
|
39495
|
+
};
|
|
39145
39496
|
}
|
|
39146
39497
|
if (!fetchproxyDisabled()) {
|
|
39147
|
-
|
|
39148
|
-
|
|
39149
|
-
|
|
39150
|
-
|
|
39151
|
-
|
|
39152
|
-
|
|
39153
|
-
|
|
39154
|
-
|
|
39155
|
-
|
|
39156
|
-
|
|
39157
|
-
|
|
39158
|
-
|
|
39159
|
-
|
|
39160
|
-
|
|
39161
|
-
|
|
39162
|
-
|
|
39163
|
-
|
|
39498
|
+
pattern.fetchproxy = async () => {
|
|
39499
|
+
try {
|
|
39500
|
+
const session = await bootstrap({
|
|
39501
|
+
serverName: package_default.name,
|
|
39502
|
+
version: package_default.version,
|
|
39503
|
+
// OFW serves both ofw.ourfamilywizard.com and www.ourfamilywizard.com;
|
|
39504
|
+
// the API + auth token live on the apex. The extension matches on
|
|
39505
|
+
// suffix, so listing the apex covers both.
|
|
39506
|
+
domains: ["ourfamilywizard.com"],
|
|
39507
|
+
declare: {
|
|
39508
|
+
cookies: [],
|
|
39509
|
+
// The web app stores the Bearer token in localStorage["auth"] and
|
|
39510
|
+
// its expiry (ISO string) in localStorage["tokenExpiry"]. Mirroring
|
|
39511
|
+
// both means our 401-replay logic can be slightly smarter, and the
|
|
39512
|
+
// expiry surfaces correctly in diagnostics.
|
|
39513
|
+
localStorage: ["auth", "tokenExpiry"],
|
|
39514
|
+
sessionStorage: [],
|
|
39515
|
+
captureHeaders: []
|
|
39516
|
+
}
|
|
39517
|
+
});
|
|
39518
|
+
const token = session.localStorage["auth"];
|
|
39519
|
+
const expiryRaw = session.localStorage["tokenExpiry"];
|
|
39520
|
+
if (!token) {
|
|
39521
|
+
throw new Error(
|
|
39522
|
+
'localStorage["auth"] missing on ourfamilywizard.com. Sign into OFW in your browser (with the fetchproxy extension installed) and retry.'
|
|
39523
|
+
);
|
|
39164
39524
|
}
|
|
39165
|
-
|
|
39166
|
-
|
|
39167
|
-
|
|
39168
|
-
|
|
39169
|
-
|
|
39170
|
-
|
|
39171
|
-
)
|
|
39172
|
-
|
|
39173
|
-
|
|
39174
|
-
|
|
39175
|
-
|
|
39176
|
-
|
|
39177
|
-
|
|
39178
|
-
} catch (e) {
|
|
39179
|
-
if (classifyBridgeError(e) === "bridge_down") {
|
|
39180
|
-
const downErr = e;
|
|
39525
|
+
return {
|
|
39526
|
+
credential: token,
|
|
39527
|
+
source: "fetchproxy",
|
|
39528
|
+
...expiryRaw ? { expiresAt: new Date(expiryRaw) } : {}
|
|
39529
|
+
};
|
|
39530
|
+
} catch (e) {
|
|
39531
|
+
if (classifyBridgeError(e) === "bridge_down") {
|
|
39532
|
+
const downErr = e;
|
|
39533
|
+
throw new Error(
|
|
39534
|
+
`OFW auth: fetchproxy bridge is down (extension service worker unreachable after retry). ${downErr.hint}`
|
|
39535
|
+
);
|
|
39536
|
+
}
|
|
39537
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
39181
39538
|
throw new Error(
|
|
39182
|
-
`OFW auth:
|
|
39539
|
+
`OFW auth: no OFW_USERNAME/OFW_PASSWORD set, and fetchproxy fallback failed: ${msg}`
|
|
39183
39540
|
);
|
|
39184
39541
|
}
|
|
39185
|
-
|
|
39186
|
-
|
|
39187
|
-
|
|
39188
|
-
|
|
39189
|
-
|
|
39542
|
+
};
|
|
39543
|
+
}
|
|
39544
|
+
if (!pattern.sessionScrape && !pattern.fetchproxy) {
|
|
39545
|
+
throw new Error(
|
|
39546
|
+
"OFW auth: set OFW_USERNAME + OFW_PASSWORD, or install the fetchproxy extension and sign into ourfamilywizard.com (unset OFW_DISABLE_FETCHPROXY if it is set)."
|
|
39547
|
+
);
|
|
39190
39548
|
}
|
|
39191
|
-
|
|
39192
|
-
|
|
39549
|
+
const { credential, source, expiresAt } = await resolveAuthPattern(pattern);
|
|
39550
|
+
return {
|
|
39551
|
+
token: credential,
|
|
39552
|
+
// The pattern's `source` is a free-form string; ours is a two-value union,
|
|
39553
|
+
// and both resolvers above set it to a member of that union.
|
|
39554
|
+
source,
|
|
39555
|
+
...expiresAt ? { expiresAt } : {}
|
|
39556
|
+
};
|
|
39557
|
+
}
|
|
39558
|
+
|
|
39559
|
+
// src/session-cache.ts
|
|
39560
|
+
function sessionCachePath(env = process.env) {
|
|
39561
|
+
return resolveStateFile({
|
|
39562
|
+
env,
|
|
39563
|
+
envVar: "OFW_SESSION_FILE",
|
|
39564
|
+
subdir: ".ofw-mcp",
|
|
39565
|
+
fileName: "session.json"
|
|
39566
|
+
});
|
|
39567
|
+
}
|
|
39568
|
+
function isTokens(raw) {
|
|
39569
|
+
if (raw === null || typeof raw !== "object") return false;
|
|
39570
|
+
const t = raw;
|
|
39571
|
+
return typeof t.accessToken === "string" && t.accessToken !== "" && typeof t.expiresAt === "number" && (t.refreshToken === void 0 || typeof t.refreshToken === "string");
|
|
39572
|
+
}
|
|
39573
|
+
function createSessionCache(opts = {}) {
|
|
39574
|
+
const env = opts.env ?? process.env;
|
|
39575
|
+
if (opts.injectedResolver === true) return null;
|
|
39576
|
+
if (!parseBoolEnv("OFW_SESSION_CACHE", { env, default: true })) return null;
|
|
39577
|
+
const username = readEnvVar("OFW_USERNAME", { env });
|
|
39578
|
+
const password = readEnvVar("OFW_PASSWORD", { env });
|
|
39579
|
+
if (username === void 0 || password === void 0) return null;
|
|
39580
|
+
return createFileStatePersistence({
|
|
39581
|
+
filePath: sessionCachePath(env),
|
|
39582
|
+
// Joined on a NUL, written as an escape rather than a literal byte: a
|
|
39583
|
+
// password may contain spaces, so a space-joined pair could collide with
|
|
39584
|
+
// a different pair by shifting the boundary between the two halves.
|
|
39585
|
+
boundTo: [username.trim().toLowerCase(), password].join("\0"),
|
|
39586
|
+
validate: (raw) => isTokens(raw) ? raw : null
|
|
39587
|
+
});
|
|
39588
|
+
}
|
|
39589
|
+
function reportCacheWriteFailure(err) {
|
|
39590
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
39591
|
+
console.error(
|
|
39592
|
+
`[ofw-mcp] could not cache the session token (${detail}); continuing without the cache \u2014 every restart will re-authenticate until this is fixed.`
|
|
39193
39593
|
);
|
|
39194
39594
|
}
|
|
39195
39595
|
|
|
39196
39596
|
// src/client.ts
|
|
39197
39597
|
try {
|
|
39198
|
-
const dir =
|
|
39199
|
-
await loadDotenvSafely({ path:
|
|
39598
|
+
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
39599
|
+
await loadDotenvSafely({ path: join5(dir, "..", ".env") });
|
|
39200
39600
|
} catch {
|
|
39201
39601
|
}
|
|
39202
39602
|
function parseContentDispositionFilename(cd) {
|
|
@@ -39227,9 +39627,9 @@ var OFWClient = class {
|
|
|
39227
39627
|
// Bearer-token lifecycle is delegated to the shared, race-safe TokenManager
|
|
39228
39628
|
// (proactive refresh inside the skew window, single-flight refresh so a burst
|
|
39229
39629
|
// of concurrent callers coalesces onto ONE `resolveAuth()`, and a 401-replay
|
|
39230
|
-
// guarded against double-refresh). It is created lazily,
|
|
39231
|
-
//
|
|
39232
|
-
//
|
|
39630
|
+
// guarded against double-refresh). It is created lazily, and mints on first
|
|
39631
|
+
// use through the function form of `initial` — see `mint` below for why that
|
|
39632
|
+
// form rather than a seeded placeholder.
|
|
39233
39633
|
tokenManager;
|
|
39234
39634
|
// Optional injected auth resolver. When set, the refresh callback uses it
|
|
39235
39635
|
// instead of the module-level global `resolveAuth` (env-var → fetchproxy
|
|
@@ -39243,22 +39643,28 @@ var OFWClient = class {
|
|
|
39243
39643
|
}
|
|
39244
39644
|
getTokenManager() {
|
|
39245
39645
|
if (!this.tokenManager) {
|
|
39646
|
+
const mint = async () => {
|
|
39647
|
+
const { token, expiresAt } = await (this.authResolver ?? resolveAuth)();
|
|
39648
|
+
return {
|
|
39649
|
+
accessToken: token,
|
|
39650
|
+
refreshToken: OFW_REFRESH_SENTINEL,
|
|
39651
|
+
expiresAt: (expiresAt ?? new Date(Date.now() + OFW_TOKEN_TTL_MS)).getTime()
|
|
39652
|
+
};
|
|
39653
|
+
};
|
|
39246
39654
|
this.tokenManager = new TokenManager({
|
|
39247
|
-
initial:
|
|
39655
|
+
initial: mint,
|
|
39656
|
+
persistence: createSessionCache({ injectedResolver: this.authResolver !== void 0 }) ?? void 0,
|
|
39657
|
+
onPersistError: reportCacheWriteFailure,
|
|
39658
|
+
// A failed renewal IS a failed login here, so the library's
|
|
39659
|
+
// re-mint-on-revoked recovery would just repeat the call that failed.
|
|
39660
|
+
isRefreshRevoked: () => false,
|
|
39248
39661
|
skewMs: OFW_TOKEN_EXPIRY_SKEW_MS,
|
|
39249
39662
|
// Map OFW's mint/refresh onto the refresh callback. `resolveAuth()`
|
|
39250
39663
|
// returns a token and a best-effort expiry; when the fetchproxy path
|
|
39251
39664
|
// can't supply one we fall back to the same 6h estimate the password
|
|
39252
39665
|
// path uses (the 401-replay covers a wrong guess). We re-arm the
|
|
39253
39666
|
// sentinel so the manager can refresh again later.
|
|
39254
|
-
refresh:
|
|
39255
|
-
const { token, expiresAt } = await (this.authResolver ?? resolveAuth)();
|
|
39256
|
-
return {
|
|
39257
|
-
accessToken: token,
|
|
39258
|
-
refreshToken: OFW_REFRESH_SENTINEL,
|
|
39259
|
-
expiresAt: (expiresAt ?? new Date(Date.now() + OFW_TOKEN_TTL_MS)).getTime()
|
|
39260
|
-
};
|
|
39261
|
-
}
|
|
39667
|
+
refresh: mint
|
|
39262
39668
|
});
|
|
39263
39669
|
}
|
|
39264
39670
|
return this.tokenManager;
|
|
@@ -40082,25 +40488,25 @@ async function syncAll(client2, opts, store) {
|
|
|
40082
40488
|
|
|
40083
40489
|
// src/config.ts
|
|
40084
40490
|
import { createHash } from "node:crypto";
|
|
40085
|
-
import { homedir as
|
|
40086
|
-
import { join as
|
|
40491
|
+
import { homedir as homedir4 } from "node:os";
|
|
40492
|
+
import { join as join6 } from "node:path";
|
|
40087
40493
|
function readCacheIdentity() {
|
|
40088
40494
|
return readEnvVar("OFW_CACHE_IDENTITY") ?? readEnvVar("OFW_USERNAME") ?? "_default";
|
|
40089
40495
|
}
|
|
40090
40496
|
function getCacheDir() {
|
|
40091
40497
|
const override = process.env.OFW_CACHE_DIR;
|
|
40092
40498
|
if (override && override.trim().length > 0) return override.trim();
|
|
40093
|
-
return
|
|
40499
|
+
return join6(homedir4(), ".cache", "ofw-mcp");
|
|
40094
40500
|
}
|
|
40095
40501
|
function getCacheDbPath() {
|
|
40096
40502
|
const identity = readCacheIdentity();
|
|
40097
40503
|
const hash2 = createHash("sha256").update(identity).digest("hex").slice(0, 16);
|
|
40098
|
-
return
|
|
40504
|
+
return join6(getCacheDir(), `${hash2}.db`);
|
|
40099
40505
|
}
|
|
40100
40506
|
function getAttachmentsDir() {
|
|
40101
40507
|
const override = process.env.OFW_ATTACHMENTS_DIR;
|
|
40102
40508
|
if (override && override.trim().length > 0) return override.trim();
|
|
40103
|
-
return
|
|
40509
|
+
return join6(homedir4(), "Downloads", "ofw-mcp");
|
|
40104
40510
|
}
|
|
40105
40511
|
function getWriteMode() {
|
|
40106
40512
|
const raw = process.env.OFW_WRITE_MODE;
|
|
@@ -41470,8 +41876,8 @@ async function extractAttachment(bytes, mimeType, fileName, opts = {}) {
|
|
|
41470
41876
|
}
|
|
41471
41877
|
|
|
41472
41878
|
// src/tools/attachments.ts
|
|
41473
|
-
import { readFileSync, statSync, mkdirSync, writeFileSync } from "node:fs";
|
|
41474
|
-
import { basename, dirname as
|
|
41879
|
+
import { readFileSync as readFileSync2, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
41880
|
+
import { basename, dirname as dirname3, extname } from "node:path";
|
|
41475
41881
|
var MIME_BY_EXT = {
|
|
41476
41882
|
".pdf": "application/pdf",
|
|
41477
41883
|
".png": "image/png",
|
|
@@ -41545,14 +41951,14 @@ var NodeAttachmentIO = class {
|
|
|
41545
41951
|
}
|
|
41546
41952
|
readDownloaded(path) {
|
|
41547
41953
|
try {
|
|
41548
|
-
return
|
|
41954
|
+
return readFileSync2(path);
|
|
41549
41955
|
} catch {
|
|
41550
41956
|
return null;
|
|
41551
41957
|
}
|
|
41552
41958
|
}
|
|
41553
41959
|
writeDownload(dest, bytes) {
|
|
41554
|
-
|
|
41555
|
-
|
|
41960
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
41961
|
+
writeFileSync2(dest, bytes);
|
|
41556
41962
|
}
|
|
41557
41963
|
};
|
|
41558
41964
|
|
|
@@ -41616,7 +42022,7 @@ async function buildInlineDelivery(input) {
|
|
|
41616
42022
|
}
|
|
41617
42023
|
|
|
41618
42024
|
// src/tools/messages.ts
|
|
41619
|
-
import { basename as basename2, join as
|
|
42025
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
41620
42026
|
|
|
41621
42027
|
// src/tools/pagination.ts
|
|
41622
42028
|
function pageState(input) {
|
|
@@ -42711,9 +43117,9 @@ ${text}` : text);
|
|
|
42711
43117
|
if (args.saveTo) {
|
|
42712
43118
|
const isDirArg = args.saveTo.endsWith("/") || args.saveTo.endsWith("\\");
|
|
42713
43119
|
const abs = expandPath2(args.saveTo);
|
|
42714
|
-
dest = isDirArg ?
|
|
43120
|
+
dest = isDirArg ? join7(abs, `${fileId}-${safeName}`) : abs;
|
|
42715
43121
|
} else {
|
|
42716
|
-
dest =
|
|
43122
|
+
dest = join7(getAttachmentsDir(), `${fileId}-${safeName}`);
|
|
42717
43123
|
}
|
|
42718
43124
|
const extractOnDisk = args.extract === true;
|
|
42719
43125
|
if (!args.force && cached2.downloadedPath === dest) {
|
|
@@ -43209,8 +43615,8 @@ function registerJournalTools(server, client2) {
|
|
|
43209
43615
|
|
|
43210
43616
|
// src/cache/node.ts
|
|
43211
43617
|
import { DatabaseSync } from "node:sqlite";
|
|
43212
|
-
import { mkdirSync as
|
|
43213
|
-
import { dirname as
|
|
43618
|
+
import { mkdirSync as mkdirSync3, chmodSync as chmodSync2, existsSync as existsSync2 } from "node:fs";
|
|
43619
|
+
import { dirname as dirname4 } from "node:path";
|
|
43214
43620
|
|
|
43215
43621
|
// src/cache/store.ts
|
|
43216
43622
|
function rowFromDb(r) {
|
|
@@ -43794,10 +44200,10 @@ var NodeSqlDriver = class {
|
|
|
43794
44200
|
}
|
|
43795
44201
|
};
|
|
43796
44202
|
function enforceCachePermissions(dbPath) {
|
|
43797
|
-
|
|
43798
|
-
|
|
44203
|
+
chmodSync2(dirname4(dbPath), 448);
|
|
44204
|
+
chmodSync2(dbPath, 384);
|
|
43799
44205
|
for (const sibling of [`${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
43800
|
-
if (
|
|
44206
|
+
if (existsSync2(sibling)) chmodSync2(sibling, 384);
|
|
43801
44207
|
}
|
|
43802
44208
|
}
|
|
43803
44209
|
var OFWCache = class _OFWCache extends LocalCacheStore {
|
|
@@ -43808,7 +44214,7 @@ var OFWCache = class _OFWCache extends LocalCacheStore {
|
|
|
43808
44214
|
db;
|
|
43809
44215
|
static open(path) {
|
|
43810
44216
|
const memory = path === ":memory:";
|
|
43811
|
-
if (!memory)
|
|
44217
|
+
if (!memory) mkdirSync3(dirname4(path), { recursive: true });
|
|
43812
44218
|
const db = new DatabaseSync(path);
|
|
43813
44219
|
if (!memory) enforceCachePermissions(path);
|
|
43814
44220
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -43838,7 +44244,7 @@ var nodeCacheProvider = () => nodeCache ??= OFWCache.open(getCacheDbPath());
|
|
|
43838
44244
|
var nodeAttachmentIO = new NodeAttachmentIO();
|
|
43839
44245
|
await runMcp({
|
|
43840
44246
|
name: "ofw",
|
|
43841
|
-
version: "2.
|
|
44247
|
+
version: "2.13.0",
|
|
43842
44248
|
// x-release-please-version
|
|
43843
44249
|
deps: client,
|
|
43844
44250
|
tools: [
|