ofw-mcp 2.13.0 → 2.15.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/auth.js +34 -4
- package/dist/bundle.js +800 -148
- package/dist/index.js +3 -1
- package/dist/tools/_shared.js +16 -6
- package/dist/tools/healthcheck.js +81 -0
- package/dist/tools/messages.js +38 -6
- package/dist/tools/project.js +196 -0
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -3116,9 +3116,28 @@ var require_utils = __commonJS({
|
|
|
3116
3116
|
"use strict";
|
|
3117
3117
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3118
3118
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
3119
|
+
var isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
3119
3120
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3120
3121
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3121
|
-
var isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
3122
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
3123
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
3124
|
+
var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
3125
|
+
var BYTE_HEX = new Array(256);
|
|
3126
|
+
{
|
|
3127
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
3128
|
+
for (let i = 0; i < 256; i++) {
|
|
3129
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
function percentEncodeNonAscii(cp) {
|
|
3133
|
+
if (cp < 2048) {
|
|
3134
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
3135
|
+
}
|
|
3136
|
+
if (cp < 65536) {
|
|
3137
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3138
|
+
}
|
|
3139
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3140
|
+
}
|
|
3122
3141
|
function stringArrayToHexStripped(input) {
|
|
3123
3142
|
let acc = "";
|
|
3124
3143
|
let code = 0;
|
|
@@ -3143,91 +3162,105 @@ var require_utils = __commonJS({
|
|
|
3143
3162
|
}
|
|
3144
3163
|
return acc;
|
|
3145
3164
|
}
|
|
3165
|
+
var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
3166
|
+
var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
3167
|
+
var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
3146
3168
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
3147
|
-
function
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
if (hex3 !== "") {
|
|
3155
|
-
address.push(hex3);
|
|
3156
|
-
} else {
|
|
3157
|
-
output.error = true;
|
|
3158
|
-
return false;
|
|
3169
|
+
function isZoneIdentifier(zone) {
|
|
3170
|
+
if (zone.length === 0) return false;
|
|
3171
|
+
for (let i = 0; i < zone.length; i++) {
|
|
3172
|
+
if (isZoneCharacter(zone[i])) continue;
|
|
3173
|
+
if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
3174
|
+
i += 2;
|
|
3175
|
+
continue;
|
|
3159
3176
|
}
|
|
3160
|
-
|
|
3177
|
+
return false;
|
|
3161
3178
|
}
|
|
3162
3179
|
return true;
|
|
3163
3180
|
}
|
|
3164
|
-
function
|
|
3165
|
-
let
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
let
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
}
|
|
3177
|
-
if (cursor === ":") {
|
|
3178
|
-
if (endipv6Encountered === true) {
|
|
3179
|
-
endIpv6 = true;
|
|
3181
|
+
function compressIPv6ZeroRun(hextets) {
|
|
3182
|
+
let bestStart = -1;
|
|
3183
|
+
let bestLength = 0;
|
|
3184
|
+
let runStart = -1;
|
|
3185
|
+
let runLength = 0;
|
|
3186
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
3187
|
+
if (hextets[i] === "0") {
|
|
3188
|
+
if (runStart === -1) runStart = i;
|
|
3189
|
+
runLength++;
|
|
3190
|
+
if (runLength > bestLength) {
|
|
3191
|
+
bestLength = runLength;
|
|
3192
|
+
bestStart = runStart;
|
|
3180
3193
|
}
|
|
3181
|
-
if (!consume(buffer, address, output)) {
|
|
3182
|
-
break;
|
|
3183
|
-
}
|
|
3184
|
-
if (++tokenCount > 7) {
|
|
3185
|
-
output.error = true;
|
|
3186
|
-
break;
|
|
3187
|
-
}
|
|
3188
|
-
if (i > 0 && input[i - 1] === ":") {
|
|
3189
|
-
endipv6Encountered = true;
|
|
3190
|
-
}
|
|
3191
|
-
address.push(":");
|
|
3192
|
-
continue;
|
|
3193
|
-
} else if (cursor === "%") {
|
|
3194
|
-
if (!consume(buffer, address, output)) {
|
|
3195
|
-
break;
|
|
3196
|
-
}
|
|
3197
|
-
consume = consumeIsZone;
|
|
3198
3194
|
} else {
|
|
3199
|
-
|
|
3195
|
+
runStart = -1;
|
|
3196
|
+
runLength = 0;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if (bestLength < 2) return hextets.join(":");
|
|
3200
|
+
const head = hextets.slice(0, bestStart).join(":");
|
|
3201
|
+
const tail = hextets.slice(bestStart + bestLength).join(":");
|
|
3202
|
+
return head + "::" + tail;
|
|
3203
|
+
}
|
|
3204
|
+
function normalizeIPv6Address(input) {
|
|
3205
|
+
const compression = input.indexOf("::");
|
|
3206
|
+
if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
|
|
3207
|
+
const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
|
|
3208
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
|
|
3209
|
+
if (compression !== -1) {
|
|
3210
|
+
if (left.length === 1 && left[0] === "") left.length = 0;
|
|
3211
|
+
if (right.length === 1 && right[0] === "") right.length = 0;
|
|
3212
|
+
}
|
|
3213
|
+
const parts = left.concat(right);
|
|
3214
|
+
let hextetCount = 0;
|
|
3215
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3216
|
+
const part = parts[i];
|
|
3217
|
+
if (part === "") return void 0;
|
|
3218
|
+
if (part.indexOf(".") !== -1) {
|
|
3219
|
+
if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
|
|
3220
|
+
hextetCount += 2;
|
|
3200
3221
|
continue;
|
|
3201
3222
|
}
|
|
3223
|
+
if (!isHextet(part)) return void 0;
|
|
3224
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
3225
|
+
hextetCount++;
|
|
3202
3226
|
}
|
|
3203
|
-
if (
|
|
3204
|
-
if (
|
|
3205
|
-
|
|
3206
|
-
} else if (endIpv6) {
|
|
3207
|
-
address.push(buffer.join(""));
|
|
3208
|
-
} else {
|
|
3209
|
-
address.push(stringArrayToHexStripped(buffer));
|
|
3210
|
-
}
|
|
3227
|
+
if (compression === -1) {
|
|
3228
|
+
if (hextetCount !== 8) return void 0;
|
|
3229
|
+
return compressIPv6ZeroRun(parts);
|
|
3211
3230
|
}
|
|
3212
|
-
|
|
3213
|
-
|
|
3231
|
+
if (hextetCount >= 8) return void 0;
|
|
3232
|
+
const expanded = parts.slice(0, left.length);
|
|
3233
|
+
for (let i = hextetCount; i < 8; i++) expanded.push("0");
|
|
3234
|
+
for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
|
|
3235
|
+
return compressIPv6ZeroRun(expanded);
|
|
3214
3236
|
}
|
|
3215
3237
|
function normalizeIPv6(host) {
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
if (
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3238
|
+
const bracketed = host[0] === "[" && host[host.length - 1] === "]";
|
|
3239
|
+
const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
|
|
3240
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
|
|
3241
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
3242
|
+
if (bracketed && isIPvFuture(input)) {
|
|
3243
|
+
input = input.toLowerCase();
|
|
3244
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
|
|
3245
|
+
}
|
|
3246
|
+
if (findToken(input, ":") < 2) {
|
|
3247
|
+
return { host, isIPV6: false, error: bracketed };
|
|
3248
|
+
}
|
|
3249
|
+
let zoneIdentifier = "";
|
|
3250
|
+
const zoneSeparator = input.indexOf("%");
|
|
3251
|
+
if (zoneSeparator !== -1) {
|
|
3252
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
|
|
3253
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
3254
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
|
|
3255
|
+
input = input.slice(0, zoneSeparator);
|
|
3256
|
+
}
|
|
3257
|
+
const address = normalizeIPv6Address(input);
|
|
3258
|
+
if (address === void 0) return { host, isIPV6: false, error: true };
|
|
3259
|
+
return {
|
|
3260
|
+
host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
|
|
3261
|
+
escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
|
|
3262
|
+
isIPV6: true
|
|
3263
|
+
};
|
|
3231
3264
|
}
|
|
3232
3265
|
function findToken(str, token) {
|
|
3233
3266
|
let ind = 0;
|
|
@@ -3346,7 +3379,8 @@ var require_utils = __commonJS({
|
|
|
3346
3379
|
function normalizePathEncoding(input) {
|
|
3347
3380
|
let output = "";
|
|
3348
3381
|
for (let i = 0; i < input.length; i++) {
|
|
3349
|
-
|
|
3382
|
+
const ch = input[i];
|
|
3383
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3350
3384
|
const hex3 = input.slice(i + 1, i + 3);
|
|
3351
3385
|
if (isHexPair(hex3)) {
|
|
3352
3386
|
const normalizedHex = hex3.toUpperCase();
|
|
@@ -3360,10 +3394,152 @@ var require_utils = __commonJS({
|
|
|
3360
3394
|
continue;
|
|
3361
3395
|
}
|
|
3362
3396
|
}
|
|
3363
|
-
if (isPathCharacter(
|
|
3364
|
-
output +=
|
|
3397
|
+
if (isPathCharacter(ch)) {
|
|
3398
|
+
output += ch;
|
|
3399
|
+
} else {
|
|
3400
|
+
const code = input.charCodeAt(i);
|
|
3401
|
+
if (code < 128) {
|
|
3402
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3403
|
+
} else if (code < 55296 || code > 57343) {
|
|
3404
|
+
output += percentEncodeNonAscii(code);
|
|
3405
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3406
|
+
const low = input.charCodeAt(i + 1);
|
|
3407
|
+
if (low >= 56320 && low <= 57343) {
|
|
3408
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3409
|
+
i++;
|
|
3410
|
+
} else {
|
|
3411
|
+
output += percentEncodeNonAscii(65533);
|
|
3412
|
+
}
|
|
3413
|
+
} else {
|
|
3414
|
+
output += percentEncodeNonAscii(65533);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
return output;
|
|
3419
|
+
}
|
|
3420
|
+
function serializePathEncoding(input, pathNoScheme = false) {
|
|
3421
|
+
let output = "";
|
|
3422
|
+
let firstSegment = pathNoScheme && input[0] !== "/";
|
|
3423
|
+
for (let i = 0; i < input.length; i++) {
|
|
3424
|
+
const ch = input[i];
|
|
3425
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3426
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3427
|
+
if (isHexPair(hex3)) {
|
|
3428
|
+
output += "%" + hex3.toUpperCase();
|
|
3429
|
+
i += 2;
|
|
3430
|
+
continue;
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
if (ch === "/") {
|
|
3434
|
+
firstSegment = false;
|
|
3435
|
+
}
|
|
3436
|
+
if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
|
|
3437
|
+
output += ch;
|
|
3438
|
+
} else {
|
|
3439
|
+
const code = input.charCodeAt(i);
|
|
3440
|
+
if (code < 128) {
|
|
3441
|
+
output += BYTE_HEX[code];
|
|
3442
|
+
} else if (code < 55296 || code > 57343) {
|
|
3443
|
+
output += percentEncodeNonAscii(code);
|
|
3444
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3445
|
+
const low = input.charCodeAt(i + 1);
|
|
3446
|
+
if (low >= 56320 && low <= 57343) {
|
|
3447
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3448
|
+
i++;
|
|
3449
|
+
} else {
|
|
3450
|
+
output += percentEncodeNonAscii(65533);
|
|
3451
|
+
}
|
|
3452
|
+
} else {
|
|
3453
|
+
output += percentEncodeNonAscii(65533);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
return output;
|
|
3458
|
+
}
|
|
3459
|
+
function encodeComponent(input, isAllowed) {
|
|
3460
|
+
let output = "";
|
|
3461
|
+
for (let i = 0; i < input.length; i++) {
|
|
3462
|
+
const ch = input[i];
|
|
3463
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3464
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3465
|
+
if (isHexPair(hex3)) {
|
|
3466
|
+
output += "%" + hex3.toUpperCase();
|
|
3467
|
+
i += 2;
|
|
3468
|
+
continue;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
if (isAllowed(ch)) {
|
|
3472
|
+
output += ch;
|
|
3473
|
+
} else {
|
|
3474
|
+
const code = input.charCodeAt(i);
|
|
3475
|
+
if (code < 128) {
|
|
3476
|
+
output += BYTE_HEX[code];
|
|
3477
|
+
} else if (code < 55296 || code > 57343) {
|
|
3478
|
+
output += percentEncodeNonAscii(code);
|
|
3479
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3480
|
+
const low = input.charCodeAt(i + 1);
|
|
3481
|
+
if (low >= 56320 && low <= 57343) {
|
|
3482
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3483
|
+
i++;
|
|
3484
|
+
} else {
|
|
3485
|
+
output += percentEncodeNonAscii(65533);
|
|
3486
|
+
}
|
|
3487
|
+
} else {
|
|
3488
|
+
output += percentEncodeNonAscii(65533);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
return output;
|
|
3493
|
+
}
|
|
3494
|
+
function encodeUserinfo(input) {
|
|
3495
|
+
return encodeComponent(input, isUserinfoCharacter);
|
|
3496
|
+
}
|
|
3497
|
+
function encodeQuery(input) {
|
|
3498
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3499
|
+
}
|
|
3500
|
+
function encodeFragment(input) {
|
|
3501
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3502
|
+
}
|
|
3503
|
+
function isEscapeSafe(cp) {
|
|
3504
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
3505
|
+
}
|
|
3506
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
3507
|
+
let output = "";
|
|
3508
|
+
for (let i = 0; i < input.length; i++) {
|
|
3509
|
+
const ch = input[i];
|
|
3510
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3511
|
+
const hex3 = input.slice(i + 1, i + 3);
|
|
3512
|
+
if (isHexPair(hex3)) {
|
|
3513
|
+
const normalizedHex = hex3.toUpperCase();
|
|
3514
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3515
|
+
if (isUnreserved(decoded)) {
|
|
3516
|
+
output += decoded;
|
|
3517
|
+
} else {
|
|
3518
|
+
output += "%" + normalizedHex;
|
|
3519
|
+
}
|
|
3520
|
+
i += 2;
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
3525
|
+
output += ch;
|
|
3365
3526
|
} else {
|
|
3366
|
-
|
|
3527
|
+
const code = input.charCodeAt(i);
|
|
3528
|
+
if (code < 128) {
|
|
3529
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3530
|
+
} else if (code < 55296 || code > 57343) {
|
|
3531
|
+
output += percentEncodeNonAscii(code);
|
|
3532
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3533
|
+
const low = input.charCodeAt(i + 1);
|
|
3534
|
+
if (low >= 56320 && low <= 57343) {
|
|
3535
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3536
|
+
i++;
|
|
3537
|
+
} else {
|
|
3538
|
+
output += percentEncodeNonAscii(65533);
|
|
3539
|
+
}
|
|
3540
|
+
} else {
|
|
3541
|
+
output += percentEncodeNonAscii(65533);
|
|
3542
|
+
}
|
|
3367
3543
|
}
|
|
3368
3544
|
}
|
|
3369
3545
|
return output;
|
|
@@ -3386,14 +3562,18 @@ var require_utils = __commonJS({
|
|
|
3386
3562
|
function recomposeAuthority(component) {
|
|
3387
3563
|
const uriTokens = [];
|
|
3388
3564
|
if (component.userinfo !== void 0) {
|
|
3389
|
-
uriTokens.push(component.userinfo);
|
|
3565
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
3390
3566
|
uriTokens.push("@");
|
|
3391
3567
|
}
|
|
3392
3568
|
if (component.host !== void 0) {
|
|
3393
|
-
let host =
|
|
3569
|
+
let host = component.host;
|
|
3394
3570
|
if (!isIPv4(host)) {
|
|
3395
|
-
|
|
3396
|
-
if (ipV6res.isIPV6
|
|
3571
|
+
let ipV6res = normalizeIPv6(host);
|
|
3572
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
3573
|
+
host = normalizePercentEncoding(host, true);
|
|
3574
|
+
ipV6res = normalizeIPv6(host);
|
|
3575
|
+
}
|
|
3576
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3397
3577
|
host = `[${ipV6res.escapedHost}]`;
|
|
3398
3578
|
} else {
|
|
3399
3579
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -3402,8 +3582,12 @@ var require_utils = __commonJS({
|
|
|
3402
3582
|
uriTokens.push(host);
|
|
3403
3583
|
}
|
|
3404
3584
|
if (typeof component.port === "number" || typeof component.port === "string") {
|
|
3585
|
+
const port = String(component.port);
|
|
3586
|
+
if (!isPort(port)) {
|
|
3587
|
+
throw new TypeError("URI port is malformed.");
|
|
3588
|
+
}
|
|
3405
3589
|
uriTokens.push(":");
|
|
3406
|
-
uriTokens.push(
|
|
3590
|
+
uriTokens.push(port);
|
|
3407
3591
|
}
|
|
3408
3592
|
return uriTokens.length ? uriTokens.join("") : void 0;
|
|
3409
3593
|
}
|
|
@@ -3413,6 +3597,11 @@ var require_utils = __commonJS({
|
|
|
3413
3597
|
reescapeHostDelimiters,
|
|
3414
3598
|
normalizePercentEncoding,
|
|
3415
3599
|
normalizePathEncoding,
|
|
3600
|
+
serializePathEncoding,
|
|
3601
|
+
normalizeQueryFragmentEncoding,
|
|
3602
|
+
encodeUserinfo,
|
|
3603
|
+
encodeQuery,
|
|
3604
|
+
encodeFragment,
|
|
3416
3605
|
escapePreservingEscapes,
|
|
3417
3606
|
removeDotSegments,
|
|
3418
3607
|
isIPv4,
|
|
@@ -3428,7 +3617,7 @@ var require_schemes = __commonJS({
|
|
|
3428
3617
|
"node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3429
3618
|
"use strict";
|
|
3430
3619
|
var { isUUID } = require_utils();
|
|
3431
|
-
var URN_REG =
|
|
3620
|
+
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
3432
3621
|
var supportedSchemeNames = (
|
|
3433
3622
|
/** @type {const} */
|
|
3434
3623
|
[
|
|
@@ -3489,9 +3678,10 @@ var require_schemes = __commonJS({
|
|
|
3489
3678
|
wsComponent.secure = void 0;
|
|
3490
3679
|
}
|
|
3491
3680
|
if (wsComponent.resourceName) {
|
|
3492
|
-
const
|
|
3681
|
+
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3682
|
+
const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3493
3683
|
wsComponent.path = path && path !== "/" ? path : void 0;
|
|
3494
|
-
wsComponent.query =
|
|
3684
|
+
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3495
3685
|
wsComponent.resourceName = void 0;
|
|
3496
3686
|
}
|
|
3497
3687
|
wsComponent.fragment = void 0;
|
|
@@ -3503,7 +3693,7 @@ var require_schemes = __commonJS({
|
|
|
3503
3693
|
return urnComponent;
|
|
3504
3694
|
}
|
|
3505
3695
|
const matches = urnComponent.path.match(URN_REG);
|
|
3506
|
-
if (matches) {
|
|
3696
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
3507
3697
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
3508
3698
|
urnComponent.nid = matches[1].toLowerCase();
|
|
3509
3699
|
urnComponent.nss = matches[2];
|
|
@@ -3637,8 +3827,17 @@ var require_schemes = __commonJS({
|
|
|
3637
3827
|
var require_fast_uri = __commonJS({
|
|
3638
3828
|
"node_modules/fast-uri/index.js"(exports, module) {
|
|
3639
3829
|
"use strict";
|
|
3640
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
3830
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3641
3831
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3832
|
+
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
3833
|
+
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
3834
|
+
function decodeValidScheme(scheme) {
|
|
3835
|
+
const decodedScheme = unescape(String(scheme));
|
|
3836
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
3837
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
3838
|
+
}
|
|
3839
|
+
return decodedScheme;
|
|
3840
|
+
}
|
|
3642
3841
|
function normalize(uri, options) {
|
|
3643
3842
|
if (typeof uri === "string") {
|
|
3644
3843
|
uri = /** @type {T} */
|
|
@@ -3651,12 +3850,34 @@ var require_fast_uri = __commonJS({
|
|
|
3651
3850
|
}
|
|
3652
3851
|
function resolve3(baseURI, relativeURI, options) {
|
|
3653
3852
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3654
|
-
const {
|
|
3655
|
-
|
|
3656
|
-
|
|
3853
|
+
const {
|
|
3854
|
+
parsed: baseParsed,
|
|
3855
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
3856
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
3857
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
3858
|
+
malformedHost: baseMalformedHost,
|
|
3859
|
+
malformedScheme: baseMalformedScheme
|
|
3860
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
3861
|
+
const {
|
|
3862
|
+
parsed: relativeParsed,
|
|
3863
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
3864
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
3865
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
3866
|
+
malformedHost: relativeMalformedHost,
|
|
3867
|
+
malformedScheme: relativeMalformedScheme
|
|
3868
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
3869
|
+
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
3657
3870
|
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3658
3871
|
}
|
|
3659
3872
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3873
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
3874
|
+
const resolvedHost = resolved.host;
|
|
3875
|
+
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
3876
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
3877
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
|
|
3878
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
3879
|
+
throw new Error(resolved.error);
|
|
3880
|
+
}
|
|
3660
3881
|
schemelessOptions.skipEscape = true;
|
|
3661
3882
|
return serialize(resolved, schemelessOptions);
|
|
3662
3883
|
}
|
|
@@ -3716,7 +3937,7 @@ var require_fast_uri = __commonJS({
|
|
|
3716
3937
|
function equal(uriA, uriB, options) {
|
|
3717
3938
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
3718
3939
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3719
|
-
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA
|
|
3940
|
+
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
|
|
3720
3941
|
}
|
|
3721
3942
|
function serialize(cmpts, opts) {
|
|
3722
3943
|
const component = {
|
|
@@ -3737,19 +3958,22 @@ var require_fast_uri = __commonJS({
|
|
|
3737
3958
|
};
|
|
3738
3959
|
const options = Object.assign({}, opts);
|
|
3739
3960
|
const uriTokens = [];
|
|
3961
|
+
if (component.scheme) {
|
|
3962
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3963
|
+
}
|
|
3740
3964
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
3741
3965
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
3966
|
+
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
3967
|
+
const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
|
|
3742
3968
|
if (component.path !== void 0) {
|
|
3743
3969
|
if (!options.skipEscape) {
|
|
3744
|
-
component.path =
|
|
3745
|
-
if (component.scheme !== void 0) {
|
|
3746
|
-
component.path = component.path.split("%3A").join(":");
|
|
3747
|
-
}
|
|
3970
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
3748
3971
|
} else {
|
|
3749
3972
|
component.path = normalizePercentEncoding(component.path);
|
|
3750
3973
|
}
|
|
3751
3974
|
}
|
|
3752
3975
|
if (options.reference !== "suffix" && component.scheme) {
|
|
3976
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3753
3977
|
uriTokens.push(component.scheme, ":");
|
|
3754
3978
|
}
|
|
3755
3979
|
const authority = recomposeAuthority(component);
|
|
@@ -3767,16 +3991,19 @@ var require_fast_uri = __commonJS({
|
|
|
3767
3991
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
3768
3992
|
s = removeDotSegments(s);
|
|
3769
3993
|
}
|
|
3994
|
+
if (pathNoScheme) {
|
|
3995
|
+
s = serializePathEncoding(s, true);
|
|
3996
|
+
}
|
|
3770
3997
|
if (authority === void 0 && s[0] === "/" && s[1] === "/") {
|
|
3771
3998
|
s = "/%2F" + s.slice(2);
|
|
3772
3999
|
}
|
|
3773
4000
|
uriTokens.push(s);
|
|
3774
4001
|
}
|
|
3775
4002
|
if (component.query !== void 0) {
|
|
3776
|
-
uriTokens.push("?", component.query);
|
|
4003
|
+
uriTokens.push("?", encodeQuery(component.query));
|
|
3777
4004
|
}
|
|
3778
4005
|
if (component.fragment !== void 0) {
|
|
3779
|
-
uriTokens.push("#", component.fragment);
|
|
4006
|
+
uriTokens.push("#", encodeFragment(component.fragment));
|
|
3780
4007
|
}
|
|
3781
4008
|
return uriTokens.join("");
|
|
3782
4009
|
}
|
|
@@ -3792,6 +4019,35 @@ var require_fast_uri = __commonJS({
|
|
|
3792
4019
|
}
|
|
3793
4020
|
return void 0;
|
|
3794
4021
|
}
|
|
4022
|
+
function hasMalformedPercentEncoding(component) {
|
|
4023
|
+
if (component === void 0) return false;
|
|
4024
|
+
let percent = component.indexOf("%");
|
|
4025
|
+
while (percent !== -1) {
|
|
4026
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
4027
|
+
return true;
|
|
4028
|
+
}
|
|
4029
|
+
percent = component.indexOf("%", percent + 3);
|
|
4030
|
+
}
|
|
4031
|
+
return false;
|
|
4032
|
+
}
|
|
4033
|
+
function isIPLiteral(host) {
|
|
4034
|
+
return host[0] === "[" && host[host.length - 1] === "]";
|
|
4035
|
+
}
|
|
4036
|
+
function hasMalformedComponentPercentEncoding(matches) {
|
|
4037
|
+
const host = matches[4];
|
|
4038
|
+
return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
4039
|
+
}
|
|
4040
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP2) {
|
|
4041
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
|
|
4042
|
+
try {
|
|
4043
|
+
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
4044
|
+
} catch (e) {
|
|
4045
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
4046
|
+
return true;
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
return false;
|
|
4050
|
+
}
|
|
3795
4051
|
function parseWithStatus(uri, opts) {
|
|
3796
4052
|
const options = Object.assign({}, opts);
|
|
3797
4053
|
const parsed = {
|
|
@@ -3804,6 +4060,11 @@ var require_fast_uri = __commonJS({
|
|
|
3804
4060
|
fragment: void 0
|
|
3805
4061
|
};
|
|
3806
4062
|
let malformedAuthorityOrPort = false;
|
|
4063
|
+
let malformedPercentEncoding = false;
|
|
4064
|
+
let malformedSchemeSpecific = false;
|
|
4065
|
+
let malformedHost = false;
|
|
4066
|
+
let malformedIPLiteral = false;
|
|
4067
|
+
let malformedScheme = false;
|
|
3807
4068
|
let isIP2 = false;
|
|
3808
4069
|
if (options.reference === "suffix") {
|
|
3809
4070
|
if (options.scheme) {
|
|
@@ -3840,6 +4101,19 @@ var require_fast_uri = __commonJS({
|
|
|
3840
4101
|
parsed.path = matches[6] || "";
|
|
3841
4102
|
parsed.query = matches[7];
|
|
3842
4103
|
parsed.fragment = matches[8];
|
|
4104
|
+
if (parsed.scheme !== void 0) {
|
|
4105
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
4106
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
4107
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
4108
|
+
} else {
|
|
4109
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
4110
|
+
malformedScheme = true;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
4114
|
+
if (malformedPercentEncoding) {
|
|
4115
|
+
parsed.error = parsed.error || "URI contains malformed percent-encoding.";
|
|
4116
|
+
}
|
|
3843
4117
|
if (isNaN(parsed.port)) {
|
|
3844
4118
|
parsed.port = matches[5];
|
|
3845
4119
|
}
|
|
@@ -3851,9 +4125,16 @@ var require_fast_uri = __commonJS({
|
|
|
3851
4125
|
if (parsed.host) {
|
|
3852
4126
|
const ipv4result = isIPv4(parsed.host);
|
|
3853
4127
|
if (ipv4result === false) {
|
|
4128
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
4129
|
+
const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
|
|
3854
4130
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
3855
|
-
|
|
3856
|
-
|
|
4131
|
+
isIP2 = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4132
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4133
|
+
parsed.host = isIP2 ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4134
|
+
if (malformedIPLiteral) {
|
|
4135
|
+
parsed.error = parsed.error || "URI host is malformed.";
|
|
4136
|
+
malformedAuthorityOrPort = true;
|
|
4137
|
+
}
|
|
3857
4138
|
} else {
|
|
3858
4139
|
isIP2 = true;
|
|
3859
4140
|
}
|
|
@@ -3871,42 +4152,36 @@ var require_fast_uri = __commonJS({
|
|
|
3871
4152
|
parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
|
|
3872
4153
|
}
|
|
3873
4154
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
3874
|
-
if (!
|
|
3875
|
-
|
|
3876
|
-
try {
|
|
3877
|
-
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
3878
|
-
} catch (e) {
|
|
3879
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
4155
|
+
if (!malformedIPLiteral) {
|
|
4156
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP2);
|
|
3882
4157
|
}
|
|
3883
4158
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
3884
4159
|
if (uri.indexOf("%") !== -1) {
|
|
3885
|
-
if (parsed.
|
|
3886
|
-
parsed.
|
|
3887
|
-
|
|
3888
|
-
if (parsed.host !== void 0) {
|
|
3889
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP2);
|
|
4160
|
+
if (parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4161
|
+
const host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4162
|
+
parsed.host = reescapeHostDelimiters(host, isIP2);
|
|
3890
4163
|
}
|
|
3891
4164
|
}
|
|
3892
4165
|
if (parsed.path) {
|
|
3893
4166
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
3894
4167
|
}
|
|
4168
|
+
if (parsed.query) {
|
|
4169
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
4170
|
+
}
|
|
3895
4171
|
if (parsed.fragment) {
|
|
3896
|
-
|
|
3897
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3898
|
-
} catch {
|
|
3899
|
-
parsed.error = parsed.error || "URI malformed";
|
|
3900
|
-
}
|
|
4172
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
3901
4173
|
}
|
|
3902
4174
|
}
|
|
3903
4175
|
if (schemeHandler && schemeHandler.parse) {
|
|
3904
4176
|
schemeHandler.parse(parsed, options);
|
|
4177
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
|
|
4178
|
+
malformedSchemeSpecific = true;
|
|
4179
|
+
}
|
|
3905
4180
|
}
|
|
3906
4181
|
} else {
|
|
3907
4182
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
3908
4183
|
}
|
|
3909
|
-
return { parsed, malformedAuthorityOrPort };
|
|
4184
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
3910
4185
|
}
|
|
3911
4186
|
function parse3(uri, opts) {
|
|
3912
4187
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -3915,20 +4190,28 @@ var require_fast_uri = __commonJS({
|
|
|
3915
4190
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
3916
4191
|
}
|
|
3917
4192
|
function normalizeStringWithStatus(uri, opts) {
|
|
3918
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
4193
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
3919
4194
|
return {
|
|
3920
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
3921
|
-
malformedAuthorityOrPort
|
|
4195
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
4196
|
+
malformedAuthorityOrPort,
|
|
4197
|
+
malformedPercentEncoding,
|
|
4198
|
+
malformedSchemeSpecific,
|
|
4199
|
+
malformedHost,
|
|
4200
|
+
malformedScheme
|
|
3922
4201
|
};
|
|
3923
4202
|
}
|
|
3924
4203
|
function normalizeComparableURI(uri, opts) {
|
|
3925
|
-
if (typeof uri
|
|
3926
|
-
|
|
3927
|
-
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
4204
|
+
if (typeof uri !== "string" && typeof uri !== "object") {
|
|
4205
|
+
return void 0;
|
|
3928
4206
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
4207
|
+
let value;
|
|
4208
|
+
try {
|
|
4209
|
+
value = typeof uri === "string" ? uri : serialize(uri, opts);
|
|
4210
|
+
} catch {
|
|
4211
|
+
return void 0;
|
|
3931
4212
|
}
|
|
4213
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
4214
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
|
|
3932
4215
|
}
|
|
3933
4216
|
var fastUri = {
|
|
3934
4217
|
SCHEMES,
|
|
@@ -34678,6 +34961,7 @@ var StdioServerTransport = class {
|
|
|
34678
34961
|
};
|
|
34679
34962
|
|
|
34680
34963
|
// node_modules/@chrischall/mcp-utils/dist/errors/index.js
|
|
34964
|
+
var DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
34681
34965
|
var McpToolError = class extends Error {
|
|
34682
34966
|
/** Actionable remediation text, when one applies. */
|
|
34683
34967
|
hint;
|
|
@@ -34719,13 +35003,62 @@ var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')[^']*('
|
|
|
34719
35003
|
function redactSecrets(text) {
|
|
34720
35004
|
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]");
|
|
34721
35005
|
}
|
|
35006
|
+
function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
35007
|
+
const str = text === null || text === void 0 ? "" : String(text);
|
|
35008
|
+
const redacted = redactSecrets(str);
|
|
35009
|
+
if (redacted.length <= max)
|
|
35010
|
+
return redacted;
|
|
35011
|
+
return `${redacted.slice(0, max)}\u2026 [truncated]`;
|
|
35012
|
+
}
|
|
35013
|
+
function messageOf(err) {
|
|
35014
|
+
if (err instanceof Error)
|
|
35015
|
+
return err.message;
|
|
35016
|
+
return String(err);
|
|
35017
|
+
}
|
|
34722
35018
|
|
|
34723
|
-
// node_modules/@chrischall/mcp-utils/dist/response/
|
|
34724
|
-
|
|
34725
|
-
|
|
34726
|
-
|
|
34727
|
-
|
|
35019
|
+
// node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
35020
|
+
var VIEWS = ["compact", "full", "raw"];
|
|
35021
|
+
var DEFAULT_VIEW = "compact";
|
|
35022
|
+
var BLURB = {
|
|
35023
|
+
compact: '"compact" (default) drops fields the response already carries elsewhere',
|
|
35024
|
+
full: '"full" returns every field this server understands',
|
|
35025
|
+
raw: '"raw" returns the upstream payload unprojected'
|
|
35026
|
+
};
|
|
35027
|
+
function viewParam(honoured, opts = {}) {
|
|
35028
|
+
if (honoured.length < 2) {
|
|
35029
|
+
throw new Error("viewParam needs at least two rungs: a parameter offering one value decides nothing");
|
|
35030
|
+
}
|
|
35031
|
+
if (!honoured.includes("compact")) {
|
|
35032
|
+
throw new Error('viewParam must offer "compact": a tool with no cheap rung has nothing to default to');
|
|
35033
|
+
}
|
|
35034
|
+
const ordered = VIEWS.filter((v) => honoured.includes(v));
|
|
35035
|
+
const sentence = `Response shape: ${ordered.map((v) => BLURB[v]).join("; ")}.`;
|
|
35036
|
+
return external_exports.enum(Object.fromEntries(ordered.map((v) => [v, v]))).optional().describe(opts.note ? `${sentence} ${opts.note}` : sentence);
|
|
35037
|
+
}
|
|
35038
|
+
function resolveView(value, honoured) {
|
|
35039
|
+
return value !== void 0 && honoured.includes(value) ? value : DEFAULT_VIEW;
|
|
34728
35040
|
}
|
|
35041
|
+
function minifiedResult(data) {
|
|
35042
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
35043
|
+
}
|
|
35044
|
+
function projectOrRaw(value, project, opts) {
|
|
35045
|
+
try {
|
|
35046
|
+
const projected = project(value);
|
|
35047
|
+
if (projected === void 0) {
|
|
35048
|
+
warn(opts, "the projection produced nothing");
|
|
35049
|
+
return value;
|
|
35050
|
+
}
|
|
35051
|
+
return projected;
|
|
35052
|
+
} catch (error51) {
|
|
35053
|
+
warn(opts, error51 instanceof Error ? error51.message : String(error51));
|
|
35054
|
+
return value;
|
|
35055
|
+
}
|
|
35056
|
+
}
|
|
35057
|
+
function warn(opts, why) {
|
|
35058
|
+
console.error(`[${opts.label}] WARNING: could not project ${opts.context} (${why}); returning the unprojected payload. The upstream shape has probably changed \u2014 the projection needs updating.`);
|
|
35059
|
+
}
|
|
35060
|
+
|
|
35061
|
+
// node_modules/@chrischall/mcp-utils/dist/response/index.js
|
|
34729
35062
|
function rawTextResult(text) {
|
|
34730
35063
|
return { content: [{ type: "text", text }] };
|
|
34731
35064
|
}
|
|
@@ -34735,6 +35068,14 @@ function errorResult(message) {
|
|
|
34735
35068
|
isError: true
|
|
34736
35069
|
};
|
|
34737
35070
|
}
|
|
35071
|
+
function pruneUndefined(obj) {
|
|
35072
|
+
const out = {};
|
|
35073
|
+
for (const key of Object.keys(obj)) {
|
|
35074
|
+
if (obj[key] !== void 0)
|
|
35075
|
+
out[key] = obj[key];
|
|
35076
|
+
}
|
|
35077
|
+
return out;
|
|
35078
|
+
}
|
|
34738
35079
|
|
|
34739
35080
|
// node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34740
35081
|
function hintResultOrRethrow(err) {
|
|
@@ -39377,6 +39718,144 @@ var BootstrapDisabledError = class extends Error {
|
|
|
39377
39718
|
}
|
|
39378
39719
|
};
|
|
39379
39720
|
|
|
39721
|
+
// node_modules/@chrischall/mcp-utils/dist/healthcheck/index.js
|
|
39722
|
+
function statusOf(err) {
|
|
39723
|
+
if (typeof err !== "object" || err === null)
|
|
39724
|
+
return void 0;
|
|
39725
|
+
const s = err.status ?? err.statusCode;
|
|
39726
|
+
return typeof s === "number" ? s : void 0;
|
|
39727
|
+
}
|
|
39728
|
+
var CREDENTIAL_ARMS = /* @__PURE__ */ new Set([
|
|
39729
|
+
"ok",
|
|
39730
|
+
"no_credential",
|
|
39731
|
+
"credential_rejected",
|
|
39732
|
+
"timeout",
|
|
39733
|
+
"http",
|
|
39734
|
+
"transport",
|
|
39735
|
+
"unknown"
|
|
39736
|
+
]);
|
|
39737
|
+
function isArm(kind) {
|
|
39738
|
+
return kind !== void 0 && CREDENTIAL_ARMS.has(kind);
|
|
39739
|
+
}
|
|
39740
|
+
function credentialHint(arm, prefix, hostLabel, source) {
|
|
39741
|
+
switch (arm) {
|
|
39742
|
+
case "ok":
|
|
39743
|
+
return `Credential from '${source}' works: ${hostLabel} accepted an authenticated request. If a real tool still fails, the problem is that tool, not auth.`;
|
|
39744
|
+
case "no_credential":
|
|
39745
|
+
return `No credential resolved. Nothing was available to authenticate with \u2014 sign in and reconnect the connector so ${prefix} receives a token, or set the documented environment variable.`;
|
|
39746
|
+
case "credential_rejected":
|
|
39747
|
+
return `${hostLabel} rejected the credential from '${source}'. It is present but no longer valid \u2014 most often expired or revoked upstream. Re-authenticate and reconnect; retrying will not fix it.`;
|
|
39748
|
+
case "timeout":
|
|
39749
|
+
return `The credential from '${source}' resolved, but ${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
|
|
39750
|
+
case "http":
|
|
39751
|
+
return `${hostLabel} answered with an error status that is not an auth rejection. That is USUALLY a ${hostLabel}-side problem rather than an auth one \u2014 but a 404 here more often means the probe path is wrong than that ${hostLabel} is broken, so check error.message and probe.url before concluding anything about the credential.`;
|
|
39752
|
+
case "transport":
|
|
39753
|
+
return `Could not reach ${hostLabel} at all. Check network egress; the credential itself was never judged.`;
|
|
39754
|
+
default:
|
|
39755
|
+
return `Unexpected failure \u2014 see error.message.`;
|
|
39756
|
+
}
|
|
39757
|
+
}
|
|
39758
|
+
function registerCredentialHealthcheckTool(args) {
|
|
39759
|
+
const { server, prefix, hostLabel, probePath, resolveCredential, probeFn, classifyThrown, hints } = args;
|
|
39760
|
+
const probeUrl = probePath ? `https://${hostLabel}${probePath}` : void 0;
|
|
39761
|
+
server.registerTool(`${prefix}_healthcheck`, {
|
|
39762
|
+
title: "Verify credentials and upstream reachability",
|
|
39763
|
+
description: `Resolves the credential the way real tools do, then makes one authenticated request to ${hostLabel}. Reports which source supplied the credential, whether ${hostLabel} accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ${hostLabel}-side problem'. Call this when a real tool fails and you want to know which hop broke. Read-only; never returns the credential itself.`,
|
|
39764
|
+
annotations: {
|
|
39765
|
+
title: "Verify credentials and upstream reachability",
|
|
39766
|
+
readOnlyHint: true,
|
|
39767
|
+
idempotentHint: true,
|
|
39768
|
+
openWorldHint: true
|
|
39769
|
+
},
|
|
39770
|
+
inputSchema: {}
|
|
39771
|
+
}, async () => {
|
|
39772
|
+
let probeStarted = 0;
|
|
39773
|
+
let state;
|
|
39774
|
+
try {
|
|
39775
|
+
state = await resolveCredential();
|
|
39776
|
+
} catch (e) {
|
|
39777
|
+
const classified = classifyThrown?.(e);
|
|
39778
|
+
const result2 = {
|
|
39779
|
+
ok: false,
|
|
39780
|
+
// Still false, and still no source: a classification explains WHY
|
|
39781
|
+
// nothing resolved, it does not invent a credential that did.
|
|
39782
|
+
credential: { source: null, resolved: false },
|
|
39783
|
+
// No `url`: nothing was probed, and naming one implies it was tried.
|
|
39784
|
+
probe: { elapsed_ms: 0 },
|
|
39785
|
+
error: {
|
|
39786
|
+
kind: classified?.kind ?? "no_credential",
|
|
39787
|
+
message: truncateErrorMessage(messageOf(e)),
|
|
39788
|
+
...classified?.detail !== void 0 ? { detail: classified.detail } : {}
|
|
39789
|
+
},
|
|
39790
|
+
// The hint must follow the KIND beside it. Falling back to
|
|
39791
|
+
// `no_credential`'s copy under a classified kind would state a cause
|
|
39792
|
+
// the kind contradicts — the same disagreement this path exists to
|
|
39793
|
+
// remove. So: an inline hint wins; else the classified arm's own
|
|
39794
|
+
// copy (consumer override first); else, for a kind this module has
|
|
39795
|
+
// no copy for, the neutral `unknown` text rather than one that
|
|
39796
|
+
// asserts a cause; else the unclassified `no_credential` default.
|
|
39797
|
+
hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
|
|
39798
|
+
};
|
|
39799
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
39800
|
+
}
|
|
39801
|
+
const credential = {
|
|
39802
|
+
source: state.source,
|
|
39803
|
+
resolved: state.source !== null,
|
|
39804
|
+
...state.detail !== void 0 ? { detail: state.detail } : {}
|
|
39805
|
+
};
|
|
39806
|
+
if (!credential.resolved) {
|
|
39807
|
+
const result2 = {
|
|
39808
|
+
ok: false,
|
|
39809
|
+
credential,
|
|
39810
|
+
probe: { elapsed_ms: 0 },
|
|
39811
|
+
error: { kind: "no_credential", message: "no credential source resolved" },
|
|
39812
|
+
hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
|
|
39813
|
+
};
|
|
39814
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
39815
|
+
}
|
|
39816
|
+
let arm = "ok";
|
|
39817
|
+
let error51;
|
|
39818
|
+
let status;
|
|
39819
|
+
let customHint;
|
|
39820
|
+
probeStarted = Date.now();
|
|
39821
|
+
try {
|
|
39822
|
+
await probeFn();
|
|
39823
|
+
} catch (e) {
|
|
39824
|
+
status = statusOf(e);
|
|
39825
|
+
const aborted2 = e instanceof Error && e.name === "AbortError";
|
|
39826
|
+
arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e)) ? "transport" : "unknown";
|
|
39827
|
+
let kind = arm;
|
|
39828
|
+
let detail;
|
|
39829
|
+
const custom2 = classifyThrown?.(e);
|
|
39830
|
+
if (custom2) {
|
|
39831
|
+
kind = custom2.kind;
|
|
39832
|
+
customHint = custom2.hint;
|
|
39833
|
+
detail = custom2.detail;
|
|
39834
|
+
}
|
|
39835
|
+
error51 = {
|
|
39836
|
+
kind,
|
|
39837
|
+
// Redacted AND bounded before it reaches the result: an upstream
|
|
39838
|
+
// failure routinely quotes what it was sent, and a healthcheck is
|
|
39839
|
+
// the tool people paste into a chat when something is broken.
|
|
39840
|
+
message: truncateErrorMessage(messageOf(e)),
|
|
39841
|
+
...detail !== void 0 ? { detail } : {}
|
|
39842
|
+
};
|
|
39843
|
+
}
|
|
39844
|
+
const result = {
|
|
39845
|
+
ok: error51 === void 0,
|
|
39846
|
+
credential,
|
|
39847
|
+
probe: {
|
|
39848
|
+
...probeUrl ? { url: probeUrl } : {},
|
|
39849
|
+
elapsed_ms: Date.now() - probeStarted,
|
|
39850
|
+
...status !== void 0 ? { status } : {}
|
|
39851
|
+
},
|
|
39852
|
+
...error51 ? { error: error51 } : {},
|
|
39853
|
+
hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
|
|
39854
|
+
};
|
|
39855
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
39856
|
+
});
|
|
39857
|
+
}
|
|
39858
|
+
|
|
39380
39859
|
// src/protocol.ts
|
|
39381
39860
|
var BASE_URL = "https://ofw.ourfamilywizard.com";
|
|
39382
39861
|
var OFW_PROTOCOL_HEADERS = {
|
|
@@ -39431,7 +39910,7 @@ async function loginWithPassword(username, password) {
|
|
|
39431
39910
|
// package.json
|
|
39432
39911
|
var package_default = {
|
|
39433
39912
|
name: "ofw-mcp",
|
|
39434
|
-
version: "2.
|
|
39913
|
+
version: "2.15.0",
|
|
39435
39914
|
license: "MIT",
|
|
39436
39915
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
39437
39916
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
@@ -39465,7 +39944,7 @@ var package_default = {
|
|
|
39465
39944
|
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
39466
39945
|
},
|
|
39467
39946
|
dependencies: {
|
|
39468
|
-
"@chrischall/mcp-utils": "^0.
|
|
39947
|
+
"@chrischall/mcp-utils": "^0.21.0",
|
|
39469
39948
|
"@fetchproxy/bootstrap": "^2.2.0",
|
|
39470
39949
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39471
39950
|
dotenv: "^17.4.2",
|
|
@@ -39484,6 +39963,14 @@ var package_default = {
|
|
|
39484
39963
|
function fetchproxyDisabled() {
|
|
39485
39964
|
return parseBoolEnv("OFW_DISABLE_FETCHPROXY");
|
|
39486
39965
|
}
|
|
39966
|
+
var NO_AUTH_CONFIGURED = "OFW auth: set OFW_USERNAME + OFW_PASSWORD, or install the fetchproxy extension and sign into ourfamilywizard.com (unset OFW_DISABLE_FETCHPROXY if it is set).";
|
|
39967
|
+
var BRIDGE_DOWN_PREFIX = "OFW auth: fetchproxy bridge is down";
|
|
39968
|
+
function isBridgeDown(e) {
|
|
39969
|
+
return e instanceof Error && e.message.startsWith(BRIDGE_DOWN_PREFIX);
|
|
39970
|
+
}
|
|
39971
|
+
function isNoAuthConfigured(e) {
|
|
39972
|
+
return e instanceof Error && e.message === NO_AUTH_CONFIGURED;
|
|
39973
|
+
}
|
|
39487
39974
|
async function resolveAuth() {
|
|
39488
39975
|
const pattern = {};
|
|
39489
39976
|
const username = readEnvVar("OFW_USERNAME");
|
|
@@ -39531,7 +40018,7 @@ async function resolveAuth() {
|
|
|
39531
40018
|
if (classifyBridgeError(e) === "bridge_down") {
|
|
39532
40019
|
const downErr = e;
|
|
39533
40020
|
throw new Error(
|
|
39534
|
-
|
|
40021
|
+
`${BRIDGE_DOWN_PREFIX} (extension service worker unreachable after retry). ${downErr.hint}`
|
|
39535
40022
|
);
|
|
39536
40023
|
}
|
|
39537
40024
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -39542,9 +40029,7 @@ async function resolveAuth() {
|
|
|
39542
40029
|
};
|
|
39543
40030
|
}
|
|
39544
40031
|
if (!pattern.sessionScrape && !pattern.fetchproxy) {
|
|
39545
|
-
throw new Error(
|
|
39546
|
-
"OFW auth: set OFW_USERNAME + OFW_PASSWORD, or install the fetchproxy extension and sign into ourfamilywizard.com (unset OFW_DISABLE_FETCHPROXY if it is set)."
|
|
39547
|
-
);
|
|
40032
|
+
throw new Error(NO_AUTH_CONFIGURED);
|
|
39548
40033
|
}
|
|
39549
40034
|
const { credential, source, expiresAt } = await resolveAuthPattern(pattern);
|
|
39550
40035
|
return {
|
|
@@ -39961,7 +40446,7 @@ function normalizeTimestampsInValue(value, tz = displayTimeZone()) {
|
|
|
39961
40446
|
|
|
39962
40447
|
// src/tools/_shared.ts
|
|
39963
40448
|
function jsonResponse(data) {
|
|
39964
|
-
return
|
|
40449
|
+
return minifiedResult(normalizeTimestampsInValue(data));
|
|
39965
40450
|
}
|
|
39966
40451
|
var textResponse = rawTextResult;
|
|
39967
40452
|
function jsonErrorResponse(data) {
|
|
@@ -40068,6 +40553,51 @@ function registerUserTools(server, client2) {
|
|
|
40068
40553
|
});
|
|
40069
40554
|
}
|
|
40070
40555
|
|
|
40556
|
+
// src/tools/healthcheck.ts
|
|
40557
|
+
function registerHealthcheckTools(server, client2, resolve3 = resolveAuth) {
|
|
40558
|
+
registerCredentialHealthcheckTool({
|
|
40559
|
+
server,
|
|
40560
|
+
prefix: "ofw",
|
|
40561
|
+
hostLabel: "ourfamilywizard.com",
|
|
40562
|
+
// The same read `ofw_get_profile` makes: authenticated, cheap, and it
|
|
40563
|
+
// changes nothing. A healthcheck that marked a message read would be
|
|
40564
|
+
// co-parent-visible and irreversible.
|
|
40565
|
+
probePath: "/pub/v2/profiles",
|
|
40566
|
+
resolveCredential: async () => {
|
|
40567
|
+
try {
|
|
40568
|
+
const auth = await resolve3();
|
|
40569
|
+
return {
|
|
40570
|
+
source: auth.source,
|
|
40571
|
+
// Never the token. Expiry is the fact that explains a connector
|
|
40572
|
+
// that worked an hour ago and does not now.
|
|
40573
|
+
detail: auth.expiresAt ? { expires_at: auth.expiresAt.toISOString() } : void 0
|
|
40574
|
+
};
|
|
40575
|
+
} catch (e) {
|
|
40576
|
+
if (isNoAuthConfigured(e)) return { source: null };
|
|
40577
|
+
throw e;
|
|
40578
|
+
}
|
|
40579
|
+
},
|
|
40580
|
+
probeFn: () => client2.request("GET", "/pub/v2/profiles"),
|
|
40581
|
+
// A downed bridge is not a missing credential, and since mcp-utils 0.19.3
|
|
40582
|
+
// the helper consults this for a `resolveCredential` failure too — so it
|
|
40583
|
+
// gets its own arm instead of the `no_credential` copy. That copy could
|
|
40584
|
+
// previously only hedge across both cases and point at `error.message`;
|
|
40585
|
+
// now each answer names one cause and one fix.
|
|
40586
|
+
classifyThrown: (err) => isBridgeDown(err) ? {
|
|
40587
|
+
kind: "transport",
|
|
40588
|
+
// The upstream `.hint` rides along in `error.message` — it carries
|
|
40589
|
+
// the actionable "click the toolbar icon" copy this cannot know.
|
|
40590
|
+
hint: "The fetchproxy bridge is down, so the browser path could not be tried. This is not a credential problem: OFW_USERNAME/OFW_PASSWORD, if set, were not reached either. See error.message for the extension-specific fix."
|
|
40591
|
+
} : void 0,
|
|
40592
|
+
hints: {
|
|
40593
|
+
// Now means exactly what it says: nothing is set up. A configured path
|
|
40594
|
+
// that was tried and failed no longer lands here.
|
|
40595
|
+
no_credential: "No OFW credential is configured. Either set OFW_USERNAME + OFW_PASSWORD, or install the fetchproxy extension and sign in to ourfamilywizard.com in a tab (unsetting OFW_DISABLE_FETCHPROXY if you set it).",
|
|
40596
|
+
credential_rejected: "OurFamilyWizard rejected the credential. If it came from `env`, the password changed or the account is locked; if from `fetchproxy`, the browser session expired \u2014 sign in again in the tab. Retrying will not fix either."
|
|
40597
|
+
}
|
|
40598
|
+
});
|
|
40599
|
+
}
|
|
40600
|
+
|
|
40071
40601
|
// src/sync.ts
|
|
40072
40602
|
var FileMetaSchema = external_exports.looseObject({
|
|
40073
40603
|
fileId: external_exports.number(),
|
|
@@ -42069,6 +42599,101 @@ function withPaginationFirst(input) {
|
|
|
42069
42599
|
return { ...head, ...body, ...head };
|
|
42070
42600
|
}
|
|
42071
42601
|
|
|
42602
|
+
// src/tools/project.ts
|
|
42603
|
+
var MESSAGE_VIEWS = ["compact", "full"];
|
|
42604
|
+
var LABEL = "ofw-mcp";
|
|
42605
|
+
function asRecord2(value) {
|
|
42606
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
42607
|
+
}
|
|
42608
|
+
function senderOf(row) {
|
|
42609
|
+
if (typeof row.fromUser === "string" && row.fromUser !== "") return row.fromUser;
|
|
42610
|
+
const author = asRecord2(asRecord2(row.listData)?.author);
|
|
42611
|
+
const name = author?.name;
|
|
42612
|
+
return typeof name === "string" && name !== "" ? name : null;
|
|
42613
|
+
}
|
|
42614
|
+
function fileCountOf(row) {
|
|
42615
|
+
const files = asRecord2(row.listData)?.files;
|
|
42616
|
+
if (typeof files === "number") return files;
|
|
42617
|
+
if (Array.isArray(files)) return files.length;
|
|
42618
|
+
return void 0;
|
|
42619
|
+
}
|
|
42620
|
+
function repliedOf(row) {
|
|
42621
|
+
const replied = asRecord2(row.listData)?.replied;
|
|
42622
|
+
return typeof replied === "boolean" ? replied : void 0;
|
|
42623
|
+
}
|
|
42624
|
+
function compactMessage(row) {
|
|
42625
|
+
const { listData: _drop, fromUser: _dropFrom, ...rest } = row;
|
|
42626
|
+
return pruneUndefined({
|
|
42627
|
+
id: rest.id,
|
|
42628
|
+
folder: rest.folder,
|
|
42629
|
+
subject: rest.subject,
|
|
42630
|
+
from: senderOf(row),
|
|
42631
|
+
sentAt: rest.sentAt,
|
|
42632
|
+
recipients: rest.recipients,
|
|
42633
|
+
read: rest.read,
|
|
42634
|
+
replied: repliedOf(row),
|
|
42635
|
+
files: fileCountOf(row),
|
|
42636
|
+
replyToId: rest.replyToId,
|
|
42637
|
+
chainRootId: rest.chainRootId,
|
|
42638
|
+
body: rest.body,
|
|
42639
|
+
fetchedBodyAt: rest.fetchedBodyAt,
|
|
42640
|
+
// Anything a TOOL added on top of the cache row — `attachments`,
|
|
42641
|
+
// `revision`, `cacheStatus`, `serverConfirmed`, `freshness` — is kept
|
|
42642
|
+
// wholesale. Those are this server's own answers, never OFW's echo, and a
|
|
42643
|
+
// projection that dropped one would be removing the thing the caller
|
|
42644
|
+
// asked for rather than the thing they got twice.
|
|
42645
|
+
...omitKnown(rest, MESSAGE_ROW_KEYS)
|
|
42646
|
+
});
|
|
42647
|
+
}
|
|
42648
|
+
var MESSAGE_ROW_KEYS = /* @__PURE__ */ new Set([
|
|
42649
|
+
"id",
|
|
42650
|
+
"folder",
|
|
42651
|
+
"subject",
|
|
42652
|
+
"sentAt",
|
|
42653
|
+
"recipients",
|
|
42654
|
+
"read",
|
|
42655
|
+
"replyToId",
|
|
42656
|
+
"chainRootId",
|
|
42657
|
+
"body",
|
|
42658
|
+
"fetchedBodyAt"
|
|
42659
|
+
]);
|
|
42660
|
+
var DRAFT_ROW_KEYS = /* @__PURE__ */ new Set(["id", "subject", "recipients", "replyToId", "modifiedAt", "body"]);
|
|
42661
|
+
function omitKnown(rest, own) {
|
|
42662
|
+
const out = {};
|
|
42663
|
+
for (const [key, value] of Object.entries(rest)) if (!own.has(key)) out[key] = value;
|
|
42664
|
+
return out;
|
|
42665
|
+
}
|
|
42666
|
+
function compactDraft(row) {
|
|
42667
|
+
const { listData: _drop, ...rest } = row;
|
|
42668
|
+
return pruneUndefined({
|
|
42669
|
+
id: rest.id,
|
|
42670
|
+
subject: rest.subject,
|
|
42671
|
+
recipients: rest.recipients,
|
|
42672
|
+
replyToId: rest.replyToId,
|
|
42673
|
+
modifiedAt: rest.modifiedAt,
|
|
42674
|
+
body: rest.body,
|
|
42675
|
+
...omitKnown(rest, DRAFT_ROW_KEYS)
|
|
42676
|
+
});
|
|
42677
|
+
}
|
|
42678
|
+
function viewMessages(view, rows) {
|
|
42679
|
+
if (view !== "compact") return rows;
|
|
42680
|
+
return projectOrRaw(rows, (rs) => rs.map((r) => compactMessage(r)), {
|
|
42681
|
+
label: LABEL,
|
|
42682
|
+
context: "the cached message rows"
|
|
42683
|
+
});
|
|
42684
|
+
}
|
|
42685
|
+
function viewDrafts(view, rows) {
|
|
42686
|
+
if (view !== "compact") return rows;
|
|
42687
|
+
return projectOrRaw(rows, (rs) => rs.map((r) => compactDraft(r)), {
|
|
42688
|
+
label: LABEL,
|
|
42689
|
+
context: "the cached draft rows"
|
|
42690
|
+
});
|
|
42691
|
+
}
|
|
42692
|
+
function viewOne(view, row) {
|
|
42693
|
+
if (view !== "compact") return row;
|
|
42694
|
+
return projectOrRaw(row, (r) => compactMessage(r), { label: LABEL, context: "a cached message row" });
|
|
42695
|
+
}
|
|
42696
|
+
|
|
42072
42697
|
// src/tools/messages.ts
|
|
42073
42698
|
var DateSchema = external_exports.looseObject({ dateTime: external_exports.string() });
|
|
42074
42699
|
var SentDetailSchema = external_exports.looseObject({
|
|
@@ -42215,12 +42840,16 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42215
42840
|
until: external_exports.string().describe("ISO date or datetime \u2014 only messages with sent_at < until (exclusive)").optional(),
|
|
42216
42841
|
q: external_exports.string().describe("Substring match on subject AND body (case-insensitive). Use to find messages on a specific topic.").optional(),
|
|
42217
42842
|
sort: external_exports.enum(["newest", "oldest"]).describe('Result order: "newest" (default, newest first) or "oldest" (oldest first). This decides which end a truncated page keeps \u2014 with "newest" page 1 of a wide date range holds its most RECENT slice, with "oldest" its earliest. Use "oldest" to start at the old end of a range instead of paging to it.').optional(),
|
|
42218
|
-
autoRefresh: external_exports.boolean().describe(AUTO_REFRESH_DESC).optional()
|
|
42843
|
+
autoRefresh: external_exports.boolean().describe(AUTO_REFRESH_DESC).optional(),
|
|
42844
|
+
view: viewParam(MESSAGE_VIEWS, {
|
|
42845
|
+
note: "compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass \"full\" for the echo."
|
|
42846
|
+
})
|
|
42219
42847
|
}
|
|
42220
42848
|
}, async (args) => {
|
|
42221
42849
|
const page = args.page ?? 1;
|
|
42222
42850
|
const size = args.size ?? 50;
|
|
42223
42851
|
const sort = args.sort ?? "newest";
|
|
42852
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42224
42853
|
const folderArg = args.folderId ?? "both";
|
|
42225
42854
|
let folder;
|
|
42226
42855
|
if (folderArg === "inbox") folder = "inbox";
|
|
@@ -42292,7 +42921,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42292
42921
|
payload.autoRefreshed = true;
|
|
42293
42922
|
}
|
|
42294
42923
|
payload.freshness = freshness;
|
|
42295
|
-
payload.messages = messages;
|
|
42924
|
+
payload.messages = viewMessages(view, messages);
|
|
42296
42925
|
return jsonResponse(payload);
|
|
42297
42926
|
});
|
|
42298
42927
|
server.registerTool("ofw_get_message", {
|
|
@@ -42300,10 +42929,14 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42300
42929
|
annotations: { readOnlyHint: false },
|
|
42301
42930
|
inputSchema: {
|
|
42302
42931
|
messageId: external_exports.string().describe("Message ID (also accepts draft IDs \u2014 drafts are routed via the drafts cache)"),
|
|
42303
|
-
allowMarkRead: external_exports.boolean().describe("Default true (the long-standing behaviour). Set false to refuse a fetch that would mark an unread INBOX message as READ on OurFamilyWizard \u2014 an irreversible, co-parent-visible change to the record. Reads that cannot stamp anything (a cached body, a sent message, an already-read message) still succeed. The server-wide OFW_ALLOW_MARK_READ=false is a ceiling this argument cannot raise.").optional()
|
|
42932
|
+
allowMarkRead: external_exports.boolean().describe("Default true (the long-standing behaviour). Set false to refuse a fetch that would mark an unread INBOX message as READ on OurFamilyWizard \u2014 an irreversible, co-parent-visible change to the record. Reads that cannot stamp anything (a cached body, a sent message, an already-read message) still succeed. The server-wide OFW_ALLOW_MARK_READ=false is a ceiling this argument cannot raise.").optional(),
|
|
42933
|
+
view: viewParam(MESSAGE_VIEWS, {
|
|
42934
|
+
note: "compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass \"full\" for the echo."
|
|
42935
|
+
})
|
|
42304
42936
|
}
|
|
42305
42937
|
}, async (args) => {
|
|
42306
42938
|
const id = Number(args.messageId);
|
|
42939
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42307
42940
|
const cache = cacheProvider();
|
|
42308
42941
|
const draftRow = await cache.getDraft(id);
|
|
42309
42942
|
if (draftRow !== null) {
|
|
@@ -42317,7 +42950,13 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42317
42950
|
id: draftRow.id,
|
|
42318
42951
|
folder: "drafts",
|
|
42319
42952
|
subject: draftRow.subject,
|
|
42320
|
-
|
|
42953
|
+
// One tool, one sender key per rung. `compact` names the sender `from`
|
|
42954
|
+
// everywhere else, so emitting `fromUser` here would mean the same
|
|
42955
|
+
// tool's compact rung used two different key names depending on
|
|
42956
|
+
// whether the id happened to be a draft. `null` rather than '' because
|
|
42957
|
+
// a draft has no sender yet — it is unsent, and an empty string reads
|
|
42958
|
+
// as a sender whose name we failed to find.
|
|
42959
|
+
...view === "compact" ? { from: null } : { fromUser: "" },
|
|
42321
42960
|
sentAt: draftRow.modifiedAt,
|
|
42322
42961
|
recipients: draftRow.recipients,
|
|
42323
42962
|
body: draftRow.body,
|
|
@@ -42327,7 +42966,10 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42327
42966
|
fetchedBodyAt: draftRow.modifiedAt,
|
|
42328
42967
|
replyToId: draftRow.replyToId,
|
|
42329
42968
|
chainRootId: null,
|
|
42330
|
-
|
|
42969
|
+
// OFW's raw echo, on `full` only. Everything above it is derived from
|
|
42970
|
+
// the DRAFTS table, which is the source of truth for a draft id — the
|
|
42971
|
+
// echo duplicates it and adds a stale copy of nothing else.
|
|
42972
|
+
...view === "compact" ? {} : { listData: draftRow.listData },
|
|
42331
42973
|
attachments: [],
|
|
42332
42974
|
// Concurrency token — pass as expectedRevision to ofw_save_draft /
|
|
42333
42975
|
// ofw_delete_draft / ofw_send_message to assert you are acting on
|
|
@@ -42377,7 +43019,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42377
43019
|
}
|
|
42378
43020
|
}
|
|
42379
43021
|
const freshness2 = await buildFreshness(cache, { source: "cache", folders: [row2.folder] });
|
|
42380
|
-
return jsonResponse({ ...withReadState(row2), attachments: attachments2, freshness: freshness2 });
|
|
43022
|
+
return jsonResponse({ ...viewOne(view, withReadState(row2)), attachments: attachments2, freshness: freshness2 });
|
|
42381
43023
|
}
|
|
42382
43024
|
const markReadCheck = markReadVerdict(cached2, args.allowMarkRead);
|
|
42383
43025
|
if (markReadCheck !== null) return markReadCheck;
|
|
@@ -42412,7 +43054,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42412
43054
|
}
|
|
42413
43055
|
const attachments = await cache.listAttachmentsForMessage(detail.id);
|
|
42414
43056
|
const freshness = await buildFreshness(cache, { source: "live", folders: [folder] });
|
|
42415
|
-
return jsonResponse({ ...withReadState(row), attachments, freshness });
|
|
43057
|
+
return jsonResponse({ ...viewOne(view, withReadState(row)), attachments, freshness });
|
|
42416
43058
|
});
|
|
42417
43059
|
if (allowSend) server.registerTool("ofw_send_message", {
|
|
42418
43060
|
description: "Send a message via OurFamilyWizard \u2014 the ONE irreversible operation here, so it carries the strongest guard. TO SEND AN EXISTING DRAFT (the safe default): pass draftId (or messageId \u2014 same thing). The tool re-reads the draft from OFW and sends the SERVER'S version, so what goes out is what is on OurFamilyWizard, not what this session remembers \u2014 subject/body act only as explicit overrides. It is guarded exactly like ofw_save_draft: pass expectedRevision to assert which version you are sending; if the draft changed on OFW since you read it \u2014 or no longer exists (it may already have been SENT) \u2014 the send is REFUSED with the current server content echoed back, and nothing goes out. RECIPIENTS: OurFamilyWizard does not persist recipients on drafts, so recipientIds is usually still required at send time (ids from ofw_get_profile). After the send is CONFIRMED (OFW returned the new message id and the re-fetched sent record matches what was posted), the source draft is deleted automatically; pass deleteDraftOnSuccess:false to keep it. On ANY failure or ambiguity the draft is never deleted \u2014 the response carries draftRetained:true with the reason. TO COMPOSE FROM SCRATCH: supply subject/body/recipientIds with no draftId. If replyToId is provided (or inherited from the draft), the cache may rewrite it to the latest reply in the same thread (a note is included when this happens). ATTACHMENTS: when sending by draftId, the server draft's own attachments carry over automatically; myFileIDs (from ofw_upload_attachment) overrides or attaches files on a fresh compose. The response leads with sentMessageId and the stable draftKey, and reports threaded (whether OFW actually linked the reply) and draftDeleted.",
|
|
@@ -42684,11 +43326,15 @@ ${JSON.stringify(
|
|
|
42684
43326
|
page: external_exports.number().int().min(1).describe("Page number (default 1)").optional(),
|
|
42685
43327
|
size: external_exports.number().int().min(1).describe("Drafts per page (default 50)").optional(),
|
|
42686
43328
|
verify: external_exports.boolean().describe("Default true: when the drafts cache is not verified-fresh, run a drafts sync first (cheap \u2014 one list page plus one detail per draft) so the response is server-confirmed in one call. Set false to serve straight from the local cache with no OFW requests.").optional(),
|
|
42687
|
-
autoRefresh: external_exports.boolean().describe(AUTO_REFRESH_DESC).optional()
|
|
43329
|
+
autoRefresh: external_exports.boolean().describe(AUTO_REFRESH_DESC).optional(),
|
|
43330
|
+
view: viewParam(MESSAGE_VIEWS, {
|
|
43331
|
+
note: "compact omits OurFamilyWizard's raw `listData` echo, which duplicates this draft's own id, subject, modifiedAt and recipients. `revision`, `draftKey` and `cacheStatus` are kept on both rungs."
|
|
43332
|
+
})
|
|
42688
43333
|
}
|
|
42689
43334
|
}, async (args) => {
|
|
42690
43335
|
const page = args.page ?? 1;
|
|
42691
43336
|
const size = args.size ?? 50;
|
|
43337
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42692
43338
|
const cache = cacheProvider();
|
|
42693
43339
|
let autoVerified = false;
|
|
42694
43340
|
let verifyNote = null;
|
|
@@ -42768,7 +43414,7 @@ ${JSON.stringify(
|
|
|
42768
43414
|
payload.verifyNote = verifyNote;
|
|
42769
43415
|
}
|
|
42770
43416
|
payload.freshness = freshness;
|
|
42771
|
-
payload.drafts = drafts;
|
|
43417
|
+
payload.drafts = viewDrafts(view, drafts);
|
|
42772
43418
|
return jsonResponse(payload);
|
|
42773
43419
|
});
|
|
42774
43420
|
if (allowDrafts) server.registerTool("ofw_save_draft", {
|
|
@@ -42959,6 +43605,11 @@ ${text}` : text);
|
|
|
42959
43605
|
page: external_exports.number().int().min(1).describe("Page (default 1)").optional(),
|
|
42960
43606
|
size: external_exports.number().int().min(1).describe("Per page (default 50)").optional(),
|
|
42961
43607
|
autoRefresh: external_exports.boolean().describe(AUTO_REFRESH_DESC).optional()
|
|
43608
|
+
// No `view` here, deliberately. This tool never emitted a cache row: it
|
|
43609
|
+
// builds a VERDICT list of `{id, subject, sentAt, unreadBy}`, which is
|
|
43610
|
+
// already narrower than anything the compact projection would produce.
|
|
43611
|
+
// A parameter offering a rung that changes nothing is the no-op schema
|
|
43612
|
+
// `viewParam` exists to refuse.
|
|
42962
43613
|
}
|
|
42963
43614
|
}, async (args) => {
|
|
42964
43615
|
const page = args.page ?? 1;
|
|
@@ -44244,10 +44895,11 @@ var nodeCacheProvider = () => nodeCache ??= OFWCache.open(getCacheDbPath());
|
|
|
44244
44895
|
var nodeAttachmentIO = new NodeAttachmentIO();
|
|
44245
44896
|
await runMcp({
|
|
44246
44897
|
name: "ofw",
|
|
44247
|
-
version: "2.
|
|
44898
|
+
version: "2.15.0",
|
|
44248
44899
|
// x-release-please-version
|
|
44249
44900
|
deps: client,
|
|
44250
44901
|
tools: [
|
|
44902
|
+
registerHealthcheckTools,
|
|
44251
44903
|
registerUserTools,
|
|
44252
44904
|
(server, deps) => registerMessageTools(server, deps, nodeCacheProvider, nodeAttachmentIO),
|
|
44253
44905
|
registerCalendarTools,
|