ofw-mcp 2.14.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/bundle.js +593 -144
- package/dist/index.js +1 -1
- package/dist/tools/_shared.js +16 -6
- 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;
|
|
3180
|
-
}
|
|
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;
|
|
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;
|
|
3196
3193
|
}
|
|
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,
|
|
@@ -34733,12 +35016,49 @@ function messageOf(err) {
|
|
|
34733
35016
|
return String(err);
|
|
34734
35017
|
}
|
|
34735
35018
|
|
|
34736
|
-
// node_modules/@chrischall/mcp-utils/dist/response/
|
|
34737
|
-
|
|
34738
|
-
|
|
34739
|
-
|
|
34740
|
-
|
|
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;
|
|
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
|
+
}
|
|
34741
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
|
|
34742
35062
|
function rawTextResult(text) {
|
|
34743
35063
|
return { content: [{ type: "text", text }] };
|
|
34744
35064
|
}
|
|
@@ -34748,6 +35068,14 @@ function errorResult(message) {
|
|
|
34748
35068
|
isError: true
|
|
34749
35069
|
};
|
|
34750
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
|
+
}
|
|
34751
35079
|
|
|
34752
35080
|
// node_modules/@chrischall/mcp-utils/dist/server/index.js
|
|
34753
35081
|
function hintResultOrRethrow(err) {
|
|
@@ -39582,7 +39910,7 @@ async function loginWithPassword(username, password) {
|
|
|
39582
39910
|
// package.json
|
|
39583
39911
|
var package_default = {
|
|
39584
39912
|
name: "ofw-mcp",
|
|
39585
|
-
version: "2.
|
|
39913
|
+
version: "2.15.0",
|
|
39586
39914
|
license: "MIT",
|
|
39587
39915
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
39588
39916
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
@@ -39616,7 +39944,7 @@ var package_default = {
|
|
|
39616
39944
|
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
39617
39945
|
},
|
|
39618
39946
|
dependencies: {
|
|
39619
|
-
"@chrischall/mcp-utils": "^0.
|
|
39947
|
+
"@chrischall/mcp-utils": "^0.21.0",
|
|
39620
39948
|
"@fetchproxy/bootstrap": "^2.2.0",
|
|
39621
39949
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39622
39950
|
dotenv: "^17.4.2",
|
|
@@ -40118,7 +40446,7 @@ function normalizeTimestampsInValue(value, tz = displayTimeZone()) {
|
|
|
40118
40446
|
|
|
40119
40447
|
// src/tools/_shared.ts
|
|
40120
40448
|
function jsonResponse(data) {
|
|
40121
|
-
return
|
|
40449
|
+
return minifiedResult(normalizeTimestampsInValue(data));
|
|
40122
40450
|
}
|
|
40123
40451
|
var textResponse = rawTextResult;
|
|
40124
40452
|
function jsonErrorResponse(data) {
|
|
@@ -42271,6 +42599,101 @@ function withPaginationFirst(input) {
|
|
|
42271
42599
|
return { ...head, ...body, ...head };
|
|
42272
42600
|
}
|
|
42273
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
|
+
|
|
42274
42697
|
// src/tools/messages.ts
|
|
42275
42698
|
var DateSchema = external_exports.looseObject({ dateTime: external_exports.string() });
|
|
42276
42699
|
var SentDetailSchema = external_exports.looseObject({
|
|
@@ -42417,12 +42840,16 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42417
42840
|
until: external_exports.string().describe("ISO date or datetime \u2014 only messages with sent_at < until (exclusive)").optional(),
|
|
42418
42841
|
q: external_exports.string().describe("Substring match on subject AND body (case-insensitive). Use to find messages on a specific topic.").optional(),
|
|
42419
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(),
|
|
42420
|
-
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
|
+
})
|
|
42421
42847
|
}
|
|
42422
42848
|
}, async (args) => {
|
|
42423
42849
|
const page = args.page ?? 1;
|
|
42424
42850
|
const size = args.size ?? 50;
|
|
42425
42851
|
const sort = args.sort ?? "newest";
|
|
42852
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42426
42853
|
const folderArg = args.folderId ?? "both";
|
|
42427
42854
|
let folder;
|
|
42428
42855
|
if (folderArg === "inbox") folder = "inbox";
|
|
@@ -42494,7 +42921,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42494
42921
|
payload.autoRefreshed = true;
|
|
42495
42922
|
}
|
|
42496
42923
|
payload.freshness = freshness;
|
|
42497
|
-
payload.messages = messages;
|
|
42924
|
+
payload.messages = viewMessages(view, messages);
|
|
42498
42925
|
return jsonResponse(payload);
|
|
42499
42926
|
});
|
|
42500
42927
|
server.registerTool("ofw_get_message", {
|
|
@@ -42502,10 +42929,14 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42502
42929
|
annotations: { readOnlyHint: false },
|
|
42503
42930
|
inputSchema: {
|
|
42504
42931
|
messageId: external_exports.string().describe("Message ID (also accepts draft IDs \u2014 drafts are routed via the drafts cache)"),
|
|
42505
|
-
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
|
+
})
|
|
42506
42936
|
}
|
|
42507
42937
|
}, async (args) => {
|
|
42508
42938
|
const id = Number(args.messageId);
|
|
42939
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42509
42940
|
const cache = cacheProvider();
|
|
42510
42941
|
const draftRow = await cache.getDraft(id);
|
|
42511
42942
|
if (draftRow !== null) {
|
|
@@ -42519,7 +42950,13 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42519
42950
|
id: draftRow.id,
|
|
42520
42951
|
folder: "drafts",
|
|
42521
42952
|
subject: draftRow.subject,
|
|
42522
|
-
|
|
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: "" },
|
|
42523
42960
|
sentAt: draftRow.modifiedAt,
|
|
42524
42961
|
recipients: draftRow.recipients,
|
|
42525
42962
|
body: draftRow.body,
|
|
@@ -42529,7 +42966,10 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42529
42966
|
fetchedBodyAt: draftRow.modifiedAt,
|
|
42530
42967
|
replyToId: draftRow.replyToId,
|
|
42531
42968
|
chainRootId: null,
|
|
42532
|
-
|
|
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 },
|
|
42533
42973
|
attachments: [],
|
|
42534
42974
|
// Concurrency token — pass as expectedRevision to ofw_save_draft /
|
|
42535
42975
|
// ofw_delete_draft / ofw_send_message to assert you are acting on
|
|
@@ -42579,7 +43019,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42579
43019
|
}
|
|
42580
43020
|
}
|
|
42581
43021
|
const freshness2 = await buildFreshness(cache, { source: "cache", folders: [row2.folder] });
|
|
42582
|
-
return jsonResponse({ ...withReadState(row2), attachments: attachments2, freshness: freshness2 });
|
|
43022
|
+
return jsonResponse({ ...viewOne(view, withReadState(row2)), attachments: attachments2, freshness: freshness2 });
|
|
42583
43023
|
}
|
|
42584
43024
|
const markReadCheck = markReadVerdict(cached2, args.allowMarkRead);
|
|
42585
43025
|
if (markReadCheck !== null) return markReadCheck;
|
|
@@ -42614,7 +43054,7 @@ function registerMessageTools(server, client2, cacheProvider, attachmentIO) {
|
|
|
42614
43054
|
}
|
|
42615
43055
|
const attachments = await cache.listAttachmentsForMessage(detail.id);
|
|
42616
43056
|
const freshness = await buildFreshness(cache, { source: "live", folders: [folder] });
|
|
42617
|
-
return jsonResponse({ ...withReadState(row), attachments, freshness });
|
|
43057
|
+
return jsonResponse({ ...viewOne(view, withReadState(row)), attachments, freshness });
|
|
42618
43058
|
});
|
|
42619
43059
|
if (allowSend) server.registerTool("ofw_send_message", {
|
|
42620
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.",
|
|
@@ -42886,11 +43326,15 @@ ${JSON.stringify(
|
|
|
42886
43326
|
page: external_exports.number().int().min(1).describe("Page number (default 1)").optional(),
|
|
42887
43327
|
size: external_exports.number().int().min(1).describe("Drafts per page (default 50)").optional(),
|
|
42888
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(),
|
|
42889
|
-
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
|
+
})
|
|
42890
43333
|
}
|
|
42891
43334
|
}, async (args) => {
|
|
42892
43335
|
const page = args.page ?? 1;
|
|
42893
43336
|
const size = args.size ?? 50;
|
|
43337
|
+
const view = resolveView(args.view, MESSAGE_VIEWS);
|
|
42894
43338
|
const cache = cacheProvider();
|
|
42895
43339
|
let autoVerified = false;
|
|
42896
43340
|
let verifyNote = null;
|
|
@@ -42970,7 +43414,7 @@ ${JSON.stringify(
|
|
|
42970
43414
|
payload.verifyNote = verifyNote;
|
|
42971
43415
|
}
|
|
42972
43416
|
payload.freshness = freshness;
|
|
42973
|
-
payload.drafts = drafts;
|
|
43417
|
+
payload.drafts = viewDrafts(view, drafts);
|
|
42974
43418
|
return jsonResponse(payload);
|
|
42975
43419
|
});
|
|
42976
43420
|
if (allowDrafts) server.registerTool("ofw_save_draft", {
|
|
@@ -43161,6 +43605,11 @@ ${text}` : text);
|
|
|
43161
43605
|
page: external_exports.number().int().min(1).describe("Page (default 1)").optional(),
|
|
43162
43606
|
size: external_exports.number().int().min(1).describe("Per page (default 50)").optional(),
|
|
43163
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.
|
|
43164
43613
|
}
|
|
43165
43614
|
}, async (args) => {
|
|
43166
43615
|
const page = args.page ?? 1;
|
|
@@ -44446,7 +44895,7 @@ var nodeCacheProvider = () => nodeCache ??= OFWCache.open(getCacheDbPath());
|
|
|
44446
44895
|
var nodeAttachmentIO = new NodeAttachmentIO();
|
|
44447
44896
|
await runMcp({
|
|
44448
44897
|
name: "ofw",
|
|
44449
|
-
version: "2.
|
|
44898
|
+
version: "2.15.0",
|
|
44450
44899
|
// x-release-please-version
|
|
44451
44900
|
deps: client,
|
|
44452
44901
|
tools: [
|