ofw-mcp 2.11.0 → 2.12.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 +609 -214
- 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 +8 -6
- 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;
|
|
@@ -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);
|
|
@@ -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,
|
|
@@ -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;
|
|
@@ -37684,8 +38019,8 @@ var FetchproxyServer = class {
|
|
|
37684
38019
|
async _fetchOnceWithTimeout(init) {
|
|
37685
38020
|
const id = this.nextRequestId++;
|
|
37686
38021
|
const inner = { type: "request", id, op: "fetch", init };
|
|
37687
|
-
const pending = new Promise((
|
|
37688
|
-
this.pending.set(id,
|
|
38022
|
+
const pending = new Promise((resolve3) => {
|
|
38023
|
+
this.pending.set(id, resolve3);
|
|
37689
38024
|
});
|
|
37690
38025
|
await this.sendInnerFrame(inner);
|
|
37691
38026
|
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
@@ -37696,12 +38031,12 @@ var FetchproxyServer = class {
|
|
|
37696
38031
|
try {
|
|
37697
38032
|
return await Promise.race([
|
|
37698
38033
|
pending,
|
|
37699
|
-
new Promise((
|
|
38034
|
+
new Promise((resolve3) => {
|
|
37700
38035
|
timer = setTimeout(() => {
|
|
37701
38036
|
this.pending.delete(id);
|
|
37702
38037
|
const elapsedMs = Date.now() - start;
|
|
37703
38038
|
const error51 = `fetchproxy: ${init.url} did not respond within ${timeoutMs}ms`;
|
|
37704
|
-
|
|
38039
|
+
resolve3({
|
|
37705
38040
|
ok: false,
|
|
37706
38041
|
error: error51,
|
|
37707
38042
|
kind: "timeout",
|
|
@@ -38063,8 +38398,8 @@ var FetchproxyServer = class {
|
|
|
38063
38398
|
const tabUrl = `https://${host}/`;
|
|
38064
38399
|
inner = { type: "request", id, op: "read_cookies", init: { tabUrl } };
|
|
38065
38400
|
}
|
|
38066
|
-
const pending = new Promise((
|
|
38067
|
-
this.pendingReadCookies.set(id,
|
|
38401
|
+
const pending = new Promise((resolve3) => {
|
|
38402
|
+
this.pendingReadCookies.set(id, resolve3);
|
|
38068
38403
|
});
|
|
38069
38404
|
await this.sendInnerFrame(inner);
|
|
38070
38405
|
const result = await this._withVerbTimeout(pending, this.pendingReadCookies, id, `https://${host}`);
|
|
@@ -38122,8 +38457,8 @@ var FetchproxyServer = class {
|
|
|
38122
38457
|
...cookiePath !== void 0 ? { path: cookiePath } : {}
|
|
38123
38458
|
}
|
|
38124
38459
|
};
|
|
38125
|
-
const pending = new Promise((
|
|
38126
|
-
this.pendingWriteCookies.set(id, { resolve:
|
|
38460
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38461
|
+
this.pendingWriteCookies.set(id, { resolve: resolve3, reject });
|
|
38127
38462
|
});
|
|
38128
38463
|
await this.sendInnerFrame(inner);
|
|
38129
38464
|
return this._withVerbTimeout(pending, this.pendingWriteCookies, id, `https://${host}`);
|
|
@@ -38185,8 +38520,8 @@ var FetchproxyServer = class {
|
|
|
38185
38520
|
...opts.pointers ? { pointers: { ...opts.pointers } } : {}
|
|
38186
38521
|
}
|
|
38187
38522
|
};
|
|
38188
|
-
const pending = new Promise((
|
|
38189
|
-
this.pendingStorage.set(id, { resolve:
|
|
38523
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38524
|
+
this.pendingStorage.set(id, { resolve: resolve3, reject });
|
|
38190
38525
|
});
|
|
38191
38526
|
await this.sendInnerFrame(inner);
|
|
38192
38527
|
return this._withVerbTimeout(pending, this.pendingStorage, id, `https://${host}`);
|
|
@@ -38290,8 +38625,8 @@ var FetchproxyServer = class {
|
|
|
38290
38625
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38291
38626
|
}
|
|
38292
38627
|
};
|
|
38293
|
-
const pending = new Promise((
|
|
38294
|
-
this.pendingCapture.set(id, { resolve:
|
|
38628
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38629
|
+
this.pendingCapture.set(id, { resolve: resolve3, reject });
|
|
38295
38630
|
});
|
|
38296
38631
|
await this.sendInnerFrame(inner);
|
|
38297
38632
|
return this._withVerbTimeout(pending, this.pendingCapture, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
@@ -38375,8 +38710,8 @@ var FetchproxyServer = class {
|
|
|
38375
38710
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38376
38711
|
}
|
|
38377
38712
|
};
|
|
38378
|
-
const pending = new Promise((
|
|
38379
|
-
this.pendingRedirect.set(id, { resolve:
|
|
38713
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38714
|
+
this.pendingRedirect.set(id, { resolve: resolve3, reject });
|
|
38380
38715
|
});
|
|
38381
38716
|
await this.sendInnerFrame(inner);
|
|
38382
38717
|
return this._withVerbTimeout(pending, this.pendingRedirect, id, `https://${opts.host}${opts.path ?? "/*"}`);
|
|
@@ -38457,8 +38792,8 @@ var FetchproxyServer = class {
|
|
|
38457
38792
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
38458
38793
|
}
|
|
38459
38794
|
};
|
|
38460
|
-
const pending = new Promise((
|
|
38461
|
-
this.pendingDownload.set(id, { resolve:
|
|
38795
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38796
|
+
this.pendingDownload.set(id, { resolve: resolve3, reject });
|
|
38462
38797
|
});
|
|
38463
38798
|
await this.sendInnerFrame(inner);
|
|
38464
38799
|
return this._withVerbTimeout(pending, this.pendingDownload, id, opts.url);
|
|
@@ -38505,8 +38840,8 @@ var FetchproxyServer = class {
|
|
|
38505
38840
|
keys: [...opts.keys]
|
|
38506
38841
|
}
|
|
38507
38842
|
};
|
|
38508
|
-
const pending = new Promise((
|
|
38509
|
-
this.pendingIdb.set(id, { resolve:
|
|
38843
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38844
|
+
this.pendingIdb.set(id, { resolve: resolve3, reject });
|
|
38510
38845
|
});
|
|
38511
38846
|
await this.sendInnerFrame(inner);
|
|
38512
38847
|
return this._withVerbTimeout(pending, this.pendingIdb, id, origin);
|
|
@@ -38545,8 +38880,8 @@ var FetchproxyServer = class {
|
|
|
38545
38880
|
op: "read_dom",
|
|
38546
38881
|
init: { origin, names: [...opts.names] }
|
|
38547
38882
|
};
|
|
38548
|
-
const pending = new Promise((
|
|
38549
|
-
this.pendingStorage.set(id, { resolve:
|
|
38883
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38884
|
+
this.pendingStorage.set(id, { resolve: resolve3, reject });
|
|
38550
38885
|
});
|
|
38551
38886
|
await this.sendInnerFrame(inner);
|
|
38552
38887
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
@@ -38591,8 +38926,8 @@ var FetchproxyServer = class {
|
|
|
38591
38926
|
...opts.tabUrl !== void 0 ? { tabUrl: opts.tabUrl } : {}
|
|
38592
38927
|
}
|
|
38593
38928
|
};
|
|
38594
|
-
const pending = new Promise((
|
|
38595
|
-
this.pendingGraphql.set(id, { resolve:
|
|
38929
|
+
const pending = new Promise((resolve3, reject) => {
|
|
38930
|
+
this.pendingGraphql.set(id, { resolve: resolve3, reject });
|
|
38596
38931
|
});
|
|
38597
38932
|
await this.sendInnerFrame(inner);
|
|
38598
38933
|
return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name);
|
|
@@ -39085,7 +39420,7 @@ async function loginWithPassword(username, password) {
|
|
|
39085
39420
|
// package.json
|
|
39086
39421
|
var package_default = {
|
|
39087
39422
|
name: "ofw-mcp",
|
|
39088
|
-
version: "2.
|
|
39423
|
+
version: "2.12.0",
|
|
39089
39424
|
license: "MIT",
|
|
39090
39425
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
39091
39426
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
@@ -39106,18 +39441,20 @@ var package_default = {
|
|
|
39106
39441
|
".claude-plugin",
|
|
39107
39442
|
"skills",
|
|
39108
39443
|
".mcp.json",
|
|
39109
|
-
"server.json"
|
|
39444
|
+
"server.json",
|
|
39445
|
+
"mint.yaml"
|
|
39110
39446
|
],
|
|
39111
39447
|
scripts: {
|
|
39112
39448
|
build: "tsc && npm run bundle",
|
|
39113
39449
|
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
39450
|
dev: "node --env-file=.env dist/index.js",
|
|
39115
|
-
test: "vitest run",
|
|
39116
|
-
"test:coverage": "vitest run --coverage",
|
|
39117
|
-
"test:watch": "vitest"
|
|
39451
|
+
test: "npm run typecheck && vitest run",
|
|
39452
|
+
"test:coverage": "npm run typecheck && vitest run --coverage",
|
|
39453
|
+
"test:watch": "vitest",
|
|
39454
|
+
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
39118
39455
|
},
|
|
39119
39456
|
dependencies: {
|
|
39120
|
-
"@chrischall/mcp-utils": "^0.
|
|
39457
|
+
"@chrischall/mcp-utils": "^0.18.0",
|
|
39121
39458
|
"@fetchproxy/bootstrap": "^2.0.0",
|
|
39122
39459
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39123
39460
|
dotenv: "^17.4.2",
|
|
@@ -39137,66 +39474,118 @@ function fetchproxyDisabled() {
|
|
|
39137
39474
|
return parseBoolEnv("OFW_DISABLE_FETCHPROXY");
|
|
39138
39475
|
}
|
|
39139
39476
|
async function resolveAuth() {
|
|
39477
|
+
const pattern = {};
|
|
39140
39478
|
const username = readEnvVar("OFW_USERNAME");
|
|
39141
39479
|
const password = readEnvVar("OFW_PASSWORD");
|
|
39142
39480
|
if (username && password) {
|
|
39143
|
-
|
|
39144
|
-
|
|
39481
|
+
pattern.sessionScrape = async () => {
|
|
39482
|
+
const { token, expiresAt: expiresAt2 } = await loginWithPassword(username, password);
|
|
39483
|
+
return { credential: token, source: "env", expiresAt: expiresAt2 };
|
|
39484
|
+
};
|
|
39145
39485
|
}
|
|
39146
39486
|
if (!fetchproxyDisabled()) {
|
|
39147
|
-
|
|
39148
|
-
|
|
39149
|
-
|
|
39150
|
-
|
|
39151
|
-
|
|
39152
|
-
|
|
39153
|
-
|
|
39154
|
-
|
|
39155
|
-
|
|
39156
|
-
|
|
39157
|
-
|
|
39158
|
-
|
|
39159
|
-
|
|
39160
|
-
|
|
39161
|
-
|
|
39162
|
-
|
|
39163
|
-
|
|
39487
|
+
pattern.fetchproxy = async () => {
|
|
39488
|
+
try {
|
|
39489
|
+
const session = await bootstrap({
|
|
39490
|
+
serverName: package_default.name,
|
|
39491
|
+
version: package_default.version,
|
|
39492
|
+
// OFW serves both ofw.ourfamilywizard.com and www.ourfamilywizard.com;
|
|
39493
|
+
// the API + auth token live on the apex. The extension matches on
|
|
39494
|
+
// suffix, so listing the apex covers both.
|
|
39495
|
+
domains: ["ourfamilywizard.com"],
|
|
39496
|
+
declare: {
|
|
39497
|
+
cookies: [],
|
|
39498
|
+
// The web app stores the Bearer token in localStorage["auth"] and
|
|
39499
|
+
// its expiry (ISO string) in localStorage["tokenExpiry"]. Mirroring
|
|
39500
|
+
// both means our 401-replay logic can be slightly smarter, and the
|
|
39501
|
+
// expiry surfaces correctly in diagnostics.
|
|
39502
|
+
localStorage: ["auth", "tokenExpiry"],
|
|
39503
|
+
sessionStorage: [],
|
|
39504
|
+
captureHeaders: []
|
|
39505
|
+
}
|
|
39506
|
+
});
|
|
39507
|
+
const token = session.localStorage["auth"];
|
|
39508
|
+
const expiryRaw = session.localStorage["tokenExpiry"];
|
|
39509
|
+
if (!token) {
|
|
39510
|
+
throw new Error(
|
|
39511
|
+
'localStorage["auth"] missing on ourfamilywizard.com. Sign into OFW in your browser (with the fetchproxy extension installed) and retry.'
|
|
39512
|
+
);
|
|
39164
39513
|
}
|
|
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;
|
|
39514
|
+
return {
|
|
39515
|
+
credential: token,
|
|
39516
|
+
source: "fetchproxy",
|
|
39517
|
+
...expiryRaw ? { expiresAt: new Date(expiryRaw) } : {}
|
|
39518
|
+
};
|
|
39519
|
+
} catch (e) {
|
|
39520
|
+
if (classifyBridgeError(e) === "bridge_down") {
|
|
39521
|
+
const downErr = e;
|
|
39522
|
+
throw new Error(
|
|
39523
|
+
`OFW auth: fetchproxy bridge is down (extension service worker unreachable after retry). ${downErr.hint}`
|
|
39524
|
+
);
|
|
39525
|
+
}
|
|
39526
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
39181
39527
|
throw new Error(
|
|
39182
|
-
`OFW auth:
|
|
39528
|
+
`OFW auth: no OFW_USERNAME/OFW_PASSWORD set, and fetchproxy fallback failed: ${msg}`
|
|
39183
39529
|
);
|
|
39184
39530
|
}
|
|
39185
|
-
|
|
39186
|
-
|
|
39187
|
-
|
|
39188
|
-
|
|
39189
|
-
|
|
39531
|
+
};
|
|
39532
|
+
}
|
|
39533
|
+
if (!pattern.sessionScrape && !pattern.fetchproxy) {
|
|
39534
|
+
throw new Error(
|
|
39535
|
+
"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)."
|
|
39536
|
+
);
|
|
39190
39537
|
}
|
|
39191
|
-
|
|
39192
|
-
|
|
39538
|
+
const { credential, source, expiresAt } = await resolveAuthPattern(pattern);
|
|
39539
|
+
return {
|
|
39540
|
+
token: credential,
|
|
39541
|
+
// The pattern's `source` is a free-form string; ours is a two-value union,
|
|
39542
|
+
// and both resolvers above set it to a member of that union.
|
|
39543
|
+
source,
|
|
39544
|
+
...expiresAt ? { expiresAt } : {}
|
|
39545
|
+
};
|
|
39546
|
+
}
|
|
39547
|
+
|
|
39548
|
+
// src/session-cache.ts
|
|
39549
|
+
function sessionCachePath(env = process.env) {
|
|
39550
|
+
return resolveStateFile({
|
|
39551
|
+
env,
|
|
39552
|
+
envVar: "OFW_SESSION_FILE",
|
|
39553
|
+
subdir: ".ofw-mcp",
|
|
39554
|
+
fileName: "session.json"
|
|
39555
|
+
});
|
|
39556
|
+
}
|
|
39557
|
+
function isTokens(raw) {
|
|
39558
|
+
if (raw === null || typeof raw !== "object") return false;
|
|
39559
|
+
const t = raw;
|
|
39560
|
+
return typeof t.accessToken === "string" && t.accessToken !== "" && typeof t.expiresAt === "number" && (t.refreshToken === void 0 || typeof t.refreshToken === "string");
|
|
39561
|
+
}
|
|
39562
|
+
function createSessionCache(opts = {}) {
|
|
39563
|
+
const env = opts.env ?? process.env;
|
|
39564
|
+
if (opts.injectedResolver === true) return null;
|
|
39565
|
+
if (!parseBoolEnv("OFW_SESSION_CACHE", { env, default: true })) return null;
|
|
39566
|
+
const username = readEnvVar("OFW_USERNAME", { env });
|
|
39567
|
+
const password = readEnvVar("OFW_PASSWORD", { env });
|
|
39568
|
+
if (username === void 0 || password === void 0) return null;
|
|
39569
|
+
return createFileStatePersistence({
|
|
39570
|
+
filePath: sessionCachePath(env),
|
|
39571
|
+
// Joined on a NUL, written as an escape rather than a literal byte: a
|
|
39572
|
+
// password may contain spaces, so a space-joined pair could collide with
|
|
39573
|
+
// a different pair by shifting the boundary between the two halves.
|
|
39574
|
+
boundTo: [username.trim().toLowerCase(), password].join("\0"),
|
|
39575
|
+
validate: (raw) => isTokens(raw) ? raw : null
|
|
39576
|
+
});
|
|
39577
|
+
}
|
|
39578
|
+
function reportCacheWriteFailure(err) {
|
|
39579
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
39580
|
+
console.error(
|
|
39581
|
+
`[ofw-mcp] could not cache the session token (${detail}); continuing without the cache \u2014 every restart will re-authenticate until this is fixed.`
|
|
39193
39582
|
);
|
|
39194
39583
|
}
|
|
39195
39584
|
|
|
39196
39585
|
// src/client.ts
|
|
39197
39586
|
try {
|
|
39198
|
-
const dir =
|
|
39199
|
-
await loadDotenvSafely({ path:
|
|
39587
|
+
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
39588
|
+
await loadDotenvSafely({ path: join5(dir, "..", ".env") });
|
|
39200
39589
|
} catch {
|
|
39201
39590
|
}
|
|
39202
39591
|
function parseContentDispositionFilename(cd) {
|
|
@@ -39227,9 +39616,9 @@ var OFWClient = class {
|
|
|
39227
39616
|
// Bearer-token lifecycle is delegated to the shared, race-safe TokenManager
|
|
39228
39617
|
// (proactive refresh inside the skew window, single-flight refresh so a burst
|
|
39229
39618
|
// of concurrent callers coalesces onto ONE `resolveAuth()`, and a 401-replay
|
|
39230
|
-
// guarded against double-refresh). It is created lazily,
|
|
39231
|
-
//
|
|
39232
|
-
//
|
|
39619
|
+
// guarded against double-refresh). It is created lazily, and mints on first
|
|
39620
|
+
// use through the function form of `initial` — see `mint` below for why that
|
|
39621
|
+
// form rather than a seeded placeholder.
|
|
39233
39622
|
tokenManager;
|
|
39234
39623
|
// Optional injected auth resolver. When set, the refresh callback uses it
|
|
39235
39624
|
// instead of the module-level global `resolveAuth` (env-var → fetchproxy
|
|
@@ -39243,22 +39632,28 @@ var OFWClient = class {
|
|
|
39243
39632
|
}
|
|
39244
39633
|
getTokenManager() {
|
|
39245
39634
|
if (!this.tokenManager) {
|
|
39635
|
+
const mint = async () => {
|
|
39636
|
+
const { token, expiresAt } = await (this.authResolver ?? resolveAuth)();
|
|
39637
|
+
return {
|
|
39638
|
+
accessToken: token,
|
|
39639
|
+
refreshToken: OFW_REFRESH_SENTINEL,
|
|
39640
|
+
expiresAt: (expiresAt ?? new Date(Date.now() + OFW_TOKEN_TTL_MS)).getTime()
|
|
39641
|
+
};
|
|
39642
|
+
};
|
|
39246
39643
|
this.tokenManager = new TokenManager({
|
|
39247
|
-
initial:
|
|
39644
|
+
initial: mint,
|
|
39645
|
+
persistence: createSessionCache({ injectedResolver: this.authResolver !== void 0 }) ?? void 0,
|
|
39646
|
+
onPersistError: reportCacheWriteFailure,
|
|
39647
|
+
// A failed renewal IS a failed login here, so the library's
|
|
39648
|
+
// re-mint-on-revoked recovery would just repeat the call that failed.
|
|
39649
|
+
isRefreshRevoked: () => false,
|
|
39248
39650
|
skewMs: OFW_TOKEN_EXPIRY_SKEW_MS,
|
|
39249
39651
|
// Map OFW's mint/refresh onto the refresh callback. `resolveAuth()`
|
|
39250
39652
|
// returns a token and a best-effort expiry; when the fetchproxy path
|
|
39251
39653
|
// can't supply one we fall back to the same 6h estimate the password
|
|
39252
39654
|
// path uses (the 401-replay covers a wrong guess). We re-arm the
|
|
39253
39655
|
// 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
|
-
}
|
|
39656
|
+
refresh: mint
|
|
39262
39657
|
});
|
|
39263
39658
|
}
|
|
39264
39659
|
return this.tokenManager;
|
|
@@ -40082,25 +40477,25 @@ async function syncAll(client2, opts, store) {
|
|
|
40082
40477
|
|
|
40083
40478
|
// src/config.ts
|
|
40084
40479
|
import { createHash } from "node:crypto";
|
|
40085
|
-
import { homedir as
|
|
40086
|
-
import { join as
|
|
40480
|
+
import { homedir as homedir4 } from "node:os";
|
|
40481
|
+
import { join as join6 } from "node:path";
|
|
40087
40482
|
function readCacheIdentity() {
|
|
40088
40483
|
return readEnvVar("OFW_CACHE_IDENTITY") ?? readEnvVar("OFW_USERNAME") ?? "_default";
|
|
40089
40484
|
}
|
|
40090
40485
|
function getCacheDir() {
|
|
40091
40486
|
const override = process.env.OFW_CACHE_DIR;
|
|
40092
40487
|
if (override && override.trim().length > 0) return override.trim();
|
|
40093
|
-
return
|
|
40488
|
+
return join6(homedir4(), ".cache", "ofw-mcp");
|
|
40094
40489
|
}
|
|
40095
40490
|
function getCacheDbPath() {
|
|
40096
40491
|
const identity = readCacheIdentity();
|
|
40097
40492
|
const hash2 = createHash("sha256").update(identity).digest("hex").slice(0, 16);
|
|
40098
|
-
return
|
|
40493
|
+
return join6(getCacheDir(), `${hash2}.db`);
|
|
40099
40494
|
}
|
|
40100
40495
|
function getAttachmentsDir() {
|
|
40101
40496
|
const override = process.env.OFW_ATTACHMENTS_DIR;
|
|
40102
40497
|
if (override && override.trim().length > 0) return override.trim();
|
|
40103
|
-
return
|
|
40498
|
+
return join6(homedir4(), "Downloads", "ofw-mcp");
|
|
40104
40499
|
}
|
|
40105
40500
|
function getWriteMode() {
|
|
40106
40501
|
const raw = process.env.OFW_WRITE_MODE;
|
|
@@ -41470,8 +41865,8 @@ async function extractAttachment(bytes, mimeType, fileName, opts = {}) {
|
|
|
41470
41865
|
}
|
|
41471
41866
|
|
|
41472
41867
|
// src/tools/attachments.ts
|
|
41473
|
-
import { readFileSync, statSync, mkdirSync, writeFileSync } from "node:fs";
|
|
41474
|
-
import { basename, dirname as
|
|
41868
|
+
import { readFileSync as readFileSync2, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
41869
|
+
import { basename, dirname as dirname3, extname } from "node:path";
|
|
41475
41870
|
var MIME_BY_EXT = {
|
|
41476
41871
|
".pdf": "application/pdf",
|
|
41477
41872
|
".png": "image/png",
|
|
@@ -41545,14 +41940,14 @@ var NodeAttachmentIO = class {
|
|
|
41545
41940
|
}
|
|
41546
41941
|
readDownloaded(path) {
|
|
41547
41942
|
try {
|
|
41548
|
-
return
|
|
41943
|
+
return readFileSync2(path);
|
|
41549
41944
|
} catch {
|
|
41550
41945
|
return null;
|
|
41551
41946
|
}
|
|
41552
41947
|
}
|
|
41553
41948
|
writeDownload(dest, bytes) {
|
|
41554
|
-
|
|
41555
|
-
|
|
41949
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
41950
|
+
writeFileSync2(dest, bytes);
|
|
41556
41951
|
}
|
|
41557
41952
|
};
|
|
41558
41953
|
|
|
@@ -41616,7 +42011,7 @@ async function buildInlineDelivery(input) {
|
|
|
41616
42011
|
}
|
|
41617
42012
|
|
|
41618
42013
|
// src/tools/messages.ts
|
|
41619
|
-
import { basename as basename2, join as
|
|
42014
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
41620
42015
|
|
|
41621
42016
|
// src/tools/pagination.ts
|
|
41622
42017
|
function pageState(input) {
|
|
@@ -42711,9 +43106,9 @@ ${text}` : text);
|
|
|
42711
43106
|
if (args.saveTo) {
|
|
42712
43107
|
const isDirArg = args.saveTo.endsWith("/") || args.saveTo.endsWith("\\");
|
|
42713
43108
|
const abs = expandPath2(args.saveTo);
|
|
42714
|
-
dest = isDirArg ?
|
|
43109
|
+
dest = isDirArg ? join7(abs, `${fileId}-${safeName}`) : abs;
|
|
42715
43110
|
} else {
|
|
42716
|
-
dest =
|
|
43111
|
+
dest = join7(getAttachmentsDir(), `${fileId}-${safeName}`);
|
|
42717
43112
|
}
|
|
42718
43113
|
const extractOnDisk = args.extract === true;
|
|
42719
43114
|
if (!args.force && cached2.downloadedPath === dest) {
|
|
@@ -43209,8 +43604,8 @@ function registerJournalTools(server, client2) {
|
|
|
43209
43604
|
|
|
43210
43605
|
// src/cache/node.ts
|
|
43211
43606
|
import { DatabaseSync } from "node:sqlite";
|
|
43212
|
-
import { mkdirSync as
|
|
43213
|
-
import { dirname as
|
|
43607
|
+
import { mkdirSync as mkdirSync3, chmodSync as chmodSync2, existsSync as existsSync2 } from "node:fs";
|
|
43608
|
+
import { dirname as dirname4 } from "node:path";
|
|
43214
43609
|
|
|
43215
43610
|
// src/cache/store.ts
|
|
43216
43611
|
function rowFromDb(r) {
|
|
@@ -43794,10 +44189,10 @@ var NodeSqlDriver = class {
|
|
|
43794
44189
|
}
|
|
43795
44190
|
};
|
|
43796
44191
|
function enforceCachePermissions(dbPath) {
|
|
43797
|
-
|
|
43798
|
-
|
|
44192
|
+
chmodSync2(dirname4(dbPath), 448);
|
|
44193
|
+
chmodSync2(dbPath, 384);
|
|
43799
44194
|
for (const sibling of [`${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
43800
|
-
if (
|
|
44195
|
+
if (existsSync2(sibling)) chmodSync2(sibling, 384);
|
|
43801
44196
|
}
|
|
43802
44197
|
}
|
|
43803
44198
|
var OFWCache = class _OFWCache extends LocalCacheStore {
|
|
@@ -43808,7 +44203,7 @@ var OFWCache = class _OFWCache extends LocalCacheStore {
|
|
|
43808
44203
|
db;
|
|
43809
44204
|
static open(path) {
|
|
43810
44205
|
const memory = path === ":memory:";
|
|
43811
|
-
if (!memory)
|
|
44206
|
+
if (!memory) mkdirSync3(dirname4(path), { recursive: true });
|
|
43812
44207
|
const db = new DatabaseSync(path);
|
|
43813
44208
|
if (!memory) enforceCachePermissions(path);
|
|
43814
44209
|
db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -43838,7 +44233,7 @@ var nodeCacheProvider = () => nodeCache ??= OFWCache.open(getCacheDbPath());
|
|
|
43838
44233
|
var nodeAttachmentIO = new NodeAttachmentIO();
|
|
43839
44234
|
await runMcp({
|
|
43840
44235
|
name: "ofw",
|
|
43841
|
-
version: "2.
|
|
44236
|
+
version: "2.12.0",
|
|
43842
44237
|
// x-release-please-version
|
|
43843
44238
|
deps: client,
|
|
43844
44239
|
tools: [
|