@chrischall/office-outlook-mcp 0.1.4 → 0.2.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/dist/bundle.js +1080 -341
- package/dist/client.js +25 -7
- package/dist/tools/_paging.js +32 -0
- package/dist/tools/calendar.js +9 -9
- package/dist/tools/directory.js +11 -7
- package/dist/tools/mail.js +17 -14
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -50,8 +50,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
50
50
|
|
|
51
51
|
// node_modules/@chrischall/mcp-utils/dist/cancel/index.js
|
|
52
52
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
53
|
-
function withCallSignal(signal, fn) {
|
|
54
|
-
return signal ? storage.run({ signal }, fn) : fn();
|
|
53
|
+
function withCallSignal(signal, fn, request) {
|
|
54
|
+
return signal ? storage.run({ signal, ...request ? { request } : {} }, fn) : fn();
|
|
55
55
|
}
|
|
56
56
|
function currentCallSignal() {
|
|
57
57
|
return storage.getStore()?.signal;
|
|
@@ -71,6 +71,18 @@ var init_cancel = __esm({
|
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
// node_modules/@chrischall/mcp-utils/dist/caller/index.js
|
|
75
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
76
|
+
function withCallerCapabilities(capabilities, fn) {
|
|
77
|
+
return capabilities ? storage2.run(capabilities, fn) : fn();
|
|
78
|
+
}
|
|
79
|
+
var storage2;
|
|
80
|
+
var init_caller = __esm({
|
|
81
|
+
"node_modules/@chrischall/mcp-utils/dist/caller/index.js"() {
|
|
82
|
+
storage2 = new AsyncLocalStorage2();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
74
86
|
// node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
|
|
75
87
|
var __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __commonJSMin, __exportAll, __copyProps2, __toESM2;
|
|
76
88
|
var init_chunk_Br0eD_fh = __esm({
|
|
@@ -35139,7 +35151,7 @@ var init_dist = __esm({
|
|
|
35139
35151
|
|
|
35140
35152
|
// node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
35141
35153
|
function redactSecrets(text) {
|
|
35142
|
-
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
|
|
35154
|
+
return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
|
|
35143
35155
|
}
|
|
35144
35156
|
function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
35145
35157
|
const str = text === null || text === void 0 ? "" : String(text);
|
|
@@ -35153,7 +35165,7 @@ function messageOf(err) {
|
|
|
35153
35165
|
return err.message;
|
|
35154
35166
|
return String(err);
|
|
35155
35167
|
}
|
|
35156
|
-
var DEFAULT_ERROR_MESSAGE_MAX, McpToolError, BEARER_RE, JWT_RE, BASIC_AUTH_RE, SET_COOKIE_RE, COOKIE_HEADER_RE, API_KEY_RE, QUERY_SECRET_RE, AWS_SIGV4_RE, JSON_SECRET_KEYS, JSON_SECRET_DQ_RE, JSON_SECRET_SQ_RE;
|
|
35168
|
+
var DEFAULT_ERROR_MESSAGE_MAX, McpToolError, BEARER_RE, JWT_RE, BASIC_AUTH_RE, SET_COOKIE_RE, COOKIE_HEADER_RE, API_KEY_RE, QUERY_SECRET_RE, OAUTH_CODE_RE, FORM_PASSWORD_RE, HEADER_SECRET_RE, AWS_SIGV4_RE, JSON_SECRET_KEYS, JSON_SECRET_DQ_RE, JSON_SECRET_SQ_RE;
|
|
35157
35169
|
var init_errors3 = __esm({
|
|
35158
35170
|
"node_modules/@chrischall/mcp-utils/dist/errors/index.js"() {
|
|
35159
35171
|
DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
@@ -35187,11 +35199,26 @@ var init_errors3 = __esm({
|
|
|
35187
35199
|
"whsec_[A-Za-z0-9]{16,}\\b"
|
|
35188
35200
|
// webhook signing secret (Stripe-style)
|
|
35189
35201
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
35190
|
-
QUERY_SECRET_RE = /([?&](?:
|
|
35202
|
+
QUERY_SECRET_RE = /([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
|
|
35203
|
+
OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
35204
|
+
FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
35205
|
+
HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
|
|
35191
35206
|
AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
|
|
35192
|
-
JSON_SECRET_KEYS =
|
|
35193
|
-
|
|
35194
|
-
|
|
35207
|
+
JSON_SECRET_KEYS = [
|
|
35208
|
+
"(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
|
|
35209
|
+
"client[_-]?secret",
|
|
35210
|
+
"api[_-]?secret",
|
|
35211
|
+
"(?:x[_-])?api[_-]?key",
|
|
35212
|
+
"private[_-]?key",
|
|
35213
|
+
"x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
|
|
35214
|
+
"(?:proxy-)?authorization",
|
|
35215
|
+
"password",
|
|
35216
|
+
"passwd",
|
|
35217
|
+
"secret",
|
|
35218
|
+
"token"
|
|
35219
|
+
].join("|");
|
|
35220
|
+
JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
|
|
35221
|
+
JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
|
|
35195
35222
|
}
|
|
35196
35223
|
});
|
|
35197
35224
|
|
|
@@ -35280,6 +35307,7 @@ var init_response = __esm({
|
|
|
35280
35307
|
var init_confirmation = __esm({
|
|
35281
35308
|
"node_modules/@chrischall/mcp-utils/dist/server/confirmation.js"() {
|
|
35282
35309
|
init_dist();
|
|
35310
|
+
init_caller();
|
|
35283
35311
|
init_response();
|
|
35284
35312
|
}
|
|
35285
35313
|
});
|
|
@@ -35293,6 +35321,15 @@ Hint: ${err.hint}`);
|
|
|
35293
35321
|
}
|
|
35294
35322
|
throw err;
|
|
35295
35323
|
}
|
|
35324
|
+
function mcpRequestFrom(args) {
|
|
35325
|
+
const ctx = args.at(-1);
|
|
35326
|
+
if (typeof ctx !== "object" || ctx === null)
|
|
35327
|
+
return void 0;
|
|
35328
|
+
const req = ctx.mcpReq;
|
|
35329
|
+
if (typeof req !== "object" || req === null)
|
|
35330
|
+
return void 0;
|
|
35331
|
+
return typeof req.notify === "function" ? req : void 0;
|
|
35332
|
+
}
|
|
35296
35333
|
function callSignalFrom(args) {
|
|
35297
35334
|
const ctx = args.at(-1);
|
|
35298
35335
|
if (typeof ctx !== "object" || ctx === null)
|
|
@@ -35303,6 +35340,20 @@ function callSignalFrom(args) {
|
|
|
35303
35340
|
const signal = req.signal;
|
|
35304
35341
|
return signal instanceof AbortSignal ? signal : void 0;
|
|
35305
35342
|
}
|
|
35343
|
+
function declaredCapabilitiesFrom(server) {
|
|
35344
|
+
const inner = server.server;
|
|
35345
|
+
if (typeof inner !== "object" || inner === null)
|
|
35346
|
+
return void 0;
|
|
35347
|
+
const read = inner.getClientCapabilities;
|
|
35348
|
+
if (typeof read !== "function")
|
|
35349
|
+
return void 0;
|
|
35350
|
+
try {
|
|
35351
|
+
const declared = read.call(inner);
|
|
35352
|
+
return typeof declared === "object" && declared !== null ? declared : void 0;
|
|
35353
|
+
} catch {
|
|
35354
|
+
return void 0;
|
|
35355
|
+
}
|
|
35356
|
+
}
|
|
35306
35357
|
function surfaceToolHints(server) {
|
|
35307
35358
|
const register2 = server.registerTool.bind(server);
|
|
35308
35359
|
server.registerTool = (name, config2, cb) => register2(name, config2, (...args) => (
|
|
@@ -35318,15 +35369,24 @@ function surfaceToolHints(server) {
|
|
|
35318
35369
|
// Here because this is the one wrapper every tool already passes
|
|
35319
35370
|
// through: threading the signal by hand would mean editing several
|
|
35320
35371
|
// hundred handlers and missing exactly the ones nobody edits.
|
|
35321
|
-
withCallSignal(callSignalFrom(args), () =>
|
|
35322
|
-
|
|
35323
|
-
|
|
35324
|
-
|
|
35325
|
-
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
|
|
35329
|
-
|
|
35372
|
+
withCallSignal(callSignalFrom(args), () => (
|
|
35373
|
+
// WHAT THE CALLER CAN DO, made ambient for the same reason and in
|
|
35374
|
+
// the same place (`caller/index.ts`). Only the 2025-era half needs
|
|
35375
|
+
// the wrapper — a 2026-07-28 request answers for itself off its own
|
|
35376
|
+
// envelope — but a guarded tool that cannot tell whether the caller
|
|
35377
|
+
// can be shown a prompt returns one the SDK then refuses to deliver,
|
|
35378
|
+
// and that refusal reaches the caller as an unexplained protocol
|
|
35379
|
+
// error it had no chance to catch.
|
|
35380
|
+
withCallerCapabilities(declaredCapabilitiesFrom(server), () => {
|
|
35381
|
+
let result;
|
|
35382
|
+
try {
|
|
35383
|
+
result = cb(...args);
|
|
35384
|
+
} catch (err) {
|
|
35385
|
+
return hintResultOrRethrow(err);
|
|
35386
|
+
}
|
|
35387
|
+
return result instanceof Promise ? result.catch(hintResultOrRethrow) : result;
|
|
35388
|
+
})
|
|
35389
|
+
), mcpRequestFrom(args))
|
|
35330
35390
|
));
|
|
35331
35391
|
}
|
|
35332
35392
|
async function createMcpServer(opts) {
|
|
@@ -35344,11 +35404,43 @@ async function createMcpServer(opts) {
|
|
|
35344
35404
|
}
|
|
35345
35405
|
function withGracefulShutdown(target, opts = {}) {
|
|
35346
35406
|
const shouldExit = opts.exit ?? true;
|
|
35407
|
+
if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
|
|
35408
|
+
throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
|
|
35409
|
+
}
|
|
35410
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
35411
|
+
if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
|
|
35412
|
+
throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
|
|
35413
|
+
}
|
|
35414
|
+
const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
|
|
35347
35415
|
let shuttingDown = false;
|
|
35416
|
+
let firstSignalAt = 0;
|
|
35417
|
+
let exited = false;
|
|
35418
|
+
const exitOnce = () => {
|
|
35419
|
+
if (exited)
|
|
35420
|
+
return;
|
|
35421
|
+
exited = true;
|
|
35422
|
+
process.exit(0);
|
|
35423
|
+
};
|
|
35348
35424
|
const handler = (signal) => {
|
|
35349
|
-
if (shuttingDown)
|
|
35425
|
+
if (shuttingDown) {
|
|
35426
|
+
if (Date.now() - firstSignalAt < repeatGraceMs)
|
|
35427
|
+
return;
|
|
35428
|
+
if (shouldExit) {
|
|
35429
|
+
console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
|
|
35430
|
+
exitOnce();
|
|
35431
|
+
}
|
|
35350
35432
|
return;
|
|
35433
|
+
}
|
|
35351
35434
|
shuttingDown = true;
|
|
35435
|
+
firstSignalAt = Date.now();
|
|
35436
|
+
let timer;
|
|
35437
|
+
if (shouldExit) {
|
|
35438
|
+
timer = setTimeout(() => {
|
|
35439
|
+
console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
|
|
35440
|
+
exitOnce();
|
|
35441
|
+
}, timeoutMs);
|
|
35442
|
+
timer.unref?.();
|
|
35443
|
+
}
|
|
35352
35444
|
void (async () => {
|
|
35353
35445
|
try {
|
|
35354
35446
|
if (opts.onSignal)
|
|
@@ -35357,8 +35449,10 @@ function withGracefulShutdown(target, opts = {}) {
|
|
|
35357
35449
|
} catch (err) {
|
|
35358
35450
|
console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
|
|
35359
35451
|
} finally {
|
|
35452
|
+
if (timer !== void 0)
|
|
35453
|
+
clearTimeout(timer);
|
|
35360
35454
|
if (shouldExit)
|
|
35361
|
-
|
|
35455
|
+
exitOnce();
|
|
35362
35456
|
}
|
|
35363
35457
|
})();
|
|
35364
35458
|
};
|
|
@@ -35386,7 +35480,7 @@ function runMcp(opts) {
|
|
|
35386
35480
|
}
|
|
35387
35481
|
return handle;
|
|
35388
35482
|
}
|
|
35389
|
-
var SERVER_PROTOCOL_VERSIONS;
|
|
35483
|
+
var SERVER_PROTOCOL_VERSIONS, DEFAULT_SHUTDOWN_TIMEOUT_MS, DEFAULT_REPEAT_SIGNAL_GRACE_MS;
|
|
35390
35484
|
var init_server = __esm({
|
|
35391
35485
|
"node_modules/@chrischall/mcp-utils/dist/server/index.js"() {
|
|
35392
35486
|
init_stdio();
|
|
@@ -35395,10 +35489,13 @@ var init_server = __esm({
|
|
|
35395
35489
|
init_response();
|
|
35396
35490
|
init_confirmation();
|
|
35397
35491
|
init_cancel();
|
|
35492
|
+
init_caller();
|
|
35398
35493
|
SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
35399
35494
|
"2026-07-28",
|
|
35400
35495
|
...SUPPORTED_PROTOCOL_VERSIONS
|
|
35401
35496
|
]);
|
|
35497
|
+
DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
35498
|
+
DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
|
|
35402
35499
|
}
|
|
35403
35500
|
});
|
|
35404
35501
|
|
|
@@ -35482,17 +35579,27 @@ var init_config = __esm({
|
|
|
35482
35579
|
}
|
|
35483
35580
|
});
|
|
35484
35581
|
|
|
35582
|
+
// node_modules/@chrischall/mcp-utils/dist/fs/confine.js
|
|
35583
|
+
var init_confine = __esm({
|
|
35584
|
+
"node_modules/@chrischall/mcp-utils/dist/fs/confine.js"() {
|
|
35585
|
+
init_config();
|
|
35586
|
+
}
|
|
35587
|
+
});
|
|
35588
|
+
|
|
35485
35589
|
// node_modules/@chrischall/mcp-utils/dist/fs/output.js
|
|
35486
35590
|
var init_output = __esm({
|
|
35487
35591
|
"node_modules/@chrischall/mcp-utils/dist/fs/output.js"() {
|
|
35488
35592
|
init_config();
|
|
35593
|
+
init_confine();
|
|
35489
35594
|
}
|
|
35490
35595
|
});
|
|
35491
35596
|
|
|
35492
35597
|
// node_modules/@chrischall/mcp-utils/dist/fs/index.js
|
|
35493
35598
|
var init_fs = __esm({
|
|
35494
35599
|
"node_modules/@chrischall/mcp-utils/dist/fs/index.js"() {
|
|
35600
|
+
init_confine();
|
|
35495
35601
|
init_output();
|
|
35602
|
+
init_confine();
|
|
35496
35603
|
}
|
|
35497
35604
|
});
|
|
35498
35605
|
|
|
@@ -35543,24 +35650,78 @@ function createApiClient(opts) {
|
|
|
35543
35650
|
const retryStatuses = retry.statuses ?? [429];
|
|
35544
35651
|
const timeoutMs = opts.timeout;
|
|
35545
35652
|
const authHeader = (token) => !token ? {} : opts.tokenHeader ? { [opts.tokenHeader]: token } : { Authorization: `Bearer ${token}` };
|
|
35546
|
-
function withTimeout(run) {
|
|
35547
|
-
if (timeoutMs == null || timeoutMs <= 0)
|
|
35548
|
-
|
|
35653
|
+
async function withTimeout(run) {
|
|
35654
|
+
if (timeoutMs == null || timeoutMs <= 0) {
|
|
35655
|
+
const res2 = await run(withAmbientCancellation(void 0));
|
|
35656
|
+
return { res: res2, done: () => {
|
|
35657
|
+
}, expired: void 0 };
|
|
35658
|
+
}
|
|
35549
35659
|
const controller = new AbortController();
|
|
35550
35660
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
35551
|
-
|
|
35661
|
+
let res;
|
|
35662
|
+
try {
|
|
35663
|
+
res = await run(withAmbientCancellation(controller.signal));
|
|
35664
|
+
} catch (err) {
|
|
35665
|
+
clearTimeout(timer);
|
|
35552
35666
|
if (err instanceof Error && err.name === "AbortError" && controller.signal.aborted) {
|
|
35553
35667
|
throw new RequestTimeoutError(service, timeoutMs);
|
|
35554
35668
|
}
|
|
35555
35669
|
throw err;
|
|
35556
|
-
}
|
|
35670
|
+
}
|
|
35671
|
+
const expired = new Promise((_resolve, reject) => {
|
|
35672
|
+
controller.signal.addEventListener("abort", () => reject(new RequestTimeoutError(service, timeoutMs)), {
|
|
35673
|
+
once: true
|
|
35674
|
+
});
|
|
35675
|
+
});
|
|
35676
|
+
expired.catch(() => {
|
|
35677
|
+
});
|
|
35678
|
+
return { res, done: () => clearTimeout(timer), expired };
|
|
35679
|
+
}
|
|
35680
|
+
async function readBody(attempt, read) {
|
|
35681
|
+
try {
|
|
35682
|
+
if (!attempt.expired)
|
|
35683
|
+
return await read(attempt.res);
|
|
35684
|
+
return await Promise.race([read(attempt.res), attempt.expired]);
|
|
35685
|
+
} catch (err) {
|
|
35686
|
+
if (err instanceof RequestTimeoutError) {
|
|
35687
|
+
attempt.res.body?.cancel().catch(() => {
|
|
35688
|
+
});
|
|
35689
|
+
}
|
|
35690
|
+
throw err;
|
|
35691
|
+
} finally {
|
|
35692
|
+
attempt.done();
|
|
35693
|
+
}
|
|
35694
|
+
}
|
|
35695
|
+
const baseParts = (() => {
|
|
35696
|
+
try {
|
|
35697
|
+
const u = new URL(base);
|
|
35698
|
+
return { origin: u.origin, username: u.username, password: u.password };
|
|
35699
|
+
} catch {
|
|
35700
|
+
return void 0;
|
|
35701
|
+
}
|
|
35702
|
+
})();
|
|
35703
|
+
const baseOrigin = baseParts?.origin;
|
|
35704
|
+
function resolveUrl(path, query) {
|
|
35705
|
+
const url2 = `${base}${path}${query}`;
|
|
35706
|
+
if (baseOrigin === void 0)
|
|
35707
|
+
return url2;
|
|
35708
|
+
let parsed;
|
|
35709
|
+
try {
|
|
35710
|
+
parsed = new URL(url2);
|
|
35711
|
+
} catch {
|
|
35712
|
+
throw new Error(`Refusing request to ${service}: path ${JSON.stringify(path)} does not form a valid URL.`);
|
|
35713
|
+
}
|
|
35714
|
+
if (parsed.origin !== baseOrigin || parsed.username !== baseParts?.username || parsed.password !== baseParts?.password) {
|
|
35715
|
+
throw new Error(`Refusing request to ${service}: path ${JSON.stringify(path)} resolves outside the base origin ${baseOrigin}. Paths must be relative to baseUrl (start them with "/").`);
|
|
35716
|
+
}
|
|
35717
|
+
return url2;
|
|
35557
35718
|
}
|
|
35558
35719
|
async function send(method, path, opt) {
|
|
35559
35720
|
const isMultipart = opt.formData !== void 0;
|
|
35560
35721
|
const hasJsonBody = !isMultipart && opt.body !== void 0;
|
|
35561
35722
|
const reqBody = isMultipart ? opt.formData : hasJsonBody ? JSON.stringify(opt.body) : void 0;
|
|
35562
35723
|
const query = opt.query ? buildQueryString(opt.query) : "";
|
|
35563
|
-
const url2 =
|
|
35724
|
+
const url2 = resolveUrl(path, query);
|
|
35564
35725
|
const bodyInit = reqBody !== void 0 ? { body: reqBody } : {};
|
|
35565
35726
|
const fetchWith = (token, signal) => doFetch(url2, {
|
|
35566
35727
|
method,
|
|
@@ -35577,8 +35738,10 @@ function createApiClient(opts) {
|
|
|
35577
35738
|
const once = () => withTimeout((signal) => opts.tokenManager ? opts.tokenManager.withAuth((token) => fetchWith(token, signal)) : (async () => fetchWith(await opts.getToken?.() || void 0, signal))());
|
|
35578
35739
|
let attempt = 0;
|
|
35579
35740
|
for (; ; ) {
|
|
35580
|
-
const
|
|
35741
|
+
const current = await once();
|
|
35742
|
+
const res = current.res;
|
|
35581
35743
|
if (retryStatuses.includes(res.status) && attempt < retry.count) {
|
|
35744
|
+
current.done();
|
|
35582
35745
|
attempt += 1;
|
|
35583
35746
|
const delay = retry.honorRetryAfter ? parseRetryAfterMs(res.headers.get("retry-after"), {
|
|
35584
35747
|
defaultMs: retry.delayMs,
|
|
@@ -35587,18 +35750,21 @@ function createApiClient(opts) {
|
|
|
35587
35750
|
await sleep3(delay);
|
|
35588
35751
|
continue;
|
|
35589
35752
|
}
|
|
35590
|
-
return
|
|
35753
|
+
return current;
|
|
35591
35754
|
}
|
|
35592
35755
|
}
|
|
35593
35756
|
async function fetchJson(method, path, opt = {}) {
|
|
35594
|
-
const
|
|
35757
|
+
const attempt = await send(method, path, opt);
|
|
35758
|
+
const res = attempt.res;
|
|
35759
|
+
if (res.status === 401 || res.status === 429 || res.status === 204)
|
|
35760
|
+
attempt.done();
|
|
35595
35761
|
if (res.status === 401)
|
|
35596
35762
|
throw unauthorized();
|
|
35597
35763
|
if (res.status === 429)
|
|
35598
35764
|
throw rateLimited();
|
|
35599
35765
|
if (res.status === 204)
|
|
35600
35766
|
return void 0;
|
|
35601
|
-
const text = await
|
|
35767
|
+
const text = await readBody(attempt, (r) => r.text());
|
|
35602
35768
|
if (!res.ok) {
|
|
35603
35769
|
throw new ApiError(res.status, formatApiError(res.status, method, path, text, { service }));
|
|
35604
35770
|
}
|
|
@@ -35608,12 +35774,15 @@ function createApiClient(opts) {
|
|
|
35608
35774
|
}
|
|
35609
35775
|
async function fetchHtml(method, path, opt = {}) {
|
|
35610
35776
|
const headers = { Accept: "text/html,*/*", ...opt.headers };
|
|
35611
|
-
const
|
|
35777
|
+
const attempt = await send(method, path, { ...opt, headers });
|
|
35778
|
+
const res = attempt.res;
|
|
35779
|
+
if (res.status === 401 || res.status === 429)
|
|
35780
|
+
attempt.done();
|
|
35612
35781
|
if (res.status === 401)
|
|
35613
35782
|
throw unauthorized();
|
|
35614
35783
|
if (res.status === 429)
|
|
35615
35784
|
throw rateLimited();
|
|
35616
|
-
const text = await
|
|
35785
|
+
const text = await readBody(attempt, (r) => r.text());
|
|
35617
35786
|
if (!res.ok) {
|
|
35618
35787
|
throw new ApiError(res.status, formatApiError(res.status, method, path, text, { service }));
|
|
35619
35788
|
}
|
|
@@ -35621,16 +35790,23 @@ function createApiClient(opts) {
|
|
|
35621
35790
|
}
|
|
35622
35791
|
async function fetchRaw(method, path, opt = {}) {
|
|
35623
35792
|
const headers = { Accept: "*/*", ...opt.headers };
|
|
35624
|
-
const
|
|
35793
|
+
const attempt = await send(method, path, { ...opt, headers });
|
|
35794
|
+
const res = attempt.res;
|
|
35795
|
+
if (res.status === 401 || res.status === 429)
|
|
35796
|
+
attempt.done();
|
|
35625
35797
|
if (res.status === 401)
|
|
35626
35798
|
throw unauthorized();
|
|
35627
35799
|
if (res.status === 429)
|
|
35628
35800
|
throw rateLimited();
|
|
35629
35801
|
if (!res.ok) {
|
|
35630
|
-
const text = await
|
|
35802
|
+
const text = await readBody(attempt, (r) => r.text()).catch((err) => {
|
|
35803
|
+
if (err instanceof RequestTimeoutError)
|
|
35804
|
+
throw err;
|
|
35805
|
+
return "";
|
|
35806
|
+
});
|
|
35631
35807
|
throw new ApiError(res.status, formatApiError(res.status, method, path, text, { service }));
|
|
35632
35808
|
}
|
|
35633
|
-
const bytes = new Uint8Array(await
|
|
35809
|
+
const bytes = new Uint8Array(await readBody(attempt, (r) => r.arrayBuffer()));
|
|
35634
35810
|
return {
|
|
35635
35811
|
status: res.status,
|
|
35636
35812
|
contentType: res.headers.get("content-type"),
|
|
@@ -35813,9 +35989,16 @@ var init_auth = __esm({
|
|
|
35813
35989
|
}
|
|
35814
35990
|
});
|
|
35815
35991
|
|
|
35992
|
+
// node_modules/@chrischall/mcp-utils/dist/internal/ascii.js
|
|
35993
|
+
var init_ascii = __esm({
|
|
35994
|
+
"node_modules/@chrischall/mcp-utils/dist/internal/ascii.js"() {
|
|
35995
|
+
}
|
|
35996
|
+
});
|
|
35997
|
+
|
|
35816
35998
|
// node_modules/@chrischall/mcp-utils/dist/scrape/index.js
|
|
35817
35999
|
var init_scrape = __esm({
|
|
35818
36000
|
"node_modules/@chrischall/mcp-utils/dist/scrape/index.js"() {
|
|
36001
|
+
init_ascii();
|
|
35819
36002
|
}
|
|
35820
36003
|
});
|
|
35821
36004
|
|
|
@@ -35823,6 +36006,7 @@ var init_scrape = __esm({
|
|
|
35823
36006
|
var init_dist2 = __esm({
|
|
35824
36007
|
"node_modules/@chrischall/mcp-utils/dist/index.js"() {
|
|
35825
36008
|
init_cancel();
|
|
36009
|
+
init_caller();
|
|
35826
36010
|
init_server();
|
|
35827
36011
|
init_response();
|
|
35828
36012
|
init_errors3();
|
|
@@ -35843,7 +36027,7 @@ var init_version = __esm({
|
|
|
35843
36027
|
"src/version.ts"() {
|
|
35844
36028
|
"use strict";
|
|
35845
36029
|
PACKAGE_NAME = "office-outlook-mcp";
|
|
35846
|
-
VERSION = "0.
|
|
36030
|
+
VERSION = "0.2.0";
|
|
35847
36031
|
}
|
|
35848
36032
|
});
|
|
35849
36033
|
|
|
@@ -36058,6 +36242,7 @@ var init_frames = __esm({
|
|
|
36058
36242
|
"capture_redirect",
|
|
36059
36243
|
"read_indexed_db",
|
|
36060
36244
|
"read_dom",
|
|
36245
|
+
"read_dom_list",
|
|
36061
36246
|
"download",
|
|
36062
36247
|
"graphql",
|
|
36063
36248
|
"write_cookies"
|
|
@@ -36558,6 +36743,79 @@ function assertDomSelectorsArray(value, label) {
|
|
|
36558
36743
|
}
|
|
36559
36744
|
}
|
|
36560
36745
|
}
|
|
36746
|
+
function assertDomListSelectorsArray(value, label) {
|
|
36747
|
+
if (!Array.isArray(value)) {
|
|
36748
|
+
throw new ProtocolError3(`${label}: expected array, got ${typeof value}`);
|
|
36749
|
+
}
|
|
36750
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36751
|
+
for (let i = 0; i < value.length; i++) {
|
|
36752
|
+
const entry = value[i];
|
|
36753
|
+
assertObject(entry, `${label}[${i}]`);
|
|
36754
|
+
if (entry.name === void 0) {
|
|
36755
|
+
throw new ProtocolError3(`${label}[${i}].name: missing`);
|
|
36756
|
+
}
|
|
36757
|
+
if (entry.itemSelector === void 0) {
|
|
36758
|
+
throw new ProtocolError3(`${label}[${i}].itemSelector: missing`);
|
|
36759
|
+
}
|
|
36760
|
+
if (entry.fields === void 0) {
|
|
36761
|
+
throw new ProtocolError3(`${label}[${i}].fields: missing`);
|
|
36762
|
+
}
|
|
36763
|
+
if (typeof entry.name !== "string" || !SCOPE_KEY_RE.test(entry.name)) {
|
|
36764
|
+
throw new ProtocolError3(`${label}[${i}].name: invalid ${JSON.stringify(entry.name)}`);
|
|
36765
|
+
}
|
|
36766
|
+
if (typeof entry.itemSelector !== "string" || !DOM_SELECTOR_RE.test(entry.itemSelector)) {
|
|
36767
|
+
throw new ProtocolError3(`${label}[${i}].itemSelector: invalid ${JSON.stringify(entry.itemSelector)}`);
|
|
36768
|
+
}
|
|
36769
|
+
if (!Array.isArray(entry.fields) || entry.fields.length === 0) {
|
|
36770
|
+
throw new ProtocolError3(`${label}[${i}].fields: expected non-empty array`);
|
|
36771
|
+
}
|
|
36772
|
+
const fieldNames = /* @__PURE__ */ new Set();
|
|
36773
|
+
for (let j = 0; j < entry.fields.length; j++) {
|
|
36774
|
+
const field = entry.fields[j];
|
|
36775
|
+
const fieldLabel = `${label}[${i}].fields[${j}]`;
|
|
36776
|
+
assertObject(field, fieldLabel);
|
|
36777
|
+
if (field.name === void 0) {
|
|
36778
|
+
throw new ProtocolError3(`${fieldLabel}.name: missing`);
|
|
36779
|
+
}
|
|
36780
|
+
if (typeof field.name !== "string" || !SCOPE_KEY_RE.test(field.name)) {
|
|
36781
|
+
throw new ProtocolError3(`${fieldLabel}.name: invalid ${JSON.stringify(field.name)}`);
|
|
36782
|
+
}
|
|
36783
|
+
if (field.selector !== void 0) {
|
|
36784
|
+
if (typeof field.selector !== "string" || !DOM_SELECTOR_RE.test(field.selector)) {
|
|
36785
|
+
throw new ProtocolError3(`${fieldLabel}.selector: invalid ${JSON.stringify(field.selector)}`);
|
|
36786
|
+
}
|
|
36787
|
+
}
|
|
36788
|
+
if (field.attribute !== void 0) {
|
|
36789
|
+
if (typeof field.attribute !== "string" || !DOM_ATTRIBUTE_RE.test(field.attribute)) {
|
|
36790
|
+
throw new ProtocolError3(`${fieldLabel}.attribute: invalid ${JSON.stringify(field.attribute)}`);
|
|
36791
|
+
}
|
|
36792
|
+
}
|
|
36793
|
+
if (fieldNames.has(field.name)) {
|
|
36794
|
+
throw new ProtocolError3(`${label}[${i}].fields: duplicate name ${JSON.stringify(field.name)}`);
|
|
36795
|
+
}
|
|
36796
|
+
fieldNames.add(field.name);
|
|
36797
|
+
for (const k of Object.keys(field)) {
|
|
36798
|
+
if (k !== "name" && k !== "selector" && k !== "attribute") {
|
|
36799
|
+
throw new ProtocolError3(`${fieldLabel}: unexpected field ${JSON.stringify(k)}`);
|
|
36800
|
+
}
|
|
36801
|
+
}
|
|
36802
|
+
}
|
|
36803
|
+
if (entry.maxItems !== void 0) {
|
|
36804
|
+
if (typeof entry.maxItems !== "number" || !Number.isInteger(entry.maxItems) || entry.maxItems < 1 || entry.maxItems > 1e3) {
|
|
36805
|
+
throw new ProtocolError3(`${label}[${i}].maxItems: invalid ${JSON.stringify(entry.maxItems)}`);
|
|
36806
|
+
}
|
|
36807
|
+
}
|
|
36808
|
+
if (seen.has(entry.name)) {
|
|
36809
|
+
throw new ProtocolError3(`${label}: duplicate name ${JSON.stringify(entry.name)}`);
|
|
36810
|
+
}
|
|
36811
|
+
seen.add(entry.name);
|
|
36812
|
+
for (const k of Object.keys(entry)) {
|
|
36813
|
+
if (k !== "name" && k !== "itemSelector" && k !== "fields" && k !== "maxItems") {
|
|
36814
|
+
throw new ProtocolError3(`${label}[${i}]: unexpected field ${JSON.stringify(k)}`);
|
|
36815
|
+
}
|
|
36816
|
+
}
|
|
36817
|
+
}
|
|
36818
|
+
}
|
|
36561
36819
|
function assertGraphqlOpsArray(value, label) {
|
|
36562
36820
|
if (!Array.isArray(value)) {
|
|
36563
36821
|
throw new ProtocolError3(`${label}: expected array, got ${typeof value}`);
|
|
@@ -36604,6 +36862,8 @@ function validateFrame(raw) {
|
|
|
36604
36862
|
return validateHelloRejected(raw);
|
|
36605
36863
|
if (t === "extension-disconnected")
|
|
36606
36864
|
return { type: "extension-disconnected" };
|
|
36865
|
+
if (t === "peer-gone")
|
|
36866
|
+
return validatePeerGone(raw);
|
|
36607
36867
|
throw new ProtocolError3(`unknown frame type: ${String(t)}`);
|
|
36608
36868
|
}
|
|
36609
36869
|
function validateHello(raw) {
|
|
@@ -36674,16 +36934,11 @@ function validateHello(raw) {
|
|
|
36674
36934
|
if (raw.domSelectors !== void 0) {
|
|
36675
36935
|
assertDomSelectorsArray(raw.domSelectors, "hello.domSelectors");
|
|
36676
36936
|
}
|
|
36677
|
-
if (raw.
|
|
36678
|
-
|
|
36679
|
-
throw new ProtocolError3("hello.accepts: expected array");
|
|
36680
|
-
}
|
|
36681
|
-
for (const a of raw.accepts) {
|
|
36682
|
-
if (typeof a !== "string") {
|
|
36683
|
-
throw new ProtocolError3(`hello.accepts: entry must be string, got ${typeof a}`);
|
|
36684
|
-
}
|
|
36685
|
-
}
|
|
36937
|
+
if (raw.domListSelectors !== void 0) {
|
|
36938
|
+
assertDomListSelectorsArray(raw.domListSelectors, "hello.domListSelectors");
|
|
36686
36939
|
}
|
|
36940
|
+
if (raw.accepts !== void 0)
|
|
36941
|
+
assertAcceptsList(raw.accepts);
|
|
36687
36942
|
if (raw.graphqlOps !== void 0) {
|
|
36688
36943
|
assertGraphqlOpsArray(raw.graphqlOps, "hello.graphqlOps");
|
|
36689
36944
|
}
|
|
@@ -36705,6 +36960,8 @@ function validateHello(raw) {
|
|
|
36705
36960
|
assertBase64(raw.identityX25519Pub, "hello.identityX25519Pub");
|
|
36706
36961
|
assertBase64(raw.identityEd25519Pub, "hello.identityEd25519Pub");
|
|
36707
36962
|
assertBase64(raw.sessionNonce, "hello.sessionNonce");
|
|
36963
|
+
if (raw.accepts !== void 0)
|
|
36964
|
+
assertAcceptsList(raw.accepts);
|
|
36708
36965
|
return raw;
|
|
36709
36966
|
}
|
|
36710
36967
|
throw new ProtocolError3(`hello.role: must be 'server' or 'extension', got ${String(role)}`);
|
|
@@ -36759,6 +37016,27 @@ function validatePairPending(raw) {
|
|
|
36759
37016
|
}
|
|
36760
37017
|
return { type: "pair-pending", mcpId: raw.mcpId, pairCode: raw.pairCode };
|
|
36761
37018
|
}
|
|
37019
|
+
function assertAcceptsList(accepts) {
|
|
37020
|
+
if (!Array.isArray(accepts)) {
|
|
37021
|
+
throw new ProtocolError3("hello.accepts: expected array");
|
|
37022
|
+
}
|
|
37023
|
+
for (const a of accepts) {
|
|
37024
|
+
if (typeof a !== "string") {
|
|
37025
|
+
throw new ProtocolError3(`hello.accepts: entry must be string, got ${typeof a}`);
|
|
37026
|
+
}
|
|
37027
|
+
}
|
|
37028
|
+
}
|
|
37029
|
+
function validatePeerGone(raw) {
|
|
37030
|
+
assertString(raw.mcpId, "peer-gone.mcpId");
|
|
37031
|
+
if (!isValidMcpId(raw.mcpId))
|
|
37032
|
+
throw new ProtocolError3("peer-gone.mcpId: invalid format");
|
|
37033
|
+
for (const k of Object.keys(raw)) {
|
|
37034
|
+
if (k !== "type" && k !== "mcpId") {
|
|
37035
|
+
throw new ProtocolError3(`peer-gone: unexpected field ${JSON.stringify(k)}`);
|
|
37036
|
+
}
|
|
37037
|
+
}
|
|
37038
|
+
return { type: "peer-gone", mcpId: raw.mcpId };
|
|
37039
|
+
}
|
|
36762
37040
|
function validateHelloRejected(raw) {
|
|
36763
37041
|
assertString(raw.mcpId, "hello-rejected.mcpId");
|
|
36764
37042
|
if (!isValidMcpId(raw.mcpId))
|
|
@@ -37008,6 +37286,24 @@ function validateInnerRequest(raw) {
|
|
|
37008
37286
|
}
|
|
37009
37287
|
return raw;
|
|
37010
37288
|
}
|
|
37289
|
+
if (raw.op === "read_dom_list") {
|
|
37290
|
+
assertObject(raw.init, "inner.init");
|
|
37291
|
+
if (raw.init.origin === void 0)
|
|
37292
|
+
throw new ProtocolError3("inner.init.origin: missing");
|
|
37293
|
+
if (raw.init.name === void 0)
|
|
37294
|
+
throw new ProtocolError3("inner.init.name: missing");
|
|
37295
|
+
assertHttpsOriginOnly(raw.init.origin, "inner.init.origin");
|
|
37296
|
+
assertString(raw.init.name, "inner.init.name");
|
|
37297
|
+
if (raw.init.name.length === 0) {
|
|
37298
|
+
throw new ProtocolError3("inner.init.name: must be non-empty");
|
|
37299
|
+
}
|
|
37300
|
+
for (const k of Object.keys(raw.init)) {
|
|
37301
|
+
if (k !== "origin" && k !== "name") {
|
|
37302
|
+
throw new ProtocolError3(`inner.init: unexpected field ${JSON.stringify(k)} on read_dom_list`);
|
|
37303
|
+
}
|
|
37304
|
+
}
|
|
37305
|
+
return raw;
|
|
37306
|
+
}
|
|
37011
37307
|
if (raw.op === "graphql_query") {
|
|
37012
37308
|
assertObject(raw.init, "inner.init");
|
|
37013
37309
|
if (raw.init.name === void 0)
|
|
@@ -37055,7 +37351,7 @@ function validateInnerRequest(raw) {
|
|
|
37055
37351
|
}
|
|
37056
37352
|
return raw;
|
|
37057
37353
|
}
|
|
37058
|
-
throw new ProtocolError3(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "download", "graphql_query", "write_cookies"; got ${JSON.stringify(raw.op)}`);
|
|
37354
|
+
throw new ProtocolError3(`inner.op: must be one of "fetch", "read_cookies", "read_local_storage", "read_session_storage", "capture_request_header", "capture_redirect", "read_indexed_db", "read_dom", "read_dom_list", "download", "graphql_query", "write_cookies"; got ${JSON.stringify(raw.op)}`);
|
|
37059
37355
|
}
|
|
37060
37356
|
function assertNonEmptyKeyArray(value, label) {
|
|
37061
37357
|
if (!Array.isArray(value)) {
|
|
@@ -37159,6 +37455,18 @@ function validateInnerResponse(raw) {
|
|
|
37159
37455
|
assertStringMap(raw.values, "inner.values");
|
|
37160
37456
|
return raw;
|
|
37161
37457
|
}
|
|
37458
|
+
if (op === "read_dom_list") {
|
|
37459
|
+
if (raw.rows === void 0) {
|
|
37460
|
+
throw new ProtocolError3("inner.rows: missing on read_dom_list response");
|
|
37461
|
+
}
|
|
37462
|
+
if (!Array.isArray(raw.rows)) {
|
|
37463
|
+
throw new ProtocolError3("inner.rows: expected array, got " + typeof raw.rows);
|
|
37464
|
+
}
|
|
37465
|
+
for (const [i, row] of raw.rows.entries()) {
|
|
37466
|
+
assertStringMap(row, `inner.rows[${i}]`);
|
|
37467
|
+
}
|
|
37468
|
+
return raw;
|
|
37469
|
+
}
|
|
37162
37470
|
if (op === "graphql_query") {
|
|
37163
37471
|
if (raw.data === void 0) {
|
|
37164
37472
|
throw new ProtocolError3("inner.data: missing on graphql_query response");
|
|
@@ -37291,12 +37599,6 @@ async function sealInnerFrame(sessionKey, mcpId, seq, inner, direction) {
|
|
|
37291
37599
|
ciphertext: toB64(ct)
|
|
37292
37600
|
};
|
|
37293
37601
|
}
|
|
37294
|
-
async function openEncryptedFrame(sessionKey, frame, direction) {
|
|
37295
|
-
const result = await openEncryptedFrameDetailed(sessionKey, frame, direction);
|
|
37296
|
-
if (result.stage === "ok")
|
|
37297
|
-
return result.inner;
|
|
37298
|
-
throw result.error instanceof Error ? result.error : new Error(String(result.error));
|
|
37299
|
-
}
|
|
37300
37602
|
async function openEncryptedFrameDetailed(sessionKey, frame, direction) {
|
|
37301
37603
|
let pt;
|
|
37302
37604
|
try {
|
|
@@ -39645,7 +39947,7 @@ var require_websocket = __commonJS({
|
|
|
39645
39947
|
var http = __require("http");
|
|
39646
39948
|
var net = __require("net");
|
|
39647
39949
|
var tls = __require("tls");
|
|
39648
|
-
var { randomBytes:
|
|
39950
|
+
var { randomBytes: randomBytes4, createHash } = __require("crypto");
|
|
39649
39951
|
var { Duplex, Readable } = __require("stream");
|
|
39650
39952
|
var { URL: URL2 } = __require("url");
|
|
39651
39953
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -40183,7 +40485,7 @@ var require_websocket = __commonJS({
|
|
|
40183
40485
|
}
|
|
40184
40486
|
}
|
|
40185
40487
|
const defaultPort = isSecure ? 443 : 80;
|
|
40186
|
-
const key =
|
|
40488
|
+
const key = randomBytes4(16).toString("base64");
|
|
40187
40489
|
const request = isSecure ? https.request : http.request;
|
|
40188
40490
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
40189
40491
|
let perMessageDeflate;
|
|
@@ -41157,6 +41459,18 @@ async function buildServerHello(opts) {
|
|
|
41157
41459
|
...d.attribute !== void 0 ? { attribute: d.attribute } : {}
|
|
41158
41460
|
}));
|
|
41159
41461
|
}
|
|
41462
|
+
if (opts.domListSelectors && opts.domListSelectors.length > 0) {
|
|
41463
|
+
hello.domListSelectors = opts.domListSelectors.map((d) => ({
|
|
41464
|
+
name: d.name,
|
|
41465
|
+
itemSelector: d.itemSelector,
|
|
41466
|
+
fields: d.fields.map((f) => ({
|
|
41467
|
+
name: f.name,
|
|
41468
|
+
...f.selector !== void 0 ? { selector: f.selector } : {},
|
|
41469
|
+
...f.attribute !== void 0 ? { attribute: f.attribute } : {}
|
|
41470
|
+
})),
|
|
41471
|
+
...d.maxItems !== void 0 ? { maxItems: d.maxItems } : {}
|
|
41472
|
+
}));
|
|
41473
|
+
}
|
|
41160
41474
|
if (opts.graphqlOps && opts.graphqlOps.length > 0) {
|
|
41161
41475
|
hello.graphqlOps = opts.graphqlOps.map((d) => ({
|
|
41162
41476
|
name: d.name,
|
|
@@ -41345,7 +41659,7 @@ var init_session_ready = __esm({
|
|
|
41345
41659
|
});
|
|
41346
41660
|
|
|
41347
41661
|
// node_modules/@fetchproxy/server/dist/identity.js
|
|
41348
|
-
import { readFile, readdir, writeFile, mkdir, chmod, rename, rm, unlink } from "node:fs/promises";
|
|
41662
|
+
import { readFile, readdir, writeFile, mkdir, chmod, rename, rm, unlink, link, lstat } from "node:fs/promises";
|
|
41349
41663
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
41350
41664
|
import { isAbsolute, join as join2 } from "node:path";
|
|
41351
41665
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -41410,7 +41724,7 @@ async function openIdentityDir(dir) {
|
|
|
41410
41724
|
throw e;
|
|
41411
41725
|
}
|
|
41412
41726
|
}
|
|
41413
|
-
async function sweepStagingFiles(dir, serverName) {
|
|
41727
|
+
async function sweepStagingFiles(dir, serverName, opts = {}) {
|
|
41414
41728
|
const base = safeIdentityFileBase(serverName).replace(/[.]/g, "\\.");
|
|
41415
41729
|
const staging = new RegExp(`^${base}\\.json\\.[0-9a-f]{${STAGING_BYTES * 2}}\\.tmp$`);
|
|
41416
41730
|
let names;
|
|
@@ -41419,48 +41733,94 @@ async function sweepStagingFiles(dir, serverName) {
|
|
|
41419
41733
|
} catch {
|
|
41420
41734
|
return;
|
|
41421
41735
|
}
|
|
41422
|
-
|
|
41736
|
+
const cutoff = opts.olderThanMs === void 0 ? null : Date.now() - opts.olderThanMs;
|
|
41737
|
+
await Promise.all(names.filter((name) => staging.test(name)).map(async (name) => {
|
|
41738
|
+
const path = join2(dir, name);
|
|
41739
|
+
if (cutoff !== null) {
|
|
41740
|
+
const st = await lstat(path).catch(() => null);
|
|
41741
|
+
if (st === null || st.mtimeMs > cutoff)
|
|
41742
|
+
return;
|
|
41743
|
+
}
|
|
41744
|
+
await unlink(path).catch(() => void 0);
|
|
41745
|
+
}));
|
|
41423
41746
|
}
|
|
41424
|
-
async function
|
|
41747
|
+
async function loadOrCreateIdentity(serverName, dir = defaultIdentityDir()) {
|
|
41425
41748
|
const path = identityFilePath(dir, serverName);
|
|
41426
41749
|
await openIdentityDir(dir);
|
|
41427
|
-
await sweepStagingFiles(dir, serverName);
|
|
41428
|
-
const tmp = `${path}.${randomBytes2(STAGING_BYTES).toString("hex")}.tmp`;
|
|
41429
41750
|
try {
|
|
41430
|
-
|
|
41431
|
-
await chmod(tmp, 384);
|
|
41432
|
-
await rename(tmp, path);
|
|
41751
|
+
return parseIdentity(await readFile(path, "utf8"));
|
|
41433
41752
|
} catch (e) {
|
|
41434
|
-
|
|
41435
|
-
|
|
41753
|
+
if (e.code !== "ENOENT" && !(e instanceof SyntaxError))
|
|
41754
|
+
throw e;
|
|
41755
|
+
}
|
|
41756
|
+
let unreadable = null;
|
|
41757
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
41758
|
+
const id = await generateIdentity();
|
|
41759
|
+
if (await publishIdentityIfAbsent(dir, serverName, id))
|
|
41760
|
+
return id;
|
|
41761
|
+
try {
|
|
41762
|
+
return parseIdentity(await readFile(path, "utf8"));
|
|
41763
|
+
} catch (e) {
|
|
41764
|
+
if (e.code === "ENOENT") {
|
|
41765
|
+
continue;
|
|
41766
|
+
}
|
|
41767
|
+
if (!(e instanceof SyntaxError))
|
|
41768
|
+
throw e;
|
|
41769
|
+
unreadable = e;
|
|
41770
|
+
await new Promise((r) => setTimeout(r, 20 * (attempt + 1)));
|
|
41771
|
+
}
|
|
41436
41772
|
}
|
|
41437
|
-
|
|
41773
|
+
if (unreadable !== null)
|
|
41774
|
+
throw unreadable;
|
|
41775
|
+
throw new Error(`fetchproxy: could not create an identity for ${JSON.stringify(serverName)} in ${dir}`);
|
|
41438
41776
|
}
|
|
41439
|
-
async function
|
|
41777
|
+
async function publishIdentityIfAbsent(dir, serverName, id) {
|
|
41440
41778
|
const path = identityFilePath(dir, serverName);
|
|
41441
|
-
await
|
|
41779
|
+
await sweepStagingFiles(dir, serverName, { olderThanMs: 6e4 });
|
|
41780
|
+
const tmp = `${path}.${randomBytes2(STAGING_BYTES).toString("hex")}.tmp`;
|
|
41442
41781
|
try {
|
|
41443
|
-
|
|
41782
|
+
await writeFile(tmp, serializeIdentity(id), { mode: 384, flag: "wx" });
|
|
41783
|
+
await chmod(tmp, 384);
|
|
41784
|
+
try {
|
|
41785
|
+
await link(tmp, path);
|
|
41786
|
+
} catch (e) {
|
|
41787
|
+
const code = e.code;
|
|
41788
|
+
if (code === void 0 || !NO_HARD_LINKS.has(code))
|
|
41789
|
+
throw e;
|
|
41790
|
+
try {
|
|
41791
|
+
await writeFile(path, serializeIdentity(id), { mode: 384, flag: "wx" });
|
|
41792
|
+
} catch (writeErr) {
|
|
41793
|
+
if (writeErr.code !== "EEXIST") {
|
|
41794
|
+
await rm(path, { force: true }).catch(() => void 0);
|
|
41795
|
+
}
|
|
41796
|
+
throw writeErr;
|
|
41797
|
+
}
|
|
41798
|
+
await chmod(path, 384);
|
|
41799
|
+
}
|
|
41800
|
+
return true;
|
|
41444
41801
|
} catch (e) {
|
|
41445
|
-
|
|
41446
|
-
|
|
41802
|
+
const code = e.code;
|
|
41803
|
+
if (code === "EEXIST" || code === "ENOENT")
|
|
41804
|
+
return false;
|
|
41805
|
+
throw e;
|
|
41806
|
+
} finally {
|
|
41807
|
+
await rm(tmp, { force: true });
|
|
41447
41808
|
}
|
|
41448
|
-
const id = await generateIdentity();
|
|
41449
|
-
await writeIdentityFile(dir, serverName, id);
|
|
41450
|
-
return id;
|
|
41451
41809
|
}
|
|
41452
|
-
var SAFE_PLAIN, SAFE_SCOPED, STAGING_BYTES;
|
|
41810
|
+
var SAFE_PLAIN, SAFE_SCOPED, STAGING_BYTES, NO_HARD_LINKS;
|
|
41453
41811
|
var init_identity = __esm({
|
|
41454
41812
|
"node_modules/@fetchproxy/server/dist/identity.js"() {
|
|
41455
41813
|
init_dist3();
|
|
41456
41814
|
SAFE_PLAIN = /^[A-Za-z0-9._-]+$/;
|
|
41457
41815
|
SAFE_SCOPED = /^@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
41458
41816
|
STAGING_BYTES = 6;
|
|
41817
|
+
NO_HARD_LINKS = /* @__PURE__ */ new Set(["EPERM", "ENOTSUP", "EOPNOTSUPP", "ENOSYS"]);
|
|
41459
41818
|
}
|
|
41460
41819
|
});
|
|
41461
41820
|
|
|
41462
41821
|
// node_modules/@fetchproxy/server/dist/extension-trust.js
|
|
41463
|
-
import { readFile as readFile2, writeFile as writeFile2, rename as rename2, unlink as unlink2, mkdir as mkdir2, chmod as chmod2 } from "node:fs/promises";
|
|
41822
|
+
import { readFile as readFile2, writeFile as writeFile2, rename as rename2, unlink as unlink2, mkdir as mkdir2, chmod as chmod2, rm as rm2 } from "node:fs/promises";
|
|
41823
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
41464
41824
|
import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
41465
41825
|
function envTrustDir(env = process.env) {
|
|
41466
41826
|
const raw = env[TRUST_DIR_ENV];
|
|
@@ -41557,10 +41917,15 @@ async function readExtensionPin(serverName, dir = defaultTrustDir()) {
|
|
|
41557
41917
|
async function writeExtensionPin(serverName, pin, dir = defaultTrustDir()) {
|
|
41558
41918
|
const path = extensionTrustPath(serverName, dir);
|
|
41559
41919
|
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
41560
|
-
const tmp = `${path}.tmp`;
|
|
41561
|
-
|
|
41562
|
-
|
|
41563
|
-
|
|
41920
|
+
const tmp = `${path}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
41921
|
+
try {
|
|
41922
|
+
await writeFile2(tmp, JSON.stringify(pin, null, 2), { mode: 384, flag: "wx" });
|
|
41923
|
+
await chmod2(tmp, 384);
|
|
41924
|
+
await rename2(tmp, path);
|
|
41925
|
+
} catch (e) {
|
|
41926
|
+
await rm2(tmp, { force: true });
|
|
41927
|
+
throw e;
|
|
41928
|
+
}
|
|
41564
41929
|
}
|
|
41565
41930
|
var TRUST_DIR_ENV, TRUST_NEW_EXTENSION_ENV;
|
|
41566
41931
|
var init_extension_trust = __esm({
|
|
@@ -41640,6 +42005,7 @@ async function startHost(opts) {
|
|
|
41640
42005
|
localStoragePointers: opts.ownLocalStoragePointers,
|
|
41641
42006
|
sessionStoragePointers: opts.ownSessionStoragePointers,
|
|
41642
42007
|
domSelectors: opts.ownDomSelectors,
|
|
42008
|
+
domListSelectors: opts.ownDomListSelectors,
|
|
41643
42009
|
graphqlOps: opts.ownGraphqlOps
|
|
41644
42010
|
};
|
|
41645
42011
|
const generateSessionKeypair = opts.generateSessionKeypair ?? generateX25519;
|
|
@@ -41710,6 +42076,31 @@ async function startHost(opts) {
|
|
|
41710
42076
|
return null;
|
|
41711
42077
|
}
|
|
41712
42078
|
};
|
|
42079
|
+
let ownDecryptFailures = 0;
|
|
42080
|
+
const rehandshakeOwnSession = async (ws) => {
|
|
42081
|
+
const hello = extensionHello;
|
|
42082
|
+
if (extensionWs !== ws || !hello)
|
|
42083
|
+
return;
|
|
42084
|
+
console.warn(`[fetchproxy] ${opts.ownServerName}: ${OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE} frames in a row from the extension failed authentication under this MCP's session key \u2014 re-handshaking this MCP's session (the extension stays connected).`);
|
|
42085
|
+
ownSession = dropOwnSessionAndEphemeral();
|
|
42086
|
+
resetSessionPromise();
|
|
42087
|
+
const mintedKeypair = await generateSessionKeypair();
|
|
42088
|
+
const mintedHello = await buildServerHello({
|
|
42089
|
+
...ownHelloBase,
|
|
42090
|
+
sessionPub: mintedKeypair.publicKey,
|
|
42091
|
+
answersExtNonce: fromB64(hello.sessionNonce)
|
|
42092
|
+
});
|
|
42093
|
+
if (extensionWs !== ws || extensionHello !== hello || ownSession !== null) {
|
|
42094
|
+
mintedKeypair.privateKey.fill(0);
|
|
42095
|
+
return;
|
|
42096
|
+
}
|
|
42097
|
+
installOwnEphemeral({
|
|
42098
|
+
nonce: fromB64(mintedHello.sessionNonce),
|
|
42099
|
+
pub: mintedKeypair.publicKey,
|
|
42100
|
+
priv: mintedKeypair.privateKey
|
|
42101
|
+
});
|
|
42102
|
+
ws.send(JSON.stringify(mintedHello));
|
|
42103
|
+
};
|
|
41713
42104
|
wss.on("connection", (ws) => {
|
|
41714
42105
|
let identified = null;
|
|
41715
42106
|
let peerMcpId = null;
|
|
@@ -41900,6 +42291,7 @@ async function startHost(opts) {
|
|
|
41900
42291
|
if (extensionWs !== ws)
|
|
41901
42292
|
return;
|
|
41902
42293
|
ownSession = new SessionState(key);
|
|
42294
|
+
ownDecryptFailures = 0;
|
|
41903
42295
|
ownPendingPairCode = null;
|
|
41904
42296
|
resolveOwnSession(ownSession);
|
|
41905
42297
|
} else {
|
|
@@ -41921,15 +42313,42 @@ async function startHost(opts) {
|
|
|
41921
42313
|
}
|
|
41922
42314
|
if (claim2 !== "ok")
|
|
41923
42315
|
return;
|
|
41924
|
-
let
|
|
42316
|
+
let result;
|
|
41925
42317
|
try {
|
|
41926
|
-
|
|
42318
|
+
result = await openEncryptedFrameDetailed(session.sessionKey, frame, "e2s");
|
|
41927
42319
|
} catch (e) {
|
|
41928
42320
|
session.releaseInboundSeq(frame.seq);
|
|
41929
42321
|
throw e;
|
|
41930
42322
|
}
|
|
42323
|
+
if (result.stage === "decrypt-failed") {
|
|
42324
|
+
session.releaseInboundSeq(frame.seq);
|
|
42325
|
+
console.warn(`[fetchproxy] ${opts.ownServerName}: dropped an inbound frame (seq ${frame.seq}) that failed authentication \u2014 most likely a straggler from a previous session.`);
|
|
42326
|
+
if (session === ownSession) {
|
|
42327
|
+
ownDecryptFailures += 1;
|
|
42328
|
+
if (ownDecryptFailures >= OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE) {
|
|
42329
|
+
ownDecryptFailures = 0;
|
|
42330
|
+
void rehandshakeOwnSession(ws).catch((e) => console.error(`[fetchproxy] ${opts.ownServerName}: re-handshake failed:`, e));
|
|
42331
|
+
}
|
|
42332
|
+
}
|
|
42333
|
+
return;
|
|
42334
|
+
}
|
|
42335
|
+
if (session === ownSession)
|
|
42336
|
+
ownDecryptFailures = 0;
|
|
41931
42337
|
session.commitInboundSeq(frame.seq);
|
|
41932
|
-
|
|
42338
|
+
if (result.stage === "ok") {
|
|
42339
|
+
ownInnerListeners.forEach((cb) => cb(result.inner));
|
|
42340
|
+
} else {
|
|
42341
|
+
console.error("[fetchproxy] host: received a frame that decrypted OK but failed validation:", result.error);
|
|
42342
|
+
const recoveredId = result.recoveredId;
|
|
42343
|
+
if (recoveredId !== void 0) {
|
|
42344
|
+
ownInnerListeners.forEach((cb) => cb({
|
|
42345
|
+
type: "response",
|
|
42346
|
+
id: recoveredId,
|
|
42347
|
+
ok: false,
|
|
42348
|
+
error: `malformed response failed protocol validation: ${String(result.error)}`
|
|
42349
|
+
}));
|
|
42350
|
+
}
|
|
42351
|
+
}
|
|
41933
42352
|
} else {
|
|
41934
42353
|
const slot = peers.get(frame.mcpId);
|
|
41935
42354
|
if (slot)
|
|
@@ -41988,6 +42407,7 @@ async function startHost(opts) {
|
|
|
41988
42407
|
rejectOwnSession(new Error("extension disconnected before ready"));
|
|
41989
42408
|
}
|
|
41990
42409
|
ownSession = dropOwnSessionAndEphemeral();
|
|
42410
|
+
ownDecryptFailures = 0;
|
|
41991
42411
|
ownPendingPairCode = null;
|
|
41992
42412
|
resetSessionPromise();
|
|
41993
42413
|
disconnectListeners.forEach((cb) => cb());
|
|
@@ -42002,8 +42422,15 @@ async function startHost(opts) {
|
|
|
42002
42422
|
}
|
|
42003
42423
|
}
|
|
42004
42424
|
if (identified === "peer" && peerMcpId) {
|
|
42005
|
-
if (peers.get(peerMcpId)?.ws === ws)
|
|
42425
|
+
if (peers.get(peerMcpId)?.ws === ws) {
|
|
42006
42426
|
peers.delete(peerMcpId);
|
|
42427
|
+
if (extensionWs && extensionHello?.accepts?.includes("peer-gone")) {
|
|
42428
|
+
try {
|
|
42429
|
+
extensionWs.send(JSON.stringify({ type: "peer-gone", mcpId: peerMcpId }));
|
|
42430
|
+
} catch {
|
|
42431
|
+
}
|
|
42432
|
+
}
|
|
42433
|
+
}
|
|
42007
42434
|
}
|
|
42008
42435
|
});
|
|
42009
42436
|
});
|
|
@@ -42044,7 +42471,7 @@ async function startHost(opts) {
|
|
|
42044
42471
|
sessionLinked: () => ownSession !== null
|
|
42045
42472
|
};
|
|
42046
42473
|
}
|
|
42047
|
-
var HTTP_ORIGIN_RE, PUBLIC_ORIGIN_RE, EXTENSION_ORIGIN_RE, OPAQUE_ORIGIN, ALLOW_LOCAL_ORIGINS_ENV, HANDSHAKE_TIMEOUT_MS, MAX_PAYLOAD_BYTES, enc4;
|
|
42474
|
+
var HTTP_ORIGIN_RE, PUBLIC_ORIGIN_RE, EXTENSION_ORIGIN_RE, OPAQUE_ORIGIN, ALLOW_LOCAL_ORIGINS_ENV, HANDSHAKE_TIMEOUT_MS, MAX_PAYLOAD_BYTES, enc4, OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE;
|
|
42048
42475
|
var init_host = __esm({
|
|
42049
42476
|
"node_modules/@fetchproxy/server/dist/host.js"() {
|
|
42050
42477
|
init_wrapper();
|
|
@@ -42062,15 +42489,21 @@ var init_host = __esm({
|
|
|
42062
42489
|
HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
42063
42490
|
MAX_PAYLOAD_BYTES = MAX_FRAME_BYTES;
|
|
42064
42491
|
enc4 = new TextEncoder();
|
|
42492
|
+
OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE = 3;
|
|
42065
42493
|
}
|
|
42066
42494
|
});
|
|
42067
42495
|
|
|
42068
42496
|
// node_modules/@fetchproxy/server/dist/peer.js
|
|
42497
|
+
function notSentError() {
|
|
42498
|
+
return new Error("fetchproxy: request not sent \u2014 the connection to the bridge host closed before it went out, so nothing reached the browser. It is safe to retry.");
|
|
42499
|
+
}
|
|
42069
42500
|
async function startPeer(opts) {
|
|
42070
42501
|
const ws = new import_websocket.default(`ws://${opts.host}:${opts.port}`, {
|
|
42071
42502
|
maxPayload: opts.maxPayloadBytes ?? MAX_PAYLOAD_BYTES
|
|
42072
42503
|
});
|
|
42504
|
+
const dialTimeoutMs = opts.dialTimeoutMs ?? PEER_DIAL_TIMEOUT_MS;
|
|
42073
42505
|
await new Promise((resolve2, reject) => {
|
|
42506
|
+
let timer;
|
|
42074
42507
|
const onOpen = () => {
|
|
42075
42508
|
cleanup();
|
|
42076
42509
|
resolve2();
|
|
@@ -42080,11 +42513,21 @@ async function startPeer(opts) {
|
|
|
42080
42513
|
reject(e);
|
|
42081
42514
|
};
|
|
42082
42515
|
const cleanup = () => {
|
|
42516
|
+
if (timer !== void 0)
|
|
42517
|
+
clearTimeout(timer);
|
|
42083
42518
|
ws.off("open", onOpen);
|
|
42084
42519
|
ws.off("error", onError);
|
|
42085
42520
|
};
|
|
42086
42521
|
ws.once("open", onOpen);
|
|
42087
42522
|
ws.once("error", onError);
|
|
42523
|
+
if (dialTimeoutMs > 0) {
|
|
42524
|
+
timer = setTimeout(() => {
|
|
42525
|
+
cleanup();
|
|
42526
|
+
ws.on("error", () => void 0);
|
|
42527
|
+
ws.terminate();
|
|
42528
|
+
reject(new Error(`fetchproxy: the process holding ${opts.host}:${opts.port} did not answer the WebSocket upgrade within ${dialTimeoutMs}ms \u2014 it may be wedged or not a fetchproxy host. Restart the MCP that owns the port (or free it) and retry.`));
|
|
42529
|
+
}, dialTimeoutMs);
|
|
42530
|
+
}
|
|
42088
42531
|
});
|
|
42089
42532
|
ws.on("error", (e) => {
|
|
42090
42533
|
console.warn(`[fetchproxy] peer: socket error: ${String(e)}`);
|
|
@@ -42103,6 +42546,7 @@ async function startPeer(opts) {
|
|
|
42103
42546
|
captureHeaders: opts.captureHeaders,
|
|
42104
42547
|
indexedDbScopes: opts.indexedDbScopes,
|
|
42105
42548
|
domSelectors: opts.domSelectors,
|
|
42549
|
+
domListSelectors: opts.domListSelectors,
|
|
42106
42550
|
localStoragePointers: opts.localStoragePointers,
|
|
42107
42551
|
sessionStoragePointers: opts.sessionStoragePointers,
|
|
42108
42552
|
graphqlOps: opts.graphqlOps,
|
|
@@ -42126,6 +42570,7 @@ async function startPeer(opts) {
|
|
|
42126
42570
|
const renegotiateListeners = [];
|
|
42127
42571
|
const pendingPairListeners = [];
|
|
42128
42572
|
const closeListeners = [];
|
|
42573
|
+
const extensionDisconnectListeners = [];
|
|
42129
42574
|
let session = null;
|
|
42130
42575
|
let sessionEphemeral = null;
|
|
42131
42576
|
function dropSessionEphemeral() {
|
|
@@ -42145,10 +42590,24 @@ async function startPeer(opts) {
|
|
|
42145
42590
|
let warnedUnverifiablePairCode = false;
|
|
42146
42591
|
let resolveFirstReady;
|
|
42147
42592
|
let rejectFirstReady;
|
|
42148
|
-
|
|
42149
|
-
|
|
42150
|
-
|
|
42151
|
-
|
|
42593
|
+
let sessionPromise;
|
|
42594
|
+
let sessionPromiseSettled = false;
|
|
42595
|
+
function resetSessionPromise() {
|
|
42596
|
+
sessionPromiseSettled = false;
|
|
42597
|
+
sessionPromise = new Promise((resolve2, reject) => {
|
|
42598
|
+
resolveFirstReady = (st) => {
|
|
42599
|
+
sessionPromiseSettled = true;
|
|
42600
|
+
resolve2(st);
|
|
42601
|
+
};
|
|
42602
|
+
rejectFirstReady = (e) => {
|
|
42603
|
+
sessionPromiseSettled = true;
|
|
42604
|
+
reject(e);
|
|
42605
|
+
};
|
|
42606
|
+
});
|
|
42607
|
+
sessionPromise.catch(() => {
|
|
42608
|
+
});
|
|
42609
|
+
}
|
|
42610
|
+
resetSessionPromise();
|
|
42152
42611
|
let extensionHello = null;
|
|
42153
42612
|
let extensionGone = false;
|
|
42154
42613
|
let cachedPin = void 0;
|
|
@@ -42245,6 +42704,12 @@ async function startPeer(opts) {
|
|
|
42245
42704
|
extensionGone = true;
|
|
42246
42705
|
dropSessionEphemeral();
|
|
42247
42706
|
pendingPairCode = null;
|
|
42707
|
+
if (session !== null) {
|
|
42708
|
+
session = null;
|
|
42709
|
+
if (sessionPromiseSettled)
|
|
42710
|
+
resetSessionPromise();
|
|
42711
|
+
}
|
|
42712
|
+
extensionDisconnectListeners.forEach((cb) => cb());
|
|
42248
42713
|
return;
|
|
42249
42714
|
}
|
|
42250
42715
|
if (frame.type === "ready" && frame.mcpId === opts.mcpId) {
|
|
@@ -42361,19 +42826,30 @@ async function startPeer(opts) {
|
|
|
42361
42826
|
}
|
|
42362
42827
|
closeListeners.forEach((cb) => cb());
|
|
42363
42828
|
});
|
|
42364
|
-
sessionPromise.catch(() => {
|
|
42365
|
-
});
|
|
42366
42829
|
const handle = {
|
|
42367
42830
|
ws,
|
|
42368
|
-
|
|
42831
|
+
// A getter: `extension-disconnected` replaces the promise (B-BUG-5).
|
|
42832
|
+
get session() {
|
|
42833
|
+
return sessionPromise;
|
|
42834
|
+
},
|
|
42369
42835
|
sendInner: async (inner) => {
|
|
42370
|
-
|
|
42371
|
-
|
|
42372
|
-
|
|
42373
|
-
|
|
42836
|
+
try {
|
|
42837
|
+
await awaitSessionReady(sessionPromise, {
|
|
42838
|
+
mcpId: opts.mcpId,
|
|
42839
|
+
pendingPairCode: () => pendingPairCode
|
|
42840
|
+
});
|
|
42841
|
+
} catch (e) {
|
|
42842
|
+
if (ws.readyState !== import_websocket.default.OPEN)
|
|
42843
|
+
throw notSentError();
|
|
42844
|
+
throw e;
|
|
42845
|
+
}
|
|
42374
42846
|
const s = session;
|
|
42847
|
+
if (s === null)
|
|
42848
|
+
throw new Error("peer: extension disconnected");
|
|
42375
42849
|
const plaintext = encodeOutboundInnerFrame(opts.mcpId, inner);
|
|
42376
42850
|
const sealed = await sealInnerFrame(s.sessionKey, opts.mcpId, s.nextOutboundSeq(), plaintext, "s2e");
|
|
42851
|
+
if (ws.readyState !== import_websocket.default.OPEN)
|
|
42852
|
+
throw notSentError();
|
|
42377
42853
|
ws.send(JSON.stringify(sealed));
|
|
42378
42854
|
},
|
|
42379
42855
|
onInner: (cb) => {
|
|
@@ -42391,11 +42867,14 @@ async function startPeer(opts) {
|
|
|
42391
42867
|
onClose: (cb) => {
|
|
42392
42868
|
closeListeners.push(cb);
|
|
42393
42869
|
},
|
|
42870
|
+
onExtensionDisconnect: (cb) => {
|
|
42871
|
+
extensionDisconnectListeners.push(cb);
|
|
42872
|
+
},
|
|
42394
42873
|
close: () => ws.close()
|
|
42395
42874
|
};
|
|
42396
42875
|
return handle;
|
|
42397
42876
|
}
|
|
42398
|
-
var enc5;
|
|
42877
|
+
var PEER_DIAL_TIMEOUT_MS, enc5;
|
|
42399
42878
|
var init_peer = __esm({
|
|
42400
42879
|
"node_modules/@fetchproxy/server/dist/peer.js"() {
|
|
42401
42880
|
init_wrapper();
|
|
@@ -42406,21 +42885,22 @@ var init_peer = __esm({
|
|
|
42406
42885
|
init_session();
|
|
42407
42886
|
init_session_ready();
|
|
42408
42887
|
init_extension_trust();
|
|
42888
|
+
PEER_DIAL_TIMEOUT_MS = 5e3;
|
|
42409
42889
|
enc5 = new TextEncoder();
|
|
42410
42890
|
}
|
|
42411
42891
|
});
|
|
42412
42892
|
|
|
42413
42893
|
// node_modules/@fetchproxy/server/dist/error-kind.js
|
|
42414
42894
|
function classifyFetchError(error62) {
|
|
42895
|
+
if (/^(in-page )?fetch threw:/.test(error62)) {
|
|
42896
|
+
return "tab_fetch_failed";
|
|
42897
|
+
}
|
|
42415
42898
|
if (/Could not establish connection/i.test(error62) || /Receiving end does not exist/i.test(error62)) {
|
|
42416
42899
|
return "content_script_unreachable";
|
|
42417
42900
|
}
|
|
42418
42901
|
if (/^tab fetch failed:/.test(error62)) {
|
|
42419
42902
|
return "tab_fetch_failed";
|
|
42420
42903
|
}
|
|
42421
|
-
if (/^(in-page )?fetch threw:/.test(error62)) {
|
|
42422
|
-
return "tab_fetch_failed";
|
|
42423
|
-
}
|
|
42424
42904
|
if (/^no tab matching /.test(error62)) {
|
|
42425
42905
|
return "no_tab";
|
|
42426
42906
|
}
|
|
@@ -42548,7 +43028,32 @@ function assertUrlInDomains(field, url2, domains) {
|
|
|
42548
43028
|
const declared = domains.map((d) => JSON.stringify(d)).join(", ");
|
|
42549
43029
|
throw new Error(`FetchproxyServer: ${field} host "${host}" is outside declared domains [${declared}] \u2014 must be one of them or a subdomain`);
|
|
42550
43030
|
}
|
|
42551
|
-
|
|
43031
|
+
function parseJsonBody(response) {
|
|
43032
|
+
if (response.status === 204 || response.body.trim() === "")
|
|
43033
|
+
return null;
|
|
43034
|
+
return JSON.parse(response.body);
|
|
43035
|
+
}
|
|
43036
|
+
function isRetrySafeOnTimeout(method, retryOnTimeout) {
|
|
43037
|
+
if (retryOnTimeout !== void 0)
|
|
43038
|
+
return retryOnTimeout;
|
|
43039
|
+
return TIMEOUT_RETRY_SAFE_METHODS.has(method.toUpperCase());
|
|
43040
|
+
}
|
|
43041
|
+
function isResendableRequest(inner) {
|
|
43042
|
+
switch (inner.op) {
|
|
43043
|
+
case "fetch":
|
|
43044
|
+
return isRetrySafeOnTimeout(inner.init.method);
|
|
43045
|
+
case "write_cookies":
|
|
43046
|
+
case "download":
|
|
43047
|
+
// A declared GraphQL operation may be a mutation; the server holds only
|
|
43048
|
+
// its name, so it cannot tell. `graphqlQuery({ retryOnTimeout: true })`
|
|
43049
|
+
// passes `resendable` explicitly for a known query.
|
|
43050
|
+
case "graphql_query":
|
|
43051
|
+
return false;
|
|
43052
|
+
default:
|
|
43053
|
+
return true;
|
|
43054
|
+
}
|
|
43055
|
+
}
|
|
43056
|
+
var FetchproxyProtocolError, FetchproxyHttpError, FetchproxyBridgeDownError, FetchproxyHintedError, FetchproxyScopeError, FetchproxyNoTabError, FetchproxyTabOpeningError, FetchproxyWaitedError, SCOPE_REJECTION, NO_TAB_REJECTION, TAB_OPENING_REJECTION, VERB_DEADLINE_GRACE_MS, FetchproxyTimeoutError, SUBDOMAIN_LABEL_RE, DEFAULT_JSON_OK_STATUSES, TIMEOUT_RETRY_SAFE_METHODS, FetchproxyServer;
|
|
42552
43057
|
var init_ws_server = __esm({
|
|
42553
43058
|
"node_modules/@fetchproxy/server/dist/ws-server.js"() {
|
|
42554
43059
|
init_dist3();
|
|
@@ -42668,6 +43173,17 @@ var init_ws_server = __esm({
|
|
|
42668
43173
|
* the message says which number that was and where it came from.
|
|
42669
43174
|
*/
|
|
42670
43175
|
requestedTimeoutMs;
|
|
43176
|
+
/**
|
|
43177
|
+
* B-BUG-1: whether re-sending this request is safe. A timeout only means
|
|
43178
|
+
* the reply did not arrive in time — the extension may already have run the
|
|
43179
|
+
* request in the tab — so this is `false` for a non-idempotent `fetch`
|
|
43180
|
+
* (anything but GET/HEAD/OPTIONS) the caller did not mark `retryOnTimeout`,
|
|
43181
|
+
* for `download` and `writeCookies`, and for `graphqlQuery` unless the
|
|
43182
|
+
* caller marked it `retryOnTimeout` (a declared operation may be a
|
|
43183
|
+
* mutation). `retryOnceOnTimeout` honours it. Defaults to `true` (the read
|
|
43184
|
+
* verbs).
|
|
43185
|
+
*/
|
|
43186
|
+
retrySafe;
|
|
42671
43187
|
constructor(args) {
|
|
42672
43188
|
const graceMs = args.graceMs ?? VERB_DEADLINE_GRACE_MS;
|
|
42673
43189
|
const callerChose = args.requestedTimeoutMs !== void 0 && args.timeoutMs === args.requestedTimeoutMs + graceMs;
|
|
@@ -42680,10 +43196,12 @@ var init_ws_server = __esm({
|
|
|
42680
43196
|
this.elapsedMs = args.elapsedMs ?? args.timeoutMs;
|
|
42681
43197
|
this.retryAttempted = args.retryAttempted ?? false;
|
|
42682
43198
|
this.requestedTimeoutMs = args.requestedTimeoutMs ?? null;
|
|
43199
|
+
this.retrySafe = args.retrySafe ?? true;
|
|
42683
43200
|
}
|
|
42684
43201
|
};
|
|
42685
43202
|
SUBDOMAIN_LABEL_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
|
42686
43203
|
DEFAULT_JSON_OK_STATUSES = [200, 201, 202, 204];
|
|
43204
|
+
TIMEOUT_RETRY_SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
42687
43205
|
FetchproxyServer = class {
|
|
42688
43206
|
/**
|
|
42689
43207
|
* Bridge role. `null` until the first verb call (or an explicit
|
|
@@ -42732,12 +43250,22 @@ var init_ws_server = __esm({
|
|
|
42732
43250
|
pendingRedirect = /* @__PURE__ */ new Map();
|
|
42733
43251
|
// 0.4.0+: read_indexed_db awaiters resolve a JSON-typed values map.
|
|
42734
43252
|
pendingIdb = /* @__PURE__ */ new Map();
|
|
43253
|
+
// 3.1.0+: read_dom_list awaiters resolve an array of field-value rows —
|
|
43254
|
+
// a different shape from `pendingStorage`'s single map, so its own map.
|
|
43255
|
+
pendingDomList = /* @__PURE__ */ new Map();
|
|
42735
43256
|
// download awaiters resolve the saved-file metadata (path + size + mime).
|
|
42736
43257
|
pendingDownload = /* @__PURE__ */ new Map();
|
|
42737
43258
|
// 1.x+: graphql_query awaiters resolve the GraphQL `data` object. Its
|
|
42738
43259
|
// shape is operation-specific, so the awaiter resolves `unknown` and the
|
|
42739
43260
|
// caller narrows.
|
|
42740
43261
|
pendingGraphql = /* @__PURE__ */ new Map();
|
|
43262
|
+
/**
|
|
43263
|
+
* B-BUG-6: the frames of in-flight requests that are safe to send again
|
|
43264
|
+
* if the host that relayed them exits before replying (see
|
|
43265
|
+
* `recoverFromHostLoss`). Keyed by request id; an entry outlives its
|
|
43266
|
+
* request only until the next prune or response.
|
|
43267
|
+
*/
|
|
43268
|
+
resendable = /* @__PURE__ */ new Map();
|
|
42741
43269
|
mcpId = null;
|
|
42742
43270
|
identity = null;
|
|
42743
43271
|
// 0.5.3+: in-flight role-election / handle-start promise. Set the
|
|
@@ -42783,7 +43311,7 @@ var init_ws_server = __esm({
|
|
|
42783
43311
|
}
|
|
42784
43312
|
for (const c of opts.capabilities) {
|
|
42785
43313
|
if (!KNOWN_CAPABILITIES.has(c)) {
|
|
42786
|
-
throw new Error(`FetchproxyServer: unknown capability ${JSON.stringify(c)} \u2014 known values: [
|
|
43314
|
+
throw new Error(`FetchproxyServer: unknown capability ${JSON.stringify(c)} \u2014 known values: ${JSON.stringify([...KNOWN_CAPABILITIES])}`);
|
|
42787
43315
|
}
|
|
42788
43316
|
}
|
|
42789
43317
|
capabilities = [...opts.capabilities];
|
|
@@ -42830,6 +43358,16 @@ var init_ws_server = __esm({
|
|
|
42830
43358
|
selector: d.selector,
|
|
42831
43359
|
...d.attribute !== void 0 ? { attribute: d.attribute } : {}
|
|
42832
43360
|
})),
|
|
43361
|
+
domListSelectors: (opts.domListSelectors ?? []).map((d) => ({
|
|
43362
|
+
name: d.name,
|
|
43363
|
+
itemSelector: d.itemSelector,
|
|
43364
|
+
fields: d.fields.map((f) => ({
|
|
43365
|
+
name: f.name,
|
|
43366
|
+
...f.selector !== void 0 ? { selector: f.selector } : {},
|
|
43367
|
+
...f.attribute !== void 0 ? { attribute: f.attribute } : {}
|
|
43368
|
+
})),
|
|
43369
|
+
...d.maxItems !== void 0 ? { maxItems: d.maxItems } : {}
|
|
43370
|
+
})),
|
|
42833
43371
|
graphqlOps: (opts.graphqlOps ?? []).map((d) => ({
|
|
42834
43372
|
name: d.name,
|
|
42835
43373
|
operationName: d.operationName
|
|
@@ -42959,6 +43497,7 @@ var init_ws_server = __esm({
|
|
|
42959
43497
|
ownLocalStoragePointers: this.opts.localStoragePointers,
|
|
42960
43498
|
ownSessionStoragePointers: this.opts.sessionStoragePointers,
|
|
42961
43499
|
ownDomSelectors: this.opts.domSelectors,
|
|
43500
|
+
ownDomListSelectors: this.opts.domListSelectors,
|
|
42962
43501
|
ownGraphqlOps: this.opts.graphqlOps,
|
|
42963
43502
|
onPairCode: this.opts.onPairCode,
|
|
42964
43503
|
extensionTrust: this.extensionTrust()
|
|
@@ -42990,6 +43529,7 @@ var init_ws_server = __esm({
|
|
|
42990
43529
|
localStoragePointers: this.opts.localStoragePointers,
|
|
42991
43530
|
sessionStoragePointers: this.opts.sessionStoragePointers,
|
|
42992
43531
|
domSelectors: this.opts.domSelectors,
|
|
43532
|
+
domListSelectors: this.opts.domListSelectors,
|
|
42993
43533
|
graphqlOps: this.opts.graphqlOps,
|
|
42994
43534
|
extensionTrust: this.extensionTrust(),
|
|
42995
43535
|
requireExtensionIdentity: this.opts.requireExtensionIdentity
|
|
@@ -42999,6 +43539,10 @@ var init_ws_server = __esm({
|
|
|
42999
43539
|
this.stopKeepalive();
|
|
43000
43540
|
this.rejectAllPending();
|
|
43001
43541
|
});
|
|
43542
|
+
this.peerHandle.onExtensionDisconnect(() => {
|
|
43543
|
+
this.stopKeepalive();
|
|
43544
|
+
this.rejectAllPending();
|
|
43545
|
+
});
|
|
43002
43546
|
this.peerHandle.onPendingPair((code) => {
|
|
43003
43547
|
this.rejectAllPending(this.pairingErrorMessage(code));
|
|
43004
43548
|
});
|
|
@@ -43010,9 +43554,9 @@ var init_ws_server = __esm({
|
|
|
43010
43554
|
if (this.closing || this.peerHandle === null)
|
|
43011
43555
|
return;
|
|
43012
43556
|
this.stopKeepalive();
|
|
43013
|
-
this.rejectAllPending();
|
|
43014
43557
|
this.peerHandle = null;
|
|
43015
43558
|
this.role = null;
|
|
43559
|
+
void this.recoverFromHostLoss();
|
|
43016
43560
|
});
|
|
43017
43561
|
}
|
|
43018
43562
|
}
|
|
@@ -43033,7 +43577,7 @@ var init_ws_server = __esm({
|
|
|
43033
43577
|
* only when the bridge itself failed (no signed-in tab, extension
|
|
43034
43578
|
* offline, etc.).
|
|
43035
43579
|
*/
|
|
43036
|
-
async fetch(init) {
|
|
43580
|
+
async fetch(init, fetchOpts = {}) {
|
|
43037
43581
|
await this.ensureConnected();
|
|
43038
43582
|
const pendingCode = this.currentPendingPairCode();
|
|
43039
43583
|
if (pendingCode !== null) {
|
|
@@ -43045,16 +43589,17 @@ var init_ws_server = __esm({
|
|
|
43045
43589
|
retryAttempted: false
|
|
43046
43590
|
};
|
|
43047
43591
|
}
|
|
43048
|
-
const first = await this._fetchOnceWithTimeout(init);
|
|
43592
|
+
const first = await this._fetchOnceWithTimeout(init, fetchOpts);
|
|
43049
43593
|
const reviveMs = this.opts.bridgeReviveDelayMs;
|
|
43050
43594
|
const isColdStartSymptom = !first.ok && (first.kind === "content_script_unreachable" || first.kind === "timeout");
|
|
43051
43595
|
if (isColdStartSymptom) {
|
|
43052
43596
|
this.lastEvictionDetectedAt = Date.now();
|
|
43053
43597
|
}
|
|
43054
|
-
|
|
43598
|
+
const retryable = isColdStartSymptom && (first.kind === "content_script_unreachable" || isRetrySafeOnTimeout(init.method, fetchOpts.retryOnTimeout));
|
|
43599
|
+
if (retryable && reviveMs !== void 0 && reviveMs > 0) {
|
|
43055
43600
|
this.lazyReviveAttempts += 1;
|
|
43056
43601
|
await new Promise((r) => setTimeout(r, reviveMs));
|
|
43057
|
-
const second = await this._fetchOnceWithTimeout(init);
|
|
43602
|
+
const second = await this._fetchOnceWithTimeout(init, fetchOpts);
|
|
43058
43603
|
if (second.ok) {
|
|
43059
43604
|
this.lazyReviveSuccesses += 1;
|
|
43060
43605
|
this.recordSuccess();
|
|
@@ -43203,13 +43748,16 @@ var init_ws_server = __esm({
|
|
|
43203
43748
|
* one of the op maps — request ids are unique) so it doesn't leak until the
|
|
43204
43749
|
* server closes, then rethrow.
|
|
43205
43750
|
*/
|
|
43206
|
-
async sendInnerFrame(inner) {
|
|
43751
|
+
async sendInnerFrame(inner, opts = {}) {
|
|
43207
43752
|
try {
|
|
43208
43753
|
if (this.hostHandle) {
|
|
43209
43754
|
await this.hostHandle.sendOwnInner(inner);
|
|
43210
43755
|
} else if (this.peerHandle) {
|
|
43211
43756
|
await this.peerHandle.sendInner(inner);
|
|
43212
43757
|
}
|
|
43758
|
+
if (inner.type === "request" && (opts.resendable ?? isResendableRequest(inner))) {
|
|
43759
|
+
this.noteResendable(inner);
|
|
43760
|
+
}
|
|
43213
43761
|
} catch (err) {
|
|
43214
43762
|
if ("id" in inner && typeof inner.id === "number") {
|
|
43215
43763
|
const { id } = inner;
|
|
@@ -43221,6 +43769,7 @@ var init_ws_server = __esm({
|
|
|
43221
43769
|
this.pendingRedirect.delete(id);
|
|
43222
43770
|
this.pendingDownload.delete(id);
|
|
43223
43771
|
this.pendingIdb.delete(id);
|
|
43772
|
+
this.pendingDomList.delete(id);
|
|
43224
43773
|
this.pendingGraphql.delete(id);
|
|
43225
43774
|
}
|
|
43226
43775
|
throw err;
|
|
@@ -43231,13 +43780,25 @@ var init_ws_server = __esm({
|
|
|
43231
43780
|
* On timeout returns the `{ok:false, kind:'timeout'}` envelope —
|
|
43232
43781
|
* the throwing surface is the convenience methods.
|
|
43233
43782
|
*/
|
|
43234
|
-
async _fetchOnceWithTimeout(init) {
|
|
43783
|
+
async _fetchOnceWithTimeout(init, fetchOpts = {}) {
|
|
43235
43784
|
const id = this.nextRequestId++;
|
|
43236
43785
|
const inner = { type: "request", id, op: "fetch", init };
|
|
43237
43786
|
const pending = this.guardPending(new Promise((resolve2) => {
|
|
43238
43787
|
this.pending.set(id, resolve2);
|
|
43239
43788
|
}));
|
|
43240
|
-
|
|
43789
|
+
try {
|
|
43790
|
+
await this.sendInnerFrame(inner, {
|
|
43791
|
+
resendable: isRetrySafeOnTimeout(init.method, fetchOpts.retryOnTimeout)
|
|
43792
|
+
});
|
|
43793
|
+
} catch (err) {
|
|
43794
|
+
return {
|
|
43795
|
+
ok: false,
|
|
43796
|
+
error: err instanceof Error ? err.message : String(err),
|
|
43797
|
+
kind: "session_not_ready",
|
|
43798
|
+
retryAttempted: false,
|
|
43799
|
+
cause: err
|
|
43800
|
+
};
|
|
43801
|
+
}
|
|
43241
43802
|
const timeoutMs = this.opts.fetchTimeoutMs;
|
|
43242
43803
|
if (timeoutMs === void 0 || timeoutMs <= 0)
|
|
43243
43804
|
return pending;
|
|
@@ -43311,7 +43872,7 @@ var init_ws_server = __esm({
|
|
|
43311
43872
|
});
|
|
43312
43873
|
return pending;
|
|
43313
43874
|
}
|
|
43314
|
-
async _withVerbTimeout(pending, pendingMap, id, url2, requestedTimeoutMs) {
|
|
43875
|
+
async _withVerbTimeout(pending, pendingMap, id, url2, requestedTimeoutMs, retrySafe = true) {
|
|
43315
43876
|
const transportMs = this.opts.fetchTimeoutMs;
|
|
43316
43877
|
if (transportMs === void 0 || transportMs <= 0)
|
|
43317
43878
|
return pending;
|
|
@@ -43332,6 +43893,7 @@ var init_ws_server = __esm({
|
|
|
43332
43893
|
port: this.opts.port,
|
|
43333
43894
|
elapsedMs: Date.now() - start,
|
|
43334
43895
|
retryAttempted: false,
|
|
43896
|
+
retrySafe,
|
|
43335
43897
|
graceMs,
|
|
43336
43898
|
...requestedTimeoutMs !== void 0 ? { requestedTimeoutMs } : {}
|
|
43337
43899
|
}));
|
|
@@ -43349,7 +43911,7 @@ var init_ws_server = __esm({
|
|
|
43349
43911
|
* logic re-implemented inline) `captureRequestHeader()` agree on what
|
|
43350
43912
|
* to throw.
|
|
43351
43913
|
*/
|
|
43352
|
-
_typedErrorFor(result, url2, op, retryAttempted) {
|
|
43914
|
+
_typedErrorFor(result, url2, op, retryAttempted, retrySafe = true) {
|
|
43353
43915
|
if (result.kind === "timeout") {
|
|
43354
43916
|
return new FetchproxyTimeoutError({
|
|
43355
43917
|
url: url2,
|
|
@@ -43357,7 +43919,8 @@ var init_ws_server = __esm({
|
|
|
43357
43919
|
role: this.role,
|
|
43358
43920
|
port: this.opts.port,
|
|
43359
43921
|
elapsedMs: result.elapsedMs,
|
|
43360
|
-
retryAttempted
|
|
43922
|
+
retryAttempted,
|
|
43923
|
+
retrySafe
|
|
43361
43924
|
});
|
|
43362
43925
|
}
|
|
43363
43926
|
if (result.kind === "content_script_unreachable") {
|
|
@@ -43431,9 +43994,12 @@ var init_ws_server = __esm({
|
|
|
43431
43994
|
// every version before this option existed.
|
|
43432
43995
|
...opts.credentials === "omit" ? { credentials: "omit" } : {}
|
|
43433
43996
|
};
|
|
43434
|
-
const result = await this.fetch(init);
|
|
43997
|
+
const result = await this.fetch(init, opts.retryOnTimeout !== void 0 ? { retryOnTimeout: opts.retryOnTimeout } : {});
|
|
43435
43998
|
if (!result.ok) {
|
|
43436
|
-
|
|
43999
|
+
if (result.kind === "session_not_ready" && result.cause instanceof Error) {
|
|
44000
|
+
throw result.cause;
|
|
44001
|
+
}
|
|
44002
|
+
throw this._typedErrorFor(result, init.url, "fetch", result.retryAttempted ?? false, isRetrySafeOnTimeout(method, opts.retryOnTimeout));
|
|
43437
44003
|
}
|
|
43438
44004
|
const response = {
|
|
43439
44005
|
status: result.status,
|
|
@@ -43473,17 +44039,21 @@ var init_ws_server = __esm({
|
|
|
43473
44039
|
* GET a path and parse the response body as JSON. Throws
|
|
43474
44040
|
* `FetchproxyHttpError` if the status is outside the default 2xx
|
|
43475
44041
|
* happy-path set (`[200, 201, 202, 204]`); pass a custom
|
|
43476
|
-
* `expectStatus` to override.
|
|
44042
|
+
* `expectStatus` to override. A 204 or an empty/whitespace body
|
|
44043
|
+
* resolves to `null` (the declared `T` does not say so, to keep
|
|
44044
|
+
* existing call sites compiling — include `| null` in `T` if the
|
|
44045
|
+
* endpoint can answer empty).
|
|
43477
44046
|
*/
|
|
43478
44047
|
async getJson(path, opts = {}) {
|
|
43479
44048
|
const response = await this.get(path, this.applyJsonDefaults(opts));
|
|
43480
|
-
return
|
|
44049
|
+
return parseJsonBody(response);
|
|
43481
44050
|
}
|
|
43482
44051
|
/**
|
|
43483
44052
|
* POST a JSON body and parse the response body as JSON. The body is
|
|
43484
44053
|
* `JSON.stringify`'d; `Content-Type: application/json` is set unless
|
|
43485
44054
|
* the caller already provided one. Defaults `expectStatus` to the 2xx
|
|
43486
|
-
* happy-path set.
|
|
44055
|
+
* happy-path set. A 204 or an empty/whitespace body resolves to `null`
|
|
44056
|
+
* rather than throwing after the write already happened (see `getJson`).
|
|
43487
44057
|
*/
|
|
43488
44058
|
async postJson(path, body, opts = {}) {
|
|
43489
44059
|
const headers = { ...opts.headers ?? {} };
|
|
@@ -43495,7 +44065,7 @@ var init_ws_server = __esm({
|
|
|
43495
44065
|
headers,
|
|
43496
44066
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
43497
44067
|
});
|
|
43498
|
-
return
|
|
44068
|
+
return parseJsonBody(response);
|
|
43499
44069
|
}
|
|
43500
44070
|
/**
|
|
43501
44071
|
* GET a path and return the response body as a string. Throws
|
|
@@ -43516,7 +44086,8 @@ var init_ws_server = __esm({
|
|
|
43516
44086
|
* - adds `Content-Type: application/json` only for a non-GET request
|
|
43517
44087
|
* that carries a `body` (and only if the caller didn't set one);
|
|
43518
44088
|
* - `JSON.stringify`s the body (GET / no-body sends nothing);
|
|
43519
|
-
* - treats a `204
|
|
44089
|
+
* - treats a `204`, an empty body, or a whitespace-only body as
|
|
44090
|
+
* `data: null` (no parse), the same rule as getJson/postJson;
|
|
43520
44091
|
* - otherwise `JSON.parse`s the body.
|
|
43521
44092
|
*
|
|
43522
44093
|
* Scope is serialization + header defaults + 204-handling +
|
|
@@ -43530,6 +44101,11 @@ var init_ws_server = __esm({
|
|
|
43530
44101
|
* Bridge-level failures (no signed-in tab, SW down, timeout) still
|
|
43531
44102
|
* throw the typed errors via `request()`, exactly like the verb
|
|
43532
44103
|
* helpers — only successful round-trips (any HTTP status) return.
|
|
44104
|
+
*
|
|
44105
|
+
* `retryOnTimeout` is `RequestOpts.retryOnTimeout`: a timed-out POST is
|
|
44106
|
+
* NOT re-sent unless the caller says it is safe to repeat. A read-only POST
|
|
44107
|
+
* (a search, a GraphQL query) should pass `true` to keep the cold-start
|
|
44108
|
+
* timeout retry (#90).
|
|
43533
44109
|
*/
|
|
43534
44110
|
async requestJson(method, path, opts = {}) {
|
|
43535
44111
|
const isGet = method.toUpperCase() === "GET";
|
|
@@ -43543,7 +44119,8 @@ var init_ws_server = __esm({
|
|
|
43543
44119
|
headers,
|
|
43544
44120
|
body: sendBody ? JSON.stringify(opts.body) : void 0,
|
|
43545
44121
|
...opts.subdomain !== void 0 ? { subdomain: opts.subdomain } : {},
|
|
43546
|
-
...opts.domain !== void 0 ? { domain: opts.domain } : {}
|
|
44122
|
+
...opts.domain !== void 0 ? { domain: opts.domain } : {},
|
|
44123
|
+
...opts.retryOnTimeout !== void 0 ? { retryOnTimeout: opts.retryOnTimeout } : {}
|
|
43547
44124
|
});
|
|
43548
44125
|
const result = {
|
|
43549
44126
|
ok: true,
|
|
@@ -43551,12 +44128,9 @@ var init_ws_server = __esm({
|
|
|
43551
44128
|
url: response.url,
|
|
43552
44129
|
body: response.body
|
|
43553
44130
|
};
|
|
43554
|
-
if (response.status === 204 || response.body === "") {
|
|
43555
|
-
return { data: null, result };
|
|
43556
|
-
}
|
|
43557
44131
|
let data;
|
|
43558
44132
|
try {
|
|
43559
|
-
data =
|
|
44133
|
+
data = parseJsonBody(response);
|
|
43560
44134
|
} catch (e) {
|
|
43561
44135
|
throw new Error(`fetchproxy ${method} ${path} \u2014 response was not JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
43562
44136
|
}
|
|
@@ -43613,18 +44187,19 @@ var init_ws_server = __esm({
|
|
|
43613
44187
|
};
|
|
43614
44188
|
}
|
|
43615
44189
|
/**
|
|
43616
|
-
*
|
|
44190
|
+
* Read the user's cookies for the chosen domain.
|
|
43617
44191
|
*
|
|
43618
44192
|
* Requires `'read_cookies'` in `FetchproxyServerOpts.capabilities`.
|
|
43619
44193
|
* Throws a developer-facing `Error` at the call site if the MCP did
|
|
43620
44194
|
* not declare the capability — this is a programming mistake, not a
|
|
43621
44195
|
* runtime condition.
|
|
43622
44196
|
*
|
|
43623
|
-
* The returned string is
|
|
43624
|
-
*
|
|
43625
|
-
*
|
|
43626
|
-
*
|
|
43627
|
-
*
|
|
44197
|
+
* The returned string is semicolon-separated `k=v` pairs. With `keys`
|
|
44198
|
+
* (each in the declared `cookieKeys`), the extension reads each name via
|
|
44199
|
+
* `chrome.cookies.get`, which INCLUDES HttpOnly cookies — the login
|
|
44200
|
+
* session cookie among them; that is what cookie-session MCPs use it
|
|
44201
|
+
* for. Without `keys` (legacy), it is the tab's `document.cookie`, which
|
|
44202
|
+
* omits HttpOnly cookies. See docs/SECURITY.md §T-cookie-exfil.
|
|
43628
44203
|
*
|
|
43629
44204
|
* Throws `FetchproxyProtocolError` if the bridge could not deliver
|
|
43630
44205
|
* the request (no signed-in tab, extension offline, etc.).
|
|
@@ -43720,7 +44295,15 @@ var init_ws_server = __esm({
|
|
|
43720
44295
|
this.pendingWriteCookies.set(id, { resolve: resolve2, reject });
|
|
43721
44296
|
}));
|
|
43722
44297
|
await this.sendInnerFrame(inner);
|
|
43723
|
-
return this._withVerbTimeout(
|
|
44298
|
+
return this._withVerbTimeout(
|
|
44299
|
+
pending,
|
|
44300
|
+
this.pendingWriteCookies,
|
|
44301
|
+
id,
|
|
44302
|
+
`https://${host}`,
|
|
44303
|
+
void 0,
|
|
44304
|
+
// A write: never re-sent after an ambiguous timeout.
|
|
44305
|
+
false
|
|
44306
|
+
);
|
|
43724
44307
|
}
|
|
43725
44308
|
/**
|
|
43726
44309
|
* 0.3.0+: read declared localStorage keys from the user's signed-in
|
|
@@ -43823,53 +44406,7 @@ var init_ws_server = __esm({
|
|
|
43823
44406
|
throw new Error("FetchproxyServer.captureRequestHeader: pass both host AND headerName, or neither (which defaults to the single declared entry)");
|
|
43824
44407
|
}
|
|
43825
44408
|
const callOpts = { ...resolved, ...opts?.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {} };
|
|
43826
|
-
|
|
43827
|
-
const result = await this._captureRequestHeaderOnce(callOpts);
|
|
43828
|
-
this.recordSuccess();
|
|
43829
|
-
return result;
|
|
43830
|
-
} catch (err) {
|
|
43831
|
-
const swDown = err instanceof FetchproxyProtocolError && classifyFetchError(err.message) === "content_script_unreachable";
|
|
43832
|
-
if (!swDown) {
|
|
43833
|
-
this.recordFailure(`capture_request_header: ${err.message ?? String(err)}`);
|
|
43834
|
-
throw err;
|
|
43835
|
-
}
|
|
43836
|
-
this.lastEvictionDetectedAt = Date.now();
|
|
43837
|
-
const reviveMs = this.opts.bridgeReviveDelayMs ?? 0;
|
|
43838
|
-
if (reviveMs > 0) {
|
|
43839
|
-
this.lazyReviveAttempts += 1;
|
|
43840
|
-
await new Promise((r) => setTimeout(r, reviveMs));
|
|
43841
|
-
try {
|
|
43842
|
-
const result = await this._captureRequestHeaderOnce(callOpts);
|
|
43843
|
-
this.lazyReviveSuccesses += 1;
|
|
43844
|
-
this.recordSuccess();
|
|
43845
|
-
return result;
|
|
43846
|
-
} catch (retryErr) {
|
|
43847
|
-
const stillDown = retryErr instanceof FetchproxyProtocolError && classifyFetchError(retryErr.message) === "content_script_unreachable";
|
|
43848
|
-
if (!stillDown) {
|
|
43849
|
-
this.recordFailure(`capture_request_header: ${retryErr.message ?? String(retryErr)}`);
|
|
43850
|
-
throw retryErr;
|
|
43851
|
-
}
|
|
43852
|
-
this.recordFailure(`capture_request_header bridge-down: ${retryErr.message}`);
|
|
43853
|
-
throw new FetchproxyBridgeDownError({
|
|
43854
|
-
originalError: retryErr.message,
|
|
43855
|
-
retryAttempted: true,
|
|
43856
|
-
op: "capture_request_header",
|
|
43857
|
-
url: `https://${resolved.host}${resolved.path ?? "/*"}`,
|
|
43858
|
-
role: this.role,
|
|
43859
|
-
port: this.opts.port
|
|
43860
|
-
});
|
|
43861
|
-
}
|
|
43862
|
-
}
|
|
43863
|
-
this.recordFailure(`capture_request_header bridge-down: ${err.message}`);
|
|
43864
|
-
throw new FetchproxyBridgeDownError({
|
|
43865
|
-
originalError: err.message,
|
|
43866
|
-
retryAttempted: false,
|
|
43867
|
-
op: "capture_request_header",
|
|
43868
|
-
url: `https://${resolved.host}${resolved.path ?? "/*"}`,
|
|
43869
|
-
role: this.role,
|
|
43870
|
-
port: this.opts.port
|
|
43871
|
-
});
|
|
43872
|
-
}
|
|
44409
|
+
return this.withLazyRevive("capture_request_header", `https://${resolved.host}${resolved.path ?? "/*"}`, () => this._captureRequestHeaderOnce(callOpts));
|
|
43873
44410
|
}
|
|
43874
44411
|
async _captureRequestHeaderOnce(opts) {
|
|
43875
44412
|
const id = this.nextRequestId++;
|
|
@@ -43918,53 +44455,7 @@ var init_ws_server = __esm({
|
|
|
43918
44455
|
}
|
|
43919
44456
|
await this.ensureConnected();
|
|
43920
44457
|
this.throwIfPendingPair();
|
|
43921
|
-
|
|
43922
|
-
const result = await this._captureRedirectOnce(opts);
|
|
43923
|
-
this.recordSuccess();
|
|
43924
|
-
return result;
|
|
43925
|
-
} catch (err) {
|
|
43926
|
-
const swDown = err instanceof FetchproxyProtocolError && classifyFetchError(err.message) === "content_script_unreachable";
|
|
43927
|
-
if (!swDown) {
|
|
43928
|
-
this.recordFailure(`capture_redirect: ${err.message ?? String(err)}`);
|
|
43929
|
-
throw err;
|
|
43930
|
-
}
|
|
43931
|
-
this.lastEvictionDetectedAt = Date.now();
|
|
43932
|
-
const reviveMs = this.opts.bridgeReviveDelayMs ?? 0;
|
|
43933
|
-
if (reviveMs > 0) {
|
|
43934
|
-
this.lazyReviveAttempts += 1;
|
|
43935
|
-
await new Promise((r) => setTimeout(r, reviveMs));
|
|
43936
|
-
try {
|
|
43937
|
-
const result = await this._captureRedirectOnce(opts);
|
|
43938
|
-
this.lazyReviveSuccesses += 1;
|
|
43939
|
-
this.recordSuccess();
|
|
43940
|
-
return result;
|
|
43941
|
-
} catch (retryErr) {
|
|
43942
|
-
const stillDown = retryErr instanceof FetchproxyProtocolError && classifyFetchError(retryErr.message) === "content_script_unreachable";
|
|
43943
|
-
if (!stillDown) {
|
|
43944
|
-
this.recordFailure(`capture_redirect: ${retryErr.message ?? String(retryErr)}`);
|
|
43945
|
-
throw retryErr;
|
|
43946
|
-
}
|
|
43947
|
-
this.recordFailure(`capture_redirect bridge-down: ${retryErr.message}`);
|
|
43948
|
-
throw new FetchproxyBridgeDownError({
|
|
43949
|
-
originalError: retryErr.message,
|
|
43950
|
-
retryAttempted: true,
|
|
43951
|
-
op: "capture_redirect",
|
|
43952
|
-
url: `https://${opts.host}${opts.path ?? "/*"}`,
|
|
43953
|
-
role: this.role,
|
|
43954
|
-
port: this.opts.port
|
|
43955
|
-
});
|
|
43956
|
-
}
|
|
43957
|
-
}
|
|
43958
|
-
this.recordFailure(`capture_redirect bridge-down: ${err.message}`);
|
|
43959
|
-
throw new FetchproxyBridgeDownError({
|
|
43960
|
-
originalError: err.message,
|
|
43961
|
-
retryAttempted: false,
|
|
43962
|
-
op: "capture_redirect",
|
|
43963
|
-
url: `https://${opts.host}${opts.path ?? "/*"}`,
|
|
43964
|
-
role: this.role,
|
|
43965
|
-
port: this.opts.port
|
|
43966
|
-
});
|
|
43967
|
-
}
|
|
44458
|
+
return this.withLazyRevive("capture_redirect", `https://${opts.host}${opts.path ?? "/*"}`, () => this._captureRedirectOnce(opts));
|
|
43968
44459
|
}
|
|
43969
44460
|
async _captureRedirectOnce(opts) {
|
|
43970
44461
|
const id = this.nextRequestId++;
|
|
@@ -44009,52 +44500,57 @@ var init_ws_server = __esm({
|
|
|
44009
44500
|
assertUrlInDomains("download url", opts.url, this.opts.domains);
|
|
44010
44501
|
await this.ensureConnected();
|
|
44011
44502
|
this.throwIfPendingPair();
|
|
44503
|
+
return this.withLazyRevive("download", opts.url, () => this._downloadOnce(opts));
|
|
44504
|
+
}
|
|
44505
|
+
/**
|
|
44506
|
+
* B-QUAL-1: the lazy-revive policy for the throwing verbs
|
|
44507
|
+
* (`capture_request_header`, `capture_redirect`, `download`), in one place.
|
|
44508
|
+
* Runs `once`; on the SW-eviction symptom (`content_script_unreachable` —
|
|
44509
|
+
* the request provably never reached a tab, so re-sending is safe) stamps
|
|
44510
|
+
* the eviction counter, waits `bridgeReviveDelayMs` and retries once, then
|
|
44511
|
+
* surfaces a `FetchproxyBridgeDownError`. Every other error is recorded and
|
|
44512
|
+
* rethrown untouched. `fetch()` keeps its own envelope-shaped variant.
|
|
44513
|
+
*/
|
|
44514
|
+
async withLazyRevive(op, url2, once) {
|
|
44515
|
+
const isSwDown = (e) => e instanceof FetchproxyProtocolError && classifyFetchError(e.message) === "content_script_unreachable";
|
|
44516
|
+
const bridgeDown = (e, retryAttempted) => {
|
|
44517
|
+
this.recordFailure(`${op} bridge-down: ${e.message}`);
|
|
44518
|
+
return new FetchproxyBridgeDownError({
|
|
44519
|
+
originalError: e.message,
|
|
44520
|
+
retryAttempted,
|
|
44521
|
+
op,
|
|
44522
|
+
url: url2,
|
|
44523
|
+
role: this.role,
|
|
44524
|
+
port: this.opts.port
|
|
44525
|
+
});
|
|
44526
|
+
};
|
|
44012
44527
|
try {
|
|
44013
|
-
const result = await
|
|
44528
|
+
const result = await once();
|
|
44014
44529
|
this.recordSuccess();
|
|
44015
44530
|
return result;
|
|
44016
44531
|
} catch (err) {
|
|
44017
|
-
|
|
44018
|
-
|
|
44019
|
-
this.recordFailure(`download: ${err.message ?? String(err)}`);
|
|
44532
|
+
if (!isSwDown(err)) {
|
|
44533
|
+
this.recordFailure(`${op}: ${err.message ?? String(err)}`);
|
|
44020
44534
|
throw err;
|
|
44021
44535
|
}
|
|
44022
44536
|
this.lastEvictionDetectedAt = Date.now();
|
|
44023
44537
|
const reviveMs = this.opts.bridgeReviveDelayMs ?? 0;
|
|
44024
|
-
if (reviveMs
|
|
44025
|
-
|
|
44026
|
-
|
|
44027
|
-
|
|
44028
|
-
|
|
44029
|
-
|
|
44030
|
-
|
|
44031
|
-
|
|
44032
|
-
|
|
44033
|
-
|
|
44034
|
-
|
|
44035
|
-
|
|
44036
|
-
|
|
44037
|
-
}
|
|
44038
|
-
this.recordFailure(`download bridge-down: ${retryErr.message}`);
|
|
44039
|
-
throw new FetchproxyBridgeDownError({
|
|
44040
|
-
originalError: retryErr.message,
|
|
44041
|
-
retryAttempted: true,
|
|
44042
|
-
op: "download",
|
|
44043
|
-
url: opts.url,
|
|
44044
|
-
role: this.role,
|
|
44045
|
-
port: this.opts.port
|
|
44046
|
-
});
|
|
44538
|
+
if (reviveMs <= 0)
|
|
44539
|
+
throw bridgeDown(err, false);
|
|
44540
|
+
this.lazyReviveAttempts += 1;
|
|
44541
|
+
await new Promise((r) => setTimeout(r, reviveMs));
|
|
44542
|
+
try {
|
|
44543
|
+
const result = await once();
|
|
44544
|
+
this.lazyReviveSuccesses += 1;
|
|
44545
|
+
this.recordSuccess();
|
|
44546
|
+
return result;
|
|
44547
|
+
} catch (retryErr) {
|
|
44548
|
+
if (!isSwDown(retryErr)) {
|
|
44549
|
+
this.recordFailure(`${op}: ${retryErr.message ?? String(retryErr)}`);
|
|
44550
|
+
throw retryErr;
|
|
44047
44551
|
}
|
|
44552
|
+
throw bridgeDown(retryErr, true);
|
|
44048
44553
|
}
|
|
44049
|
-
this.recordFailure(`download bridge-down: ${err.message}`);
|
|
44050
|
-
throw new FetchproxyBridgeDownError({
|
|
44051
|
-
originalError: err.message,
|
|
44052
|
-
retryAttempted: false,
|
|
44053
|
-
op: "download",
|
|
44054
|
-
url: opts.url,
|
|
44055
|
-
role: this.role,
|
|
44056
|
-
port: this.opts.port
|
|
44057
|
-
});
|
|
44058
44554
|
}
|
|
44059
44555
|
}
|
|
44060
44556
|
async _downloadOnce(opts) {
|
|
@@ -44083,7 +44579,9 @@ var init_ws_server = __esm({
|
|
|
44083
44579
|
// CAP and need the identical explanation; missing it then was the point
|
|
44084
44580
|
// of #279 — "both verbs" was counted off the two call sites in view
|
|
44085
44581
|
// rather than off the type. Three is still the number.
|
|
44086
|
-
opts.timeoutMs
|
|
44582
|
+
opts.timeoutMs,
|
|
44583
|
+
// A second download saves a duplicate "file (1)".
|
|
44584
|
+
false
|
|
44087
44585
|
);
|
|
44088
44586
|
}
|
|
44089
44587
|
/**
|
|
@@ -44174,6 +44672,52 @@ var init_ws_server = __esm({
|
|
|
44174
44672
|
await this.sendInnerFrame(inner);
|
|
44175
44673
|
return this._withVerbTimeout(pending, this.pendingStorage, id, origin);
|
|
44176
44674
|
}
|
|
44675
|
+
/**
|
|
44676
|
+
* 3.1.0+: read a declared REPEATED DOM structure from the user's
|
|
44677
|
+
* signed-in tab. Requires `'read_dom_list'` in capabilities AND `name` to
|
|
44678
|
+
* match a declared `domListSelectors` entry. The extension resolves the
|
|
44679
|
+
* entry's `itemSelector` via `document.querySelectorAll` in the matched
|
|
44680
|
+
* tab's DOM (isolated world), then resolves each declared field against
|
|
44681
|
+
* every matched item in turn — no page-JS execution, same as `readDom`.
|
|
44682
|
+
*
|
|
44683
|
+
* Returns one row per matched item, in document order, truncated to the
|
|
44684
|
+
* entry's declared `maxItems` when set. A field absent on a given item is
|
|
44685
|
+
* omitted from that item's row (the row itself is still returned). Throws
|
|
44686
|
+
* `FetchproxyProtocolError` on bridge failures and a plain `Error` on
|
|
44687
|
+
* developer mistakes (undeclared capability, undeclared name).
|
|
44688
|
+
*
|
|
44689
|
+
* Reads only what is CURRENTLY rendered — a virtualized or lazily-loaded
|
|
44690
|
+
* list returns only its visible rows. A caller that needs more scrolls
|
|
44691
|
+
* the list (out of band) and calls again.
|
|
44692
|
+
*/
|
|
44693
|
+
async readDomList(opts) {
|
|
44694
|
+
if (!this.opts.capabilities.includes("read_dom_list")) {
|
|
44695
|
+
throw new Error('FetchproxyServer.readDomList(): MCP did not declare "read_dom_list" in capabilities');
|
|
44696
|
+
}
|
|
44697
|
+
await this.ensureConnected();
|
|
44698
|
+
this.throwIfPendingPair();
|
|
44699
|
+
if (typeof opts.name !== "string" || opts.name.length === 0) {
|
|
44700
|
+
throw new Error("FetchproxyServer.readDomList: opts.name must be a non-empty string");
|
|
44701
|
+
}
|
|
44702
|
+
this.assertScopeSubset([opts.name], this.opts.domListSelectors.map((d) => d.name), "domListSelectors");
|
|
44703
|
+
if (opts.subdomain !== void 0)
|
|
44704
|
+
assertSubdomainLabel(opts.subdomain);
|
|
44705
|
+
const baseDomain = this.resolveBaseDomain(opts.domain);
|
|
44706
|
+
const host = opts.subdomain ? `${opts.subdomain}.${baseDomain}` : baseDomain;
|
|
44707
|
+
const origin = `https://${host}`;
|
|
44708
|
+
const id = this.nextRequestId++;
|
|
44709
|
+
const inner = {
|
|
44710
|
+
type: "request",
|
|
44711
|
+
id,
|
|
44712
|
+
op: "read_dom_list",
|
|
44713
|
+
init: { origin, name: opts.name }
|
|
44714
|
+
};
|
|
44715
|
+
const pending = this.guardPending(new Promise((resolve2, reject) => {
|
|
44716
|
+
this.pendingDomList.set(id, { resolve: resolve2, reject });
|
|
44717
|
+
}));
|
|
44718
|
+
await this.sendInnerFrame(inner);
|
|
44719
|
+
return this._withVerbTimeout(pending, this.pendingDomList, id, origin);
|
|
44720
|
+
}
|
|
44177
44721
|
/**
|
|
44178
44722
|
* 1.x+: run a declared GraphQL operation through the page's own Apollo
|
|
44179
44723
|
* client (`window.__APOLLO_CLIENT__`) in the signed-in tab's MAIN world.
|
|
@@ -44217,8 +44761,9 @@ var init_ws_server = __esm({
|
|
|
44217
44761
|
const pending = this.guardPending(new Promise((resolve2, reject) => {
|
|
44218
44762
|
this.pendingGraphql.set(id, { resolve: resolve2, reject });
|
|
44219
44763
|
}));
|
|
44220
|
-
|
|
44221
|
-
|
|
44764
|
+
const retrySafe = opts.retryOnTimeout === true;
|
|
44765
|
+
await this.sendInnerFrame(inner, { resendable: retrySafe });
|
|
44766
|
+
return this._withVerbTimeout(pending, this.pendingGraphql, id, opts.name, void 0, retrySafe);
|
|
44222
44767
|
}
|
|
44223
44768
|
assertScopeSubset(requested, declared, label) {
|
|
44224
44769
|
const undeclared = undeclaredKeys(requested, declared);
|
|
@@ -44255,6 +44800,7 @@ var init_ws_server = __esm({
|
|
|
44255
44800
|
onInner(inner) {
|
|
44256
44801
|
if (inner.type !== "response")
|
|
44257
44802
|
return;
|
|
44803
|
+
this.resendable.delete(inner.id);
|
|
44258
44804
|
this.lastExtensionMessageAt = Date.now();
|
|
44259
44805
|
const fetchCb = this.pending.get(inner.id);
|
|
44260
44806
|
if (fetchCb) {
|
|
@@ -44343,6 +44889,20 @@ var init_ws_server = __esm({
|
|
|
44343
44889
|
}
|
|
44344
44890
|
return;
|
|
44345
44891
|
}
|
|
44892
|
+
const domListCb = this.pendingDomList.get(inner.id);
|
|
44893
|
+
if (domListCb) {
|
|
44894
|
+
this.pendingDomList.delete(inner.id);
|
|
44895
|
+
if (inner.ok) {
|
|
44896
|
+
if (inner.op === "read_dom_list" && inner.rows) {
|
|
44897
|
+
domListCb.resolve(inner.rows.map((row) => ({ ...row })));
|
|
44898
|
+
} else {
|
|
44899
|
+
domListCb.reject(new FetchproxyProtocolError(`unexpected ${String(inner.op)} response on read_dom_list awaiter`));
|
|
44900
|
+
}
|
|
44901
|
+
} else {
|
|
44902
|
+
domListCb.reject(protocolErrorFrom(inner.error));
|
|
44903
|
+
}
|
|
44904
|
+
return;
|
|
44905
|
+
}
|
|
44346
44906
|
const downloadCb = this.pendingDownload.get(inner.id);
|
|
44347
44907
|
if (downloadCb) {
|
|
44348
44908
|
this.pendingDownload.delete(inner.id);
|
|
@@ -44402,42 +44962,133 @@ var init_ws_server = __esm({
|
|
|
44402
44962
|
}
|
|
44403
44963
|
}
|
|
44404
44964
|
}
|
|
44405
|
-
rejectAllPending(reason = "extension disconnected") {
|
|
44965
|
+
rejectAllPending(reason = "extension disconnected", keep = /* @__PURE__ */ new Set()) {
|
|
44966
|
+
for (const id of this.allPendingIds()) {
|
|
44967
|
+
if (!keep.has(id))
|
|
44968
|
+
this.rejectPendingId(id, reason);
|
|
44969
|
+
}
|
|
44970
|
+
if (keep.size === 0)
|
|
44971
|
+
this.resendable.clear();
|
|
44972
|
+
}
|
|
44973
|
+
/** Every request id with a live awaiter, across all op maps. */
|
|
44974
|
+
allPendingIds() {
|
|
44975
|
+
const ids = [];
|
|
44976
|
+
for (const map2 of this.pendingMaps())
|
|
44977
|
+
for (const id of map2.keys())
|
|
44978
|
+
ids.push(id);
|
|
44979
|
+
return ids;
|
|
44980
|
+
}
|
|
44981
|
+
pendingMaps() {
|
|
44982
|
+
return [
|
|
44983
|
+
this.pending,
|
|
44984
|
+
this.pendingReadCookies,
|
|
44985
|
+
this.pendingStorage,
|
|
44986
|
+
this.pendingWriteCookies,
|
|
44987
|
+
this.pendingCapture,
|
|
44988
|
+
this.pendingRedirect,
|
|
44989
|
+
this.pendingIdb,
|
|
44990
|
+
this.pendingDomList,
|
|
44991
|
+
this.pendingDownload,
|
|
44992
|
+
this.pendingGraphql
|
|
44993
|
+
];
|
|
44994
|
+
}
|
|
44995
|
+
isPendingId(id) {
|
|
44996
|
+
return this.pendingMaps().some((m) => m.has(id));
|
|
44997
|
+
}
|
|
44998
|
+
/** Fail the one awaiter registered under `id`, in its own map's shape. */
|
|
44999
|
+
rejectPendingId(id, reason) {
|
|
44406
45000
|
const err = new FetchproxyProtocolError(reason);
|
|
44407
|
-
|
|
44408
|
-
|
|
45001
|
+
this.resendable.delete(id);
|
|
45002
|
+
const fetchCb = this.pending.get(id);
|
|
45003
|
+
if (fetchCb) {
|
|
45004
|
+
this.pending.delete(id);
|
|
45005
|
+
fetchCb({
|
|
44409
45006
|
ok: false,
|
|
44410
45007
|
error: err.message,
|
|
44411
45008
|
kind: classifyFetchError(err.message),
|
|
44412
45009
|
retryAttempted: false
|
|
44413
45010
|
});
|
|
45011
|
+
return;
|
|
45012
|
+
}
|
|
45013
|
+
const cookieCb = this.pendingReadCookies.get(id);
|
|
45014
|
+
if (cookieCb) {
|
|
45015
|
+
this.pendingReadCookies.delete(id);
|
|
45016
|
+
cookieCb({ ok: false, error: err.message });
|
|
45017
|
+
return;
|
|
45018
|
+
}
|
|
45019
|
+
for (const map2 of [
|
|
45020
|
+
this.pendingStorage,
|
|
45021
|
+
this.pendingWriteCookies,
|
|
45022
|
+
this.pendingCapture,
|
|
45023
|
+
this.pendingRedirect,
|
|
45024
|
+
this.pendingIdb,
|
|
45025
|
+
this.pendingDomList,
|
|
45026
|
+
this.pendingDownload,
|
|
45027
|
+
this.pendingGraphql
|
|
45028
|
+
]) {
|
|
45029
|
+
const entry = map2.get(id);
|
|
45030
|
+
if (entry) {
|
|
45031
|
+
map2.delete(id);
|
|
45032
|
+
entry.reject(err);
|
|
45033
|
+
return;
|
|
45034
|
+
}
|
|
44414
45035
|
}
|
|
44415
|
-
|
|
44416
|
-
|
|
44417
|
-
|
|
44418
|
-
|
|
44419
|
-
|
|
44420
|
-
|
|
44421
|
-
|
|
44422
|
-
|
|
44423
|
-
|
|
44424
|
-
|
|
44425
|
-
|
|
44426
|
-
|
|
44427
|
-
|
|
44428
|
-
|
|
44429
|
-
|
|
44430
|
-
|
|
44431
|
-
|
|
44432
|
-
|
|
44433
|
-
|
|
44434
|
-
|
|
44435
|
-
|
|
44436
|
-
|
|
44437
|
-
|
|
44438
|
-
|
|
44439
|
-
|
|
44440
|
-
this.
|
|
45036
|
+
}
|
|
45037
|
+
noteResendable(inner) {
|
|
45038
|
+
this.resendable.set(inner.id, inner);
|
|
45039
|
+
if (this.resendable.size > 256) {
|
|
45040
|
+
for (const id of [...this.resendable.keys()]) {
|
|
45041
|
+
if (!this.isPendingId(id))
|
|
45042
|
+
this.resendable.delete(id);
|
|
45043
|
+
}
|
|
45044
|
+
}
|
|
45045
|
+
}
|
|
45046
|
+
/**
|
|
45047
|
+
* B-BUG-6: the host this peer relayed through has gone — most often a
|
|
45048
|
+
* short-lived process (a bootstrap lift, an `fpx` call) that won the
|
|
45049
|
+
* election and then closed. Fail only the in-flight requests that may
|
|
45050
|
+
* already have run (a non-idempotent fetch, a cookie write, a download);
|
|
45051
|
+
* re-elect and send the rest again. The replies to the originals died with
|
|
45052
|
+
* the old host, and each re-sent request keeps its awaiter and its
|
|
45053
|
+
* original deadline.
|
|
45054
|
+
*/
|
|
45055
|
+
async recoverFromHostLoss() {
|
|
45056
|
+
const toResend = /* @__PURE__ */ new Map();
|
|
45057
|
+
for (const [id, inner] of this.resendable) {
|
|
45058
|
+
if (this.isPendingId(id))
|
|
45059
|
+
toResend.set(id, inner);
|
|
45060
|
+
}
|
|
45061
|
+
this.resendable.clear();
|
|
45062
|
+
this.rejectAllPending("fetchproxy: the MCP process holding the bridge port exited while this request was in flight. It was not re-sent because it may already have run in the browser \u2014 check before retrying.", new Set(toResend.keys()));
|
|
45063
|
+
if (toResend.size === 0)
|
|
45064
|
+
return;
|
|
45065
|
+
const lost = (id, e) => this.rejectPendingId(id, `fetchproxy: the bridge host exited and reconnecting failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
45066
|
+
try {
|
|
45067
|
+
await this.ensureConnected();
|
|
45068
|
+
} catch (e) {
|
|
45069
|
+
for (const id of toResend.keys())
|
|
45070
|
+
lost(id, e);
|
|
45071
|
+
return;
|
|
45072
|
+
}
|
|
45073
|
+
for (const [id, inner] of toResend) {
|
|
45074
|
+
if (!this.isPendingId(id))
|
|
45075
|
+
continue;
|
|
45076
|
+
try {
|
|
45077
|
+
await this.resendInnerFrame(inner);
|
|
45078
|
+
} catch (e) {
|
|
45079
|
+
lost(id, e);
|
|
45080
|
+
}
|
|
45081
|
+
}
|
|
45082
|
+
}
|
|
45083
|
+
/** Send an already-registered request again, keeping its awaiter on failure. */
|
|
45084
|
+
async resendInnerFrame(inner) {
|
|
45085
|
+
if (this.hostHandle)
|
|
45086
|
+
await this.hostHandle.sendOwnInner(inner);
|
|
45087
|
+
else if (this.peerHandle)
|
|
45088
|
+
await this.peerHandle.sendInner(inner);
|
|
45089
|
+
else
|
|
45090
|
+
throw new Error("no bridge handle after re-election");
|
|
45091
|
+
this.noteResendable(inner);
|
|
44441
45092
|
}
|
|
44442
45093
|
/**
|
|
44443
45094
|
* 0.5.2+: read the current pair-pending pair code from whichever handle
|
|
@@ -44802,7 +45453,8 @@ function createFetchproxyTransport(opts) {
|
|
|
44802
45453
|
...init.headers !== void 0 ? { headers: init.headers } : {},
|
|
44803
45454
|
...init.body !== void 0 ? { body: init.body } : {},
|
|
44804
45455
|
...init.subdomain !== void 0 ? { subdomain: init.subdomain } : {},
|
|
44805
|
-
...init.domain !== void 0 ? { domain: init.domain } : {}
|
|
45456
|
+
...init.domain !== void 0 ? { domain: init.domain } : {},
|
|
45457
|
+
...init.retryOnTimeout !== void 0 ? { retryOnTimeout: init.retryOnTimeout } : {}
|
|
44806
45458
|
}));
|
|
44807
45459
|
return { status: response.status, body: response.body, url: response.url };
|
|
44808
45460
|
},
|
|
@@ -44811,7 +45463,8 @@ function createFetchproxyTransport(opts) {
|
|
|
44811
45463
|
...init.headers !== void 0 ? { headers: init.headers } : {},
|
|
44812
45464
|
...init.body !== void 0 ? { body: init.body } : {},
|
|
44813
45465
|
...init.subdomain !== void 0 ? { subdomain: init.subdomain } : {},
|
|
44814
|
-
...init.domain !== void 0 ? { domain: init.domain } : {}
|
|
45466
|
+
...init.domain !== void 0 ? { domain: init.domain } : {},
|
|
45467
|
+
...init.retryOnTimeout !== void 0 ? { retryOnTimeout: init.retryOnTimeout } : {}
|
|
44815
45468
|
}));
|
|
44816
45469
|
return {
|
|
44817
45470
|
data,
|
|
@@ -45136,7 +45789,39 @@ function defaultIsRefreshRevoked(err) {
|
|
|
45136
45789
|
return false;
|
|
45137
45790
|
if (err instanceof ApiError && err.status >= 500)
|
|
45138
45791
|
return false;
|
|
45139
|
-
return
|
|
45792
|
+
return !isTransientFailure(err);
|
|
45793
|
+
}
|
|
45794
|
+
function isPersistenceFailure(err) {
|
|
45795
|
+
return err !== null && typeof err === "object" && err.persistenceFailure === true;
|
|
45796
|
+
}
|
|
45797
|
+
var TRANSIENT_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
45798
|
+
"ECONNRESET",
|
|
45799
|
+
"ECONNREFUSED",
|
|
45800
|
+
"ECONNABORTED",
|
|
45801
|
+
"ETIMEDOUT",
|
|
45802
|
+
"ENOTFOUND",
|
|
45803
|
+
"EAI_AGAIN",
|
|
45804
|
+
"ENETUNREACH",
|
|
45805
|
+
"ENETDOWN",
|
|
45806
|
+
"EHOSTUNREACH",
|
|
45807
|
+
"EPIPE"
|
|
45808
|
+
]);
|
|
45809
|
+
function isTransientFailure(err) {
|
|
45810
|
+
let cur = err;
|
|
45811
|
+
for (let depth = 0; depth < 4 && cur !== null && typeof cur === "object"; depth++) {
|
|
45812
|
+
const e = cur;
|
|
45813
|
+
if (typeof e.status === "number" && (e.status >= 500 || e.status === 429 || e.status === 408))
|
|
45814
|
+
return true;
|
|
45815
|
+
if (e.name === "AbortError" || e.name === "TimeoutError")
|
|
45816
|
+
return true;
|
|
45817
|
+
if (typeof e.code === "string" && (TRANSIENT_NETWORK_CODES.has(e.code) || e.code.startsWith("UND_ERR_"))) {
|
|
45818
|
+
return true;
|
|
45819
|
+
}
|
|
45820
|
+
if (e.name === "TypeError" && e.message === "fetch failed")
|
|
45821
|
+
return true;
|
|
45822
|
+
cur = e.cause;
|
|
45823
|
+
}
|
|
45824
|
+
return false;
|
|
45140
45825
|
}
|
|
45141
45826
|
function isBearerTokens(raw) {
|
|
45142
45827
|
if (raw === null || typeof raw !== "object")
|
|
@@ -45303,6 +45988,8 @@ var TokenManager = class {
|
|
|
45303
45988
|
async reBootstrap(err) {
|
|
45304
45989
|
if (err instanceof StatePersistenceError)
|
|
45305
45990
|
throw err.cause;
|
|
45991
|
+
if (isPersistenceFailure(err))
|
|
45992
|
+
throw err;
|
|
45306
45993
|
if (this.bootstrapFn === void 0)
|
|
45307
45994
|
throw err;
|
|
45308
45995
|
if (!this.isRefreshRevokedFn(err))
|
|
@@ -45318,6 +46005,10 @@ var TokenManager = class {
|
|
|
45318
46005
|
try {
|
|
45319
46006
|
await this.refreshNow();
|
|
45320
46007
|
} catch (err) {
|
|
46008
|
+
const held = this.tokens;
|
|
46009
|
+
if (!(err instanceof StatePersistenceError) && held !== void 0 && this.now() < held.expiresAt && !this.isRefreshRevokedFn(err)) {
|
|
46010
|
+
return held.accessToken;
|
|
46011
|
+
}
|
|
45321
46012
|
return (await this.reBootstrap(err)).accessToken;
|
|
45322
46013
|
}
|
|
45323
46014
|
tokens = this.tokens ?? tokens;
|
|
@@ -45532,17 +46223,33 @@ var OutlookClient = class {
|
|
|
45532
46223
|
headers: prefer.length ? { Prefer: prefer.join(", ") } : void 0
|
|
45533
46224
|
});
|
|
45534
46225
|
}
|
|
45535
|
-
/**
|
|
45536
|
-
|
|
46226
|
+
/**
|
|
46227
|
+
* GET an absolute URL — used only to follow `@odata.nextLink`.
|
|
46228
|
+
*
|
|
46229
|
+
* The link is absolute and already carries the base path
|
|
46230
|
+
* (`https://outlook.office.com/api/v2.0/me/messages?$skip=25`), while the
|
|
46231
|
+
* underlying client appends every path to that same base. So the base path is
|
|
46232
|
+
* stripped here, and a link that is not under it — another origin, or another
|
|
46233
|
+
* path on the same host — is refused rather than sent the bearer token.
|
|
46234
|
+
*/
|
|
46235
|
+
async getAbsolute(url2, opts = {}) {
|
|
45537
46236
|
this.#requireConfigured();
|
|
45538
46237
|
const base = new URL(this.#baseUrl);
|
|
45539
|
-
|
|
45540
|
-
|
|
45541
|
-
|
|
45542
|
-
|
|
46238
|
+
let target;
|
|
46239
|
+
try {
|
|
46240
|
+
target = new URL(url2);
|
|
46241
|
+
} catch {
|
|
46242
|
+
throw new McpToolError(`Refusing to follow a link that is not a URL: ${url2}`, {
|
|
46243
|
+
hint: "Pass the nextLink value exactly as a previous listing returned it."
|
|
46244
|
+
});
|
|
46245
|
+
}
|
|
46246
|
+
const prefix = base.pathname.replace(/\/+$/, "");
|
|
46247
|
+
if (target.origin !== base.origin || !(target.pathname === prefix || target.pathname.startsWith(`${prefix}/`))) {
|
|
46248
|
+
throw new McpToolError(`Refusing to follow a link to ${target.origin}${target.pathname}.`, {
|
|
46249
|
+
hint: `Pagination links must stay under the Outlook API base (${this.#baseUrl}).`
|
|
45543
46250
|
});
|
|
45544
46251
|
}
|
|
45545
|
-
return this
|
|
46252
|
+
return this.get(target.pathname.slice(prefix.length) + target.search, opts);
|
|
45546
46253
|
}
|
|
45547
46254
|
/**
|
|
45548
46255
|
* The single write path. Everything mutating goes through here so auth and
|
|
@@ -45682,6 +46389,22 @@ function projectCollection(payload, project, label) {
|
|
|
45682
46389
|
});
|
|
45683
46390
|
}
|
|
45684
46391
|
|
|
46392
|
+
// src/tools/_paging.ts
|
|
46393
|
+
init_zod();
|
|
46394
|
+
var nextLinkParam = external_exports.string().min(1).optional().describe(
|
|
46395
|
+
"Continue a previous listing: pass its `nextLink` value unchanged to fetch the next page. When set, the paging/filter arguments are taken from the link and ignored here."
|
|
46396
|
+
);
|
|
46397
|
+
function fetchPage(client2, nextLink, path, opts = {}) {
|
|
46398
|
+
return nextLink !== void 0 ? client2.getAbsolute(nextLink, opts) : client2.get(path, opts);
|
|
46399
|
+
}
|
|
46400
|
+
function plainCollection(data) {
|
|
46401
|
+
const items = Array.isArray(data?.value) ? data.value : [];
|
|
46402
|
+
const out = { count: items.length, items: items.map(stripOData) };
|
|
46403
|
+
const next = data?.["@odata.nextLink"];
|
|
46404
|
+
if (next) out.nextLink = next;
|
|
46405
|
+
return out;
|
|
46406
|
+
}
|
|
46407
|
+
|
|
45685
46408
|
// src/tools/mail.ts
|
|
45686
46409
|
var VIEWS2 = ["compact", "full", "raw"];
|
|
45687
46410
|
var WELL_KNOWN = [
|
|
@@ -45714,11 +46437,14 @@ function registerMailTools(server, client2) {
|
|
|
45714
46437
|
annotations: { readOnlyHint: true },
|
|
45715
46438
|
inputSchema: external_exports.object({
|
|
45716
46439
|
view: viewParam(VIEWS2),
|
|
45717
|
-
limit: external_exports.number().int().min(1).max(200).optional().describe("Max folders (default 50)")
|
|
46440
|
+
limit: external_exports.number().int().min(1).max(200).optional().describe("Max folders (default 50)"),
|
|
46441
|
+
nextLink: nextLinkParam
|
|
45718
46442
|
})
|
|
45719
46443
|
},
|
|
45720
|
-
async ({ view, limit }) => {
|
|
45721
|
-
const data = await
|
|
46444
|
+
async ({ view, limit, nextLink }) => {
|
|
46445
|
+
const data = await fetchPage(
|
|
46446
|
+
client2,
|
|
46447
|
+
nextLink,
|
|
45722
46448
|
`/me/mailfolders${qs({ $top: limit ?? 50 })}`
|
|
45723
46449
|
);
|
|
45724
46450
|
if (resolveView(view, VIEWS2) === "raw") return minifiedResult(data);
|
|
@@ -45728,7 +46454,7 @@ function registerMailTools(server, client2) {
|
|
|
45728
46454
|
server.registerTool(
|
|
45729
46455
|
"outlook_list_messages",
|
|
45730
46456
|
{
|
|
45731
|
-
description: "List messages in a folder, newest first. Defaults to the inbox. Use `unreadOnly` for a triage view. Bodies are NOT included \u2014 call outlook_get_message for one. Note `search` and `unreadOnly` cannot be combined (the API rejects $search with $filter); pass `search` alone to search the whole mailbox.",
|
|
46457
|
+
description: "List messages in a folder, newest first. Defaults to the inbox. Use `unreadOnly` for a triage view. Bodies are NOT included \u2014 call outlook_get_message for one. Note `search` and `unreadOnly` cannot be combined on a first-page request (the API rejects $search with $filter); pass `search` alone to search the whole mailbox. When following a `nextLink`, both are ignored, so echoing them back is harmless.",
|
|
45732
46458
|
annotations: { readOnlyHint: true },
|
|
45733
46459
|
inputSchema: external_exports.object({
|
|
45734
46460
|
view: viewParam(VIEWS2),
|
|
@@ -45736,11 +46462,12 @@ function registerMailTools(server, client2) {
|
|
|
45736
46462
|
limit: external_exports.number().int().min(1).max(100).optional().describe("Max messages (default 25)"),
|
|
45737
46463
|
skip: external_exports.number().int().min(0).optional().describe("Offset for paging"),
|
|
45738
46464
|
unreadOnly: external_exports.boolean().optional().describe("Only unread messages"),
|
|
45739
|
-
search: external_exports.string().optional().describe("Full-text search across the whole mailbox; cannot combine with unreadOnly")
|
|
46465
|
+
search: external_exports.string().optional().describe("Full-text search across the whole mailbox; cannot combine with unreadOnly on a first page (ignored with nextLink)"),
|
|
46466
|
+
nextLink: nextLinkParam
|
|
45740
46467
|
})
|
|
45741
46468
|
},
|
|
45742
|
-
async ({ view, folder, limit, skip, unreadOnly, search }) => {
|
|
45743
|
-
if (search !== void 0 && unreadOnly === true) {
|
|
46469
|
+
async ({ view, folder, limit, skip, unreadOnly, search, nextLink }) => {
|
|
46470
|
+
if (nextLink === void 0 && search !== void 0 && unreadOnly === true) {
|
|
45744
46471
|
throw new McpToolError("`search` and `unreadOnly` cannot be combined.", {
|
|
45745
46472
|
hint: "Outlook rejects $search together with $filter. Search first, then filter the results."
|
|
45746
46473
|
});
|
|
@@ -45757,7 +46484,11 @@ function registerMailTools(server, client2) {
|
|
|
45757
46484
|
params.$orderby = "ReceivedDateTime desc";
|
|
45758
46485
|
if (unreadOnly === true) params.$filter = "IsRead eq false";
|
|
45759
46486
|
}
|
|
45760
|
-
const data = await
|
|
46487
|
+
const data = await fetchPage(
|
|
46488
|
+
client2,
|
|
46489
|
+
nextLink,
|
|
46490
|
+
`${base}${qs(params)}`
|
|
46491
|
+
);
|
|
45761
46492
|
const v = resolveView(view, VIEWS2);
|
|
45762
46493
|
if (v === "raw") return minifiedResult(data);
|
|
45763
46494
|
return minifiedResult(
|
|
@@ -45791,19 +46522,19 @@ function registerMailTools(server, client2) {
|
|
|
45791
46522
|
description: "List a message's attachments (name, size, content type). Metadata only \u2014 this deliberately does not download bytes.",
|
|
45792
46523
|
annotations: { readOnlyHint: true },
|
|
45793
46524
|
inputSchema: external_exports.object({
|
|
45794
|
-
id: external_exports.string().min(1).describe("Message Id")
|
|
46525
|
+
id: external_exports.string().min(1).describe("Message Id"),
|
|
46526
|
+
nextLink: nextLinkParam
|
|
45795
46527
|
})
|
|
45796
46528
|
},
|
|
45797
|
-
async ({ id }) => {
|
|
45798
|
-
const data = await
|
|
46529
|
+
async ({ id, nextLink }) => {
|
|
46530
|
+
const data = await fetchPage(
|
|
46531
|
+
client2,
|
|
46532
|
+
nextLink,
|
|
45799
46533
|
`/me/messages/${encodeURIComponent(id)}/attachments${qs({
|
|
45800
46534
|
$select: "Id,Name,Size,ContentType"
|
|
45801
46535
|
})}`
|
|
45802
46536
|
);
|
|
45803
|
-
return minifiedResult(
|
|
45804
|
-
count: data.value?.length ?? 0,
|
|
45805
|
-
items: (data.value ?? []).map(stripOData)
|
|
45806
|
-
});
|
|
46537
|
+
return minifiedResult(plainCollection(data));
|
|
45807
46538
|
}
|
|
45808
46539
|
);
|
|
45809
46540
|
}
|
|
@@ -45850,12 +46581,15 @@ function registerCalendarTools(server, client2) {
|
|
|
45850
46581
|
timeZone: external_exports.string().optional().describe(
|
|
45851
46582
|
'Windows time-zone name for the returned times, e.g. "Eastern Standard Time". NOT an IANA name like America/New_York.'
|
|
45852
46583
|
),
|
|
45853
|
-
limit: external_exports.number().int().min(1).max(200).optional().describe("Max events (default 50)")
|
|
46584
|
+
limit: external_exports.number().int().min(1).max(200).optional().describe("Max events (default 50)"),
|
|
46585
|
+
nextLink: nextLinkParam
|
|
45854
46586
|
})
|
|
45855
46587
|
},
|
|
45856
|
-
async ({ view, start, end, timeZone, limit }) => {
|
|
46588
|
+
async ({ view, start, end, timeZone, limit, nextLink }) => {
|
|
45857
46589
|
const zone = timeZone ?? await mailboxTimeZone(client2);
|
|
45858
|
-
const data = await
|
|
46590
|
+
const data = await fetchPage(
|
|
46591
|
+
client2,
|
|
46592
|
+
nextLink,
|
|
45859
46593
|
`/me/calendarview${qs2({
|
|
45860
46594
|
startDateTime: start,
|
|
45861
46595
|
endDateTime: end,
|
|
@@ -45900,18 +46634,14 @@ function registerCalendarTools(server, client2) {
|
|
|
45900
46634
|
description: "List the calendars in the mailbox, for picking one to read or write against.",
|
|
45901
46635
|
annotations: { readOnlyHint: true },
|
|
45902
46636
|
inputSchema: external_exports.object({
|
|
45903
|
-
view: viewParam(VIEWS2)
|
|
46637
|
+
view: viewParam(VIEWS2),
|
|
46638
|
+
nextLink: nextLinkParam
|
|
45904
46639
|
})
|
|
45905
46640
|
},
|
|
45906
|
-
async ({ view }) => {
|
|
45907
|
-
const data = await client2
|
|
45908
|
-
`/me/calendars${qs2({ $select: "Id,Name,Color,CanEdit,Owner" })}`
|
|
45909
|
-
);
|
|
46641
|
+
async ({ view, nextLink }) => {
|
|
46642
|
+
const data = await fetchPage(client2, nextLink, `/me/calendars${qs2({ $select: "Id,Name,Color,CanEdit,Owner" })}`);
|
|
45910
46643
|
if (resolveView(view, VIEWS2) === "raw") return minifiedResult(data);
|
|
45911
|
-
return minifiedResult(
|
|
45912
|
-
count: data.value?.length ?? 0,
|
|
45913
|
-
items: (data.value ?? []).map(stripOData)
|
|
45914
|
-
});
|
|
46644
|
+
return minifiedResult(plainCollection(data));
|
|
45915
46645
|
}
|
|
45916
46646
|
);
|
|
45917
46647
|
}
|
|
@@ -45929,7 +46659,7 @@ function qs3(params) {
|
|
|
45929
46659
|
}
|
|
45930
46660
|
function collection(data, raw) {
|
|
45931
46661
|
if (raw) return data;
|
|
45932
|
-
return
|
|
46662
|
+
return plainCollection(data);
|
|
45933
46663
|
}
|
|
45934
46664
|
function registerDirectoryTools(server, client2) {
|
|
45935
46665
|
server.registerTool(
|
|
@@ -45958,11 +46688,14 @@ function registerDirectoryTools(server, client2) {
|
|
|
45958
46688
|
inputSchema: external_exports.object({
|
|
45959
46689
|
view: viewParam(VIEWS2),
|
|
45960
46690
|
limit: external_exports.number().int().min(1).max(200).optional().describe("Max contacts (default 50)"),
|
|
45961
|
-
skip: external_exports.number().int().min(0).optional().describe("Offset for paging")
|
|
46691
|
+
skip: external_exports.number().int().min(0).optional().describe("Offset for paging"),
|
|
46692
|
+
nextLink: nextLinkParam
|
|
45962
46693
|
})
|
|
45963
46694
|
},
|
|
45964
|
-
async ({ view, limit, skip }) => {
|
|
45965
|
-
const data = await
|
|
46695
|
+
async ({ view, limit, skip, nextLink }) => {
|
|
46696
|
+
const data = await fetchPage(
|
|
46697
|
+
client2,
|
|
46698
|
+
nextLink,
|
|
45966
46699
|
`/me/contacts${qs3({
|
|
45967
46700
|
$top: limit ?? 50,
|
|
45968
46701
|
$skip: skip,
|
|
@@ -45981,11 +46714,14 @@ function registerDirectoryTools(server, client2) {
|
|
|
45981
46714
|
annotations: { readOnlyHint: true },
|
|
45982
46715
|
inputSchema: external_exports.object({
|
|
45983
46716
|
view: viewParam(VIEWS2),
|
|
45984
|
-
limit: external_exports.number().int().min(1).max(100).optional().describe("Max people (default 25)")
|
|
46717
|
+
limit: external_exports.number().int().min(1).max(100).optional().describe("Max people (default 25)"),
|
|
46718
|
+
nextLink: nextLinkParam
|
|
45985
46719
|
})
|
|
45986
46720
|
},
|
|
45987
|
-
async ({ view, limit }) => {
|
|
45988
|
-
const data = await
|
|
46721
|
+
async ({ view, limit, nextLink }) => {
|
|
46722
|
+
const data = await fetchPage(
|
|
46723
|
+
client2,
|
|
46724
|
+
nextLink,
|
|
45989
46725
|
`/me/people${qs3({
|
|
45990
46726
|
$top: limit ?? 25,
|
|
45991
46727
|
$select: "Id,DisplayName,ScoredEmailAddresses,JobTitle,CompanyName"
|
|
@@ -46001,11 +46737,14 @@ function registerDirectoryTools(server, client2) {
|
|
|
46001
46737
|
annotations: { readOnlyHint: true },
|
|
46002
46738
|
inputSchema: external_exports.object({
|
|
46003
46739
|
view: viewParam(VIEWS2),
|
|
46004
|
-
limit: external_exports.number().int().min(1).max(200).optional().describe("Max tasks (default 50)")
|
|
46740
|
+
limit: external_exports.number().int().min(1).max(200).optional().describe("Max tasks (default 50)"),
|
|
46741
|
+
nextLink: nextLinkParam
|
|
46005
46742
|
})
|
|
46006
46743
|
},
|
|
46007
|
-
async ({ view, limit }) => {
|
|
46008
|
-
const data = await
|
|
46744
|
+
async ({ view, limit, nextLink }) => {
|
|
46745
|
+
const data = await fetchPage(
|
|
46746
|
+
client2,
|
|
46747
|
+
nextLink,
|
|
46009
46748
|
`/me/tasks${qs3({
|
|
46010
46749
|
$top: limit ?? 50,
|
|
46011
46750
|
$select: "Id,Subject,Status,Importance,DueDateTime,CompletedDateTime"
|