toolcraft 0.0.310 → 0.0.312
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/composition.json +3 -3
- package/dist/composition.json +3 -3
- package/node_modules/auth-store/README.md +20 -0
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +6 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +15 -0
- package/node_modules/auth-store/dist/index.d.ts +1 -0
- package/node_modules/auth-store/dist/keychain-store.d.ts +8 -0
- package/node_modules/auth-store/dist/keychain-store.js +13 -0
- package/node_modules/auth-store/dist/transaction-lock.d.ts +24 -0
- package/node_modules/auth-store/dist/transaction-lock.js +152 -0
- package/node_modules/auth-store/dist/types.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +30 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +23 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +176 -182
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/session-transaction.d.ts +6 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/session-transaction.js +42 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +3 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +7 -3
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/types.d.ts +9 -0
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +35 -0
- package/node_modules/tiny-mcp-client/dist/index.js +486 -249
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
|
@@ -279,8 +279,8 @@ function receivedType(value) {
|
|
|
279
279
|
}
|
|
280
280
|
return typeof value;
|
|
281
281
|
}
|
|
282
|
-
function issue(
|
|
283
|
-
return { path:
|
|
282
|
+
function issue(path5, expected, value, message, keyword = keywordFor(expected)) {
|
|
283
|
+
return { path: path5, expected, received: receivedType(value), message, keyword };
|
|
284
284
|
}
|
|
285
285
|
function keywordFor(expected) {
|
|
286
286
|
if (["null", "boolean", "object", "array", "number", "integer", "string"].includes(expected)) {
|
|
@@ -997,113 +997,113 @@ function evaluateArrayApplicators(graph, node, schema, value, context) {
|
|
|
997
997
|
}
|
|
998
998
|
return results;
|
|
999
999
|
}
|
|
1000
|
-
function evaluateValidationKeywords(node, schema, value,
|
|
1000
|
+
function evaluateValidationKeywords(node, schema, value, path5) {
|
|
1001
1001
|
const results = [];
|
|
1002
1002
|
const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) ? schema.type : [];
|
|
1003
1003
|
if (types.length > 0 && !types.some((type) => typeof type === "string" && typeMatches(type, value))) {
|
|
1004
|
-
results.push(invalidResult(issue(
|
|
1004
|
+
results.push(invalidResult(issue(path5, types.join(","), value, `must be ${types.join(",")}`)));
|
|
1005
1005
|
return results;
|
|
1006
1006
|
}
|
|
1007
1007
|
if (schema.const !== void 0 && !deepEqual(schema.const, value)) {
|
|
1008
|
-
results.push(invalidResult(issue(
|
|
1008
|
+
results.push(invalidResult(issue(path5, "const", value, "must be equal to constant")));
|
|
1009
1009
|
}
|
|
1010
1010
|
if (Array.isArray(schema.enum) && !schema.enum.some((entry) => deepEqual(entry, value))) {
|
|
1011
|
-
results.push(invalidResult(issue(
|
|
1011
|
+
results.push(invalidResult(issue(path5, "enum", value, "must be equal to one of the allowed values")));
|
|
1012
1012
|
}
|
|
1013
1013
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1014
|
-
evaluateNumberKeywords(schema, value,
|
|
1014
|
+
evaluateNumberKeywords(schema, value, path5, results);
|
|
1015
1015
|
}
|
|
1016
1016
|
if (typeof value === "string") {
|
|
1017
|
-
evaluateStringKeywords(schema, value,
|
|
1017
|
+
evaluateStringKeywords(schema, value, path5, results);
|
|
1018
1018
|
}
|
|
1019
1019
|
if (Array.isArray(value)) {
|
|
1020
|
-
evaluateArrayKeywords(schema, value,
|
|
1020
|
+
evaluateArrayKeywords(schema, value, path5, results);
|
|
1021
1021
|
}
|
|
1022
1022
|
if (isObject(value)) {
|
|
1023
|
-
evaluateObjectKeywords(node, schema, value,
|
|
1023
|
+
evaluateObjectKeywords(node, schema, value, path5, results);
|
|
1024
1024
|
}
|
|
1025
1025
|
return results;
|
|
1026
1026
|
}
|
|
1027
|
-
function evaluateNumberKeywords(schema, value,
|
|
1027
|
+
function evaluateNumberKeywords(schema, value, path5, results) {
|
|
1028
1028
|
if (typeof schema.multipleOf === "number" && !isMultipleOf(value, schema.multipleOf)) {
|
|
1029
|
-
results.push(invalidResult(issue(
|
|
1029
|
+
results.push(invalidResult(issue(path5, `multiple of ${schema.multipleOf}`, value, `must be multiple of ${schema.multipleOf}`)));
|
|
1030
1030
|
}
|
|
1031
1031
|
if (typeof schema.maximum === "number" && value > schema.maximum) {
|
|
1032
|
-
results.push(invalidResult(issue(
|
|
1032
|
+
results.push(invalidResult(issue(path5, `<= ${schema.maximum}`, value, `must be <= ${schema.maximum}`)));
|
|
1033
1033
|
}
|
|
1034
1034
|
if (typeof schema.minimum === "number" && value < schema.minimum) {
|
|
1035
|
-
results.push(invalidResult(issue(
|
|
1035
|
+
results.push(invalidResult(issue(path5, `>= ${schema.minimum}`, value, `must be >= ${schema.minimum}`)));
|
|
1036
1036
|
}
|
|
1037
1037
|
if (typeof schema.exclusiveMaximum === "number" && value >= schema.exclusiveMaximum) {
|
|
1038
|
-
results.push(invalidResult(issue(
|
|
1038
|
+
results.push(invalidResult(issue(path5, `< ${schema.exclusiveMaximum}`, value, `must be < ${schema.exclusiveMaximum}`)));
|
|
1039
1039
|
}
|
|
1040
1040
|
if (typeof schema.exclusiveMinimum === "number" && value <= schema.exclusiveMinimum) {
|
|
1041
|
-
results.push(invalidResult(issue(
|
|
1041
|
+
results.push(invalidResult(issue(path5, `> ${schema.exclusiveMinimum}`, value, `must be > ${schema.exclusiveMinimum}`)));
|
|
1042
1042
|
}
|
|
1043
1043
|
}
|
|
1044
|
-
function evaluateStringKeywords(schema, value,
|
|
1044
|
+
function evaluateStringKeywords(schema, value, path5, results) {
|
|
1045
1045
|
const length = unicodeLength(value);
|
|
1046
1046
|
if (typeof schema.maxLength === "number" && length > schema.maxLength) {
|
|
1047
|
-
results.push(invalidResult(issue(
|
|
1047
|
+
results.push(invalidResult(issue(path5, `length <= ${schema.maxLength}`, value, `must NOT have more than ${schema.maxLength} characters`)));
|
|
1048
1048
|
}
|
|
1049
1049
|
if (typeof schema.minLength === "number" && length < schema.minLength) {
|
|
1050
|
-
results.push(invalidResult(issue(
|
|
1050
|
+
results.push(invalidResult(issue(path5, `length >= ${schema.minLength}`, value, `must NOT have fewer than ${schema.minLength} characters`)));
|
|
1051
1051
|
}
|
|
1052
1052
|
if (typeof schema.pattern === "string" && !new RegExp(schema.pattern, "u").test(value)) {
|
|
1053
|
-
results.push(invalidResult(issue(
|
|
1053
|
+
results.push(invalidResult(issue(path5, `pattern ${schema.pattern}`, value, `must match pattern ${schema.pattern}`)));
|
|
1054
1054
|
}
|
|
1055
1055
|
}
|
|
1056
|
-
function evaluateArrayKeywords(schema, value,
|
|
1056
|
+
function evaluateArrayKeywords(schema, value, path5, results) {
|
|
1057
1057
|
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
|
|
1058
|
-
results.push(invalidResult(issue(
|
|
1058
|
+
results.push(invalidResult(issue(path5, `items <= ${schema.maxItems}`, value, `must NOT have more than ${schema.maxItems} items`)));
|
|
1059
1059
|
}
|
|
1060
1060
|
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
1061
|
-
results.push(invalidResult(issue(
|
|
1061
|
+
results.push(invalidResult(issue(path5, `items >= ${schema.minItems}`, value, `must NOT have fewer than ${schema.minItems} items`)));
|
|
1062
1062
|
}
|
|
1063
1063
|
if (schema.uniqueItems === true) {
|
|
1064
1064
|
for (let left = 0; left < value.length; left += 1) {
|
|
1065
1065
|
for (let right = left + 1; right < value.length; right += 1) {
|
|
1066
1066
|
if (deepEqual(value[left], value[right])) {
|
|
1067
|
-
results.push(invalidResult(issue(
|
|
1067
|
+
results.push(invalidResult(issue(path5, "unique items", value, "must NOT have duplicate items")));
|
|
1068
1068
|
return;
|
|
1069
1069
|
}
|
|
1070
1070
|
}
|
|
1071
1071
|
}
|
|
1072
1072
|
}
|
|
1073
1073
|
}
|
|
1074
|
-
function evaluateObjectKeywords(node, schema, value,
|
|
1074
|
+
function evaluateObjectKeywords(node, schema, value, path5, results) {
|
|
1075
1075
|
const keys = Object.keys(value);
|
|
1076
1076
|
if (typeof schema.maxProperties === "number" && keys.length > schema.maxProperties) {
|
|
1077
|
-
results.push(invalidResult(issue(
|
|
1077
|
+
results.push(invalidResult(issue(path5, `properties <= ${schema.maxProperties}`, value, `must NOT have more than ${schema.maxProperties} properties`)));
|
|
1078
1078
|
}
|
|
1079
1079
|
if (typeof schema.minProperties === "number" && keys.length < schema.minProperties) {
|
|
1080
|
-
results.push(invalidResult(issue(
|
|
1080
|
+
results.push(invalidResult(issue(path5, `properties >= ${schema.minProperties}`, value, `must NOT have fewer than ${schema.minProperties} properties`)));
|
|
1081
1081
|
}
|
|
1082
1082
|
if (Array.isArray(schema.required)) {
|
|
1083
1083
|
for (const key2 of schema.required) {
|
|
1084
1084
|
if (typeof key2 === "string" && !Object.prototype.hasOwnProperty.call(value, key2)) {
|
|
1085
|
-
results.push(invalidResult(issue([...
|
|
1085
|
+
results.push(invalidResult(issue([...path5, key2], "required", void 0, `must have required property '${key2}'`)));
|
|
1086
1086
|
}
|
|
1087
1087
|
}
|
|
1088
1088
|
}
|
|
1089
1089
|
const dependentRequired = isObject(schema.dependentRequired) ? schema.dependentRequired : {};
|
|
1090
1090
|
for (const [key2, dependencies] of Object.entries(dependentRequired)) {
|
|
1091
1091
|
if (Object.prototype.hasOwnProperty.call(value, key2) && Array.isArray(dependencies)) {
|
|
1092
|
-
addMissingDependencies(value, dependencies,
|
|
1092
|
+
addMissingDependencies(value, dependencies, path5, results);
|
|
1093
1093
|
}
|
|
1094
1094
|
}
|
|
1095
1095
|
if (node.dialect === "draft7" && isObject(schema.dependencies)) {
|
|
1096
1096
|
for (const [key2, dependencies] of Object.entries(schema.dependencies)) {
|
|
1097
1097
|
if (Object.prototype.hasOwnProperty.call(value, key2) && Array.isArray(dependencies)) {
|
|
1098
|
-
addMissingDependencies(value, dependencies,
|
|
1098
|
+
addMissingDependencies(value, dependencies, path5, results);
|
|
1099
1099
|
}
|
|
1100
1100
|
}
|
|
1101
1101
|
}
|
|
1102
1102
|
}
|
|
1103
|
-
function addMissingDependencies(value, dependencies,
|
|
1103
|
+
function addMissingDependencies(value, dependencies, path5, results) {
|
|
1104
1104
|
for (const dependency of dependencies) {
|
|
1105
1105
|
if (typeof dependency === "string" && !Object.prototype.hasOwnProperty.call(value, dependency)) {
|
|
1106
|
-
results.push(invalidResult(issue([...
|
|
1106
|
+
results.push(invalidResult(issue([...path5, dependency], "dependency", void 0, `must have property '${dependency}'`)));
|
|
1107
1107
|
}
|
|
1108
1108
|
}
|
|
1109
1109
|
}
|
|
@@ -3397,19 +3397,168 @@ var SubscriptionManager = class {
|
|
|
3397
3397
|
|
|
3398
3398
|
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
3399
3399
|
import crypto from "node:crypto";
|
|
3400
|
-
import
|
|
3400
|
+
import path4 from "node:path";
|
|
3401
3401
|
|
|
3402
3402
|
// ../auth-store/dist/encrypted-file-store.js
|
|
3403
|
-
import { createCipheriv, createDecipheriv, randomBytes, randomUUID, scrypt } from "node:crypto";
|
|
3403
|
+
import { createCipheriv, createDecipheriv, randomBytes, randomUUID as randomUUID2, scrypt } from "node:crypto";
|
|
3404
3404
|
import { promises as fs } from "node:fs";
|
|
3405
3405
|
import { homedir, hostname, userInfo } from "node:os";
|
|
3406
|
-
import
|
|
3406
|
+
import path2 from "node:path";
|
|
3407
3407
|
|
|
3408
3408
|
// ../auth-store/dist/error-codes.js
|
|
3409
3409
|
function hasOwnErrorCode(error, code) {
|
|
3410
3410
|
return error instanceof Error && Object.prototype.hasOwnProperty.call(error, "code") && error.code === code;
|
|
3411
3411
|
}
|
|
3412
3412
|
|
|
3413
|
+
// ../auth-store/dist/transaction-lock.js
|
|
3414
|
+
import { randomUUID } from "node:crypto";
|
|
3415
|
+
import path from "node:path";
|
|
3416
|
+
async function withSecretStoreFileLock(fs2, lockDirectory, operation, options = {}) {
|
|
3417
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
3418
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647)
|
|
3419
|
+
throw new Error("Invalid secret-store transaction lock timeout");
|
|
3420
|
+
options.signal?.throwIfAborted();
|
|
3421
|
+
const deadline = performance.now() + timeoutMs;
|
|
3422
|
+
const directory = path.resolve(lockDirectory);
|
|
3423
|
+
await assertLockDirectoryPath(fs2, directory);
|
|
3424
|
+
await fs2.mkdir(directory, { recursive: true, mode: 448 });
|
|
3425
|
+
await assertLockDirectoryPath(fs2, directory);
|
|
3426
|
+
const name = `${process.pid}-${randomUUID()}.claim`;
|
|
3427
|
+
const claimPath = path.join(directory, name);
|
|
3428
|
+
const temporaryPath = `${claimPath}.tmp`;
|
|
3429
|
+
let claimed = true;
|
|
3430
|
+
let temporaryCreated = false;
|
|
3431
|
+
let outcome;
|
|
3432
|
+
try {
|
|
3433
|
+
try {
|
|
3434
|
+
await fs2.writeFile(claimPath, JSON.stringify({ ticket: null }), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
3435
|
+
} catch (error) {
|
|
3436
|
+
if (hasOwnErrorCode(error, "EEXIST"))
|
|
3437
|
+
claimed = false;
|
|
3438
|
+
throw error;
|
|
3439
|
+
}
|
|
3440
|
+
const existing = await readClaims(fs2, directory, name);
|
|
3441
|
+
const ticket = existing.reduce((max, claim) => Math.max(max, claim.ticket ?? 0), 0) + 1;
|
|
3442
|
+
if (!Number.isSafeInteger(ticket))
|
|
3443
|
+
throw new Error("Secret-store transaction lock ticket overflow");
|
|
3444
|
+
temporaryCreated = true;
|
|
3445
|
+
try {
|
|
3446
|
+
await fs2.writeFile(temporaryPath, JSON.stringify({ ticket }), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
3447
|
+
} catch (error) {
|
|
3448
|
+
if (hasOwnErrorCode(error, "EEXIST"))
|
|
3449
|
+
temporaryCreated = false;
|
|
3450
|
+
throw error;
|
|
3451
|
+
}
|
|
3452
|
+
await fs2.rename(temporaryPath, claimPath);
|
|
3453
|
+
temporaryCreated = false;
|
|
3454
|
+
for (; ; ) {
|
|
3455
|
+
options.signal?.throwIfAborted();
|
|
3456
|
+
const peers = await readClaims(fs2, directory, name);
|
|
3457
|
+
if (!peers.some((peer) => peer.ticket === null || peer.ticket < ticket || peer.ticket === ticket && peer.name < name))
|
|
3458
|
+
break;
|
|
3459
|
+
const remaining = deadline - performance.now();
|
|
3460
|
+
if (remaining <= 0)
|
|
3461
|
+
throw new Error("Timed out waiting for secret-store transaction lock");
|
|
3462
|
+
await new Promise((resolve, reject) => {
|
|
3463
|
+
const abort = () => {
|
|
3464
|
+
clearTimeout(timer);
|
|
3465
|
+
options.signal?.removeEventListener("abort", abort);
|
|
3466
|
+
reject(options.signal?.reason);
|
|
3467
|
+
};
|
|
3468
|
+
const timer = setTimeout(() => {
|
|
3469
|
+
options.signal?.removeEventListener("abort", abort);
|
|
3470
|
+
resolve();
|
|
3471
|
+
}, Math.min(10, remaining));
|
|
3472
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
3473
|
+
if (options.signal?.aborted)
|
|
3474
|
+
abort();
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
options.signal?.throwIfAborted();
|
|
3478
|
+
outcome = { result: await operation() };
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
outcome = { error };
|
|
3481
|
+
}
|
|
3482
|
+
const cleanup = [];
|
|
3483
|
+
for (const target of [...temporaryCreated ? [temporaryPath] : [], ...claimed ? [claimPath] : []]) {
|
|
3484
|
+
try {
|
|
3485
|
+
await fs2.unlink(target);
|
|
3486
|
+
} catch (error) {
|
|
3487
|
+
if (!hasOwnErrorCode(error, "ENOENT"))
|
|
3488
|
+
cleanup.push(error);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
if (cleanup.length)
|
|
3492
|
+
throw new AggregateError([..."error" in outcome ? [outcome.error] : [], ...cleanup], "Secret-store transaction lock cleanup failed");
|
|
3493
|
+
if ("error" in outcome)
|
|
3494
|
+
throw outcome.error;
|
|
3495
|
+
return outcome.result;
|
|
3496
|
+
}
|
|
3497
|
+
async function assertNoSymbolicLink(fs2, target) {
|
|
3498
|
+
try {
|
|
3499
|
+
if ((await fs2.lstat(target)).isSymbolicLink())
|
|
3500
|
+
throw new Error("Refusing secret-store transaction lock through symbolic link");
|
|
3501
|
+
} catch (error) {
|
|
3502
|
+
if (!hasOwnErrorCode(error, "ENOENT"))
|
|
3503
|
+
throw error;
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
async function assertLockDirectoryPath(fs2, directory) {
|
|
3507
|
+
const root = path.parse(directory).root;
|
|
3508
|
+
const segments = directory.slice(root.length).split(path.sep).filter(Boolean);
|
|
3509
|
+
let current = root;
|
|
3510
|
+
for (const [index, segment] of segments.entries()) {
|
|
3511
|
+
current = path.join(current, segment);
|
|
3512
|
+
if (index > 0 || segments.length === 1)
|
|
3513
|
+
await assertNoSymbolicLink(fs2, current);
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
async function readClaims(fs2, directory, ownName) {
|
|
3517
|
+
const claims = [];
|
|
3518
|
+
for (const name of await fs2.readdir(directory)) {
|
|
3519
|
+
if (name === ownName || !name.endsWith(".claim"))
|
|
3520
|
+
continue;
|
|
3521
|
+
const pidText = name.slice(0, name.indexOf("-"));
|
|
3522
|
+
const pid = Number(pidText);
|
|
3523
|
+
if (!Number.isSafeInteger(pid) || pid < 1 || String(pid) !== pidText)
|
|
3524
|
+
throw new Error("Malformed secret-store transaction lock owner");
|
|
3525
|
+
const target = path.join(directory, name);
|
|
3526
|
+
await assertNoSymbolicLink(fs2, target);
|
|
3527
|
+
let alive = true;
|
|
3528
|
+
try {
|
|
3529
|
+
process.kill(pid, 0);
|
|
3530
|
+
} catch (error) {
|
|
3531
|
+
alive = !hasOwnErrorCode(error, "ESRCH");
|
|
3532
|
+
}
|
|
3533
|
+
if (!alive) {
|
|
3534
|
+
try {
|
|
3535
|
+
await fs2.unlink(target);
|
|
3536
|
+
} catch (error) {
|
|
3537
|
+
if (!hasOwnErrorCode(error, "ENOENT"))
|
|
3538
|
+
throw error;
|
|
3539
|
+
}
|
|
3540
|
+
continue;
|
|
3541
|
+
}
|
|
3542
|
+
let ticket = null;
|
|
3543
|
+
let raw;
|
|
3544
|
+
try {
|
|
3545
|
+
raw = await fs2.readFile(target, "utf8");
|
|
3546
|
+
} catch (error) {
|
|
3547
|
+
if (hasOwnErrorCode(error, "ENOENT"))
|
|
3548
|
+
continue;
|
|
3549
|
+
throw error;
|
|
3550
|
+
}
|
|
3551
|
+
try {
|
|
3552
|
+
const value = JSON.parse(raw);
|
|
3553
|
+
if (value !== null && typeof value === "object" && "ticket" in value && typeof value.ticket === "number" && Number.isSafeInteger(value.ticket) && value.ticket > 0)
|
|
3554
|
+
ticket = value.ticket;
|
|
3555
|
+
} catch {
|
|
3556
|
+
}
|
|
3557
|
+
claims.push({ name, ticket });
|
|
3558
|
+
}
|
|
3559
|
+
return claims;
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3413
3562
|
// ../auth-store/dist/encrypted-file-store.js
|
|
3414
3563
|
var derivedKeyCache = /* @__PURE__ */ new Map();
|
|
3415
3564
|
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
@@ -3426,6 +3575,7 @@ var EncryptedFileStore = class {
|
|
|
3426
3575
|
getMachineIdentity;
|
|
3427
3576
|
getRandomBytes;
|
|
3428
3577
|
keyPromise = null;
|
|
3578
|
+
throwOnInvalidDocument;
|
|
3429
3579
|
constructor(input) {
|
|
3430
3580
|
this.fs = input.fs ?? fs;
|
|
3431
3581
|
this.salt = input.salt;
|
|
@@ -3435,7 +3585,7 @@ var EncryptedFileStore = class {
|
|
|
3435
3585
|
const defaultFileName = input.defaultFileName ?? "credentials.enc";
|
|
3436
3586
|
assertSafeDefaultDirectory(defaultDirectory);
|
|
3437
3587
|
assertSafeDefaultFileName(defaultFileName);
|
|
3438
|
-
this.filePath =
|
|
3588
|
+
this.filePath = path2.join(homeDirectory, defaultDirectory, defaultFileName);
|
|
3439
3589
|
this.symbolicLinkCheckStartPath = resolveDefaultDirectoryCheckStart(homeDirectory, defaultDirectory);
|
|
3440
3590
|
} else {
|
|
3441
3591
|
this.filePath = input.filePath;
|
|
@@ -3443,6 +3593,7 @@ var EncryptedFileStore = class {
|
|
|
3443
3593
|
}
|
|
3444
3594
|
this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
|
|
3445
3595
|
this.getRandomBytes = input.getRandomBytes ?? randomBytes;
|
|
3596
|
+
this.throwOnInvalidDocument = input.throwOnInvalidDocument ?? false;
|
|
3446
3597
|
}
|
|
3447
3598
|
async get() {
|
|
3448
3599
|
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
@@ -3457,6 +3608,8 @@ var EncryptedFileStore = class {
|
|
|
3457
3608
|
}
|
|
3458
3609
|
const document = parseEncryptedDocument(rawDocument);
|
|
3459
3610
|
if (!document) {
|
|
3611
|
+
if (this.throwOnInvalidDocument)
|
|
3612
|
+
throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
|
|
3460
3613
|
return null;
|
|
3461
3614
|
}
|
|
3462
3615
|
const key2 = await this.getEncryptionKey();
|
|
@@ -3465,6 +3618,8 @@ var EncryptedFileStore = class {
|
|
|
3465
3618
|
const authTag = Buffer.from(document.authTag, "base64");
|
|
3466
3619
|
const ciphertext = Buffer.from(document.ciphertext, "base64");
|
|
3467
3620
|
if (iv.byteLength !== ENCRYPTION_IV_BYTES || authTag.byteLength !== ENCRYPTION_AUTH_TAG_BYTES) {
|
|
3621
|
+
if (this.throwOnInvalidDocument)
|
|
3622
|
+
throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
|
|
3468
3623
|
return null;
|
|
3469
3624
|
}
|
|
3470
3625
|
const decipher = createDecipheriv(ENCRYPTION_ALGORITHM, key2, iv);
|
|
@@ -3472,9 +3627,17 @@ var EncryptedFileStore = class {
|
|
|
3472
3627
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
3473
3628
|
return plaintext.toString("utf8");
|
|
3474
3629
|
} catch {
|
|
3630
|
+
if (this.throwOnInvalidDocument)
|
|
3631
|
+
throw new Error("Invalid encrypted credential document; reset the store explicitly to recover");
|
|
3475
3632
|
return null;
|
|
3476
3633
|
}
|
|
3477
3634
|
}
|
|
3635
|
+
async withLock(operation, options = {}) {
|
|
3636
|
+
await this.assertCredentialPathHasNoSymbolicLinks(`${this.filePath}.lock`);
|
|
3637
|
+
if (this.fs.readdir === void 0)
|
|
3638
|
+
throw new Error("Secret-store transaction locks require filesystem readdir support");
|
|
3639
|
+
return withSecretStoreFileLock(this.fs, `${this.filePath}.lock`, operation, options);
|
|
3640
|
+
}
|
|
3478
3641
|
async set(value) {
|
|
3479
3642
|
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
3480
3643
|
const key2 = await this.getEncryptionKey();
|
|
@@ -3491,9 +3654,9 @@ var EncryptedFileStore = class {
|
|
|
3491
3654
|
authTag: authTag.toString("base64"),
|
|
3492
3655
|
ciphertext: ciphertext.toString("base64")
|
|
3493
3656
|
};
|
|
3494
|
-
await this.fs.mkdir(
|
|
3657
|
+
await this.fs.mkdir(path2.dirname(this.filePath), { recursive: true });
|
|
3495
3658
|
await this.assertCredentialPathHasNoSymbolicLinks(this.filePath);
|
|
3496
|
-
const temporaryPath = `${this.filePath}.${process.pid}.${
|
|
3659
|
+
const temporaryPath = `${this.filePath}.${process.pid}.${randomUUID2()}.tmp`;
|
|
3497
3660
|
let temporaryCreated = false;
|
|
3498
3661
|
try {
|
|
3499
3662
|
await this.assertCredentialPathHasNoSymbolicLinks(temporaryPath);
|
|
@@ -3523,7 +3686,7 @@ var EncryptedFileStore = class {
|
|
|
3523
3686
|
}
|
|
3524
3687
|
}
|
|
3525
3688
|
async assertCredentialPathHasNoSymbolicLinks(targetPath) {
|
|
3526
|
-
const resolvedPath =
|
|
3689
|
+
const resolvedPath = path2.resolve(targetPath);
|
|
3527
3690
|
const protectedPaths = getProtectedCredentialPaths(resolvedPath, this.symbolicLinkCheckStartPath);
|
|
3528
3691
|
for (const currentPath of protectedPaths) {
|
|
3529
3692
|
try {
|
|
@@ -3554,26 +3717,26 @@ var EncryptedFileStore = class {
|
|
|
3554
3717
|
};
|
|
3555
3718
|
function resolveDefaultDirectoryCheckStart(homeDirectory, defaultDirectory) {
|
|
3556
3719
|
const [firstSegment] = defaultDirectory.split(/[\\/]+/).filter(Boolean);
|
|
3557
|
-
return
|
|
3720
|
+
return path2.resolve(homeDirectory, firstSegment ?? ".");
|
|
3558
3721
|
}
|
|
3559
3722
|
function getProtectedCredentialPaths(resolvedPath, symbolicLinkCheckStartPath) {
|
|
3560
3723
|
if (symbolicLinkCheckStartPath === null) {
|
|
3561
3724
|
return getExplicitProtectedCredentialPaths(resolvedPath);
|
|
3562
3725
|
}
|
|
3563
|
-
const resolvedStartPath =
|
|
3726
|
+
const resolvedStartPath = path2.resolve(symbolicLinkCheckStartPath);
|
|
3564
3727
|
if (!isPathInsideOrEqual(resolvedPath, resolvedStartPath)) {
|
|
3565
|
-
return [
|
|
3728
|
+
return [path2.dirname(resolvedPath), resolvedPath];
|
|
3566
3729
|
}
|
|
3567
3730
|
const protectedPaths = [resolvedStartPath];
|
|
3568
3731
|
let currentPath = resolvedStartPath;
|
|
3569
|
-
for (const segment of
|
|
3570
|
-
currentPath =
|
|
3732
|
+
for (const segment of path2.relative(resolvedStartPath, resolvedPath).split(path2.sep).filter(Boolean)) {
|
|
3733
|
+
currentPath = path2.join(currentPath, segment);
|
|
3571
3734
|
protectedPaths.push(currentPath);
|
|
3572
3735
|
}
|
|
3573
3736
|
return protectedPaths;
|
|
3574
3737
|
}
|
|
3575
3738
|
function assertSafeDefaultDirectory(defaultDirectory) {
|
|
3576
|
-
if (
|
|
3739
|
+
if (path2.isAbsolute(defaultDirectory) || path2.win32.isAbsolute(defaultDirectory)) {
|
|
3577
3740
|
throw new Error("defaultDirectory must be a relative path inside the home directory");
|
|
3578
3741
|
}
|
|
3579
3742
|
for (const segment of splitPathSegments(defaultDirectory)) {
|
|
@@ -3591,15 +3754,15 @@ function splitPathSegments(value) {
|
|
|
3591
3754
|
return value.split("/").flatMap((segment) => segment.split("\\")).filter((segment) => segment.length > 0);
|
|
3592
3755
|
}
|
|
3593
3756
|
function getExplicitProtectedCredentialPaths(resolvedPath) {
|
|
3594
|
-
const parsed =
|
|
3595
|
-
const segments = resolvedPath.slice(parsed.root.length).split(
|
|
3757
|
+
const parsed = path2.parse(resolvedPath);
|
|
3758
|
+
const segments = resolvedPath.slice(parsed.root.length).split(path2.sep).filter((segment) => segment.length > 0);
|
|
3596
3759
|
if (segments.length <= 1) {
|
|
3597
3760
|
return [resolvedPath];
|
|
3598
3761
|
}
|
|
3599
3762
|
const protectedPaths = [];
|
|
3600
3763
|
let currentPath = parsed.root;
|
|
3601
3764
|
for (const [index, segment] of segments.entries()) {
|
|
3602
|
-
currentPath =
|
|
3765
|
+
currentPath = path2.join(currentPath, segment);
|
|
3603
3766
|
if (index === 0) {
|
|
3604
3767
|
continue;
|
|
3605
3768
|
}
|
|
@@ -3608,8 +3771,8 @@ function getExplicitProtectedCredentialPaths(resolvedPath) {
|
|
|
3608
3771
|
return protectedPaths;
|
|
3609
3772
|
}
|
|
3610
3773
|
function isPathInsideOrEqual(childPath, parentPath) {
|
|
3611
|
-
const relativePath =
|
|
3612
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
3774
|
+
const relativePath = path2.relative(parentPath, childPath);
|
|
3775
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path2.isAbsolute(relativePath);
|
|
3613
3776
|
}
|
|
3614
3777
|
async function removeIfPresent(fileSystem, filePath) {
|
|
3615
3778
|
try {
|
|
@@ -3692,16 +3855,24 @@ function isAlreadyExistsError(error) {
|
|
|
3692
3855
|
|
|
3693
3856
|
// ../auth-store/dist/keychain-store.js
|
|
3694
3857
|
import { spawn } from "node:child_process";
|
|
3858
|
+
import { createHash } from "node:crypto";
|
|
3859
|
+
import { promises as nodeFs } from "node:fs";
|
|
3860
|
+
import { homedir as homedir2 } from "node:os";
|
|
3861
|
+
import path3 from "node:path";
|
|
3695
3862
|
var SECURITY_CLI = "security";
|
|
3696
3863
|
var KEYCHAIN_ITEM_NOT_FOUND_EXIT_CODE = 44;
|
|
3697
3864
|
var KeychainStore = class {
|
|
3698
3865
|
runCommand;
|
|
3699
3866
|
service;
|
|
3700
3867
|
account;
|
|
3868
|
+
lockFs;
|
|
3869
|
+
lockDirectory;
|
|
3701
3870
|
constructor(input) {
|
|
3702
3871
|
this.runCommand = input.runCommand ?? runSecurityCommand;
|
|
3703
3872
|
this.service = input.service.trim();
|
|
3704
3873
|
this.account = input.account.trim();
|
|
3874
|
+
this.lockFs = input.lock?.fs ?? nodeFs;
|
|
3875
|
+
this.lockDirectory = input.lock?.directory ?? path3.join(homedir2(), ".auth-store", "keychain-locks");
|
|
3705
3876
|
if (this.service.length === 0) {
|
|
3706
3877
|
throw new Error("Keychain service must not be empty");
|
|
3707
3878
|
}
|
|
@@ -3719,6 +3890,10 @@ var KeychainStore = class {
|
|
|
3719
3890
|
}
|
|
3720
3891
|
throw createSecurityCliFailure("read secret from macOS Keychain", result);
|
|
3721
3892
|
}
|
|
3893
|
+
async withLock(operation, options = {}) {
|
|
3894
|
+
const identity = createHash("sha256").update(JSON.stringify([this.service, this.account])).digest("hex");
|
|
3895
|
+
return withSecretStoreFileLock(this.lockFs, path3.join(this.lockDirectory, identity), operation, options);
|
|
3896
|
+
}
|
|
3722
3897
|
async set(value) {
|
|
3723
3898
|
if (value.includes("\n") || value.includes("\r")) {
|
|
3724
3899
|
throw new Error("Keychain secrets cannot contain line breaks");
|
|
@@ -3904,13 +4079,24 @@ var DEFAULT_CLIENT_KEYCHAIN_SERVICE = "poe-code-mcp-oauth-clients";
|
|
|
3904
4079
|
var MAX_JS_DATE_MS = 864e13;
|
|
3905
4080
|
function createAuthStoreSessionStore(options = {}) {
|
|
3906
4081
|
return {
|
|
4082
|
+
async withLock(resource, operation, lockOptions) {
|
|
4083
|
+
const store = createResourceSecretStore(resource, options);
|
|
4084
|
+
if (store.withLock === void 0)
|
|
4085
|
+
throw new Error("OAuth secret-store backend does not support transaction locks");
|
|
4086
|
+
return store.withLock(operation, lockOptions);
|
|
4087
|
+
},
|
|
3907
4088
|
async load(resource) {
|
|
3908
4089
|
const store = createResourceSecretStore(resource, options);
|
|
3909
4090
|
const value = await store.get();
|
|
3910
4091
|
if (value === null) {
|
|
3911
4092
|
return null;
|
|
3912
4093
|
}
|
|
3913
|
-
|
|
4094
|
+
let parsed;
|
|
4095
|
+
try {
|
|
4096
|
+
parsed = JSON.parse(value);
|
|
4097
|
+
} catch {
|
|
4098
|
+
throw new Error("Stored OAuth session must be valid JSON; reset the store explicitly to recover");
|
|
4099
|
+
}
|
|
3914
4100
|
if (isStoredOAuthSession(parsed)) {
|
|
3915
4101
|
return parsed;
|
|
3916
4102
|
}
|
|
@@ -3934,7 +4120,12 @@ function createAuthStoreClientStore(options) {
|
|
|
3934
4120
|
if (value === null) {
|
|
3935
4121
|
return null;
|
|
3936
4122
|
}
|
|
3937
|
-
|
|
4123
|
+
let parsed;
|
|
4124
|
+
try {
|
|
4125
|
+
parsed = JSON.parse(value);
|
|
4126
|
+
} catch {
|
|
4127
|
+
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4128
|
+
}
|
|
3938
4129
|
const clientId = isObjectRecord(parsed) ? getOwnString(parsed, "clientId") : void 0;
|
|
3939
4130
|
if (clientId !== void 0) {
|
|
3940
4131
|
const client = { clientId };
|
|
@@ -3958,10 +4149,11 @@ function createAuthStoreClientStore(options) {
|
|
|
3958
4149
|
function createNamedSecretStore(key2, options, defaults) {
|
|
3959
4150
|
const hash = crypto.createHash("sha256").update(key2).digest("hex");
|
|
3960
4151
|
const configuredFilePath = options.fileStore?.filePath;
|
|
3961
|
-
const parsedFilePath = configuredFilePath === void 0 ? null :
|
|
4152
|
+
const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
|
|
3962
4153
|
const fileStore = {
|
|
3963
4154
|
...options.fileStore,
|
|
3964
|
-
|
|
4155
|
+
throwOnInvalidDocument: true,
|
|
4156
|
+
filePath: parsedFilePath === null ? void 0 : path4.join(parsedFilePath.dir, `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`),
|
|
3965
4157
|
salt: options.fileStore?.salt ?? defaults.salt,
|
|
3966
4158
|
defaultDirectory: options.fileStore?.defaultDirectory || defaults.directory,
|
|
3967
4159
|
defaultFileName: parsedFilePath === null ? `${hash}.enc` : `${parsedFilePath.name}-${hash}${parsedFilePath.ext || ".enc"}`
|
|
@@ -4003,7 +4195,7 @@ function isStoredOAuthSession(value) {
|
|
|
4003
4195
|
if (!isObjectRecord(value)) {
|
|
4004
4196
|
return false;
|
|
4005
4197
|
}
|
|
4006
|
-
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && isStoredOAuthClient(getOwnEntry3(value, "client")) && isStoredOAuthDiscovery(getOwnEntry3(value, "discovery")) && isStoredOAuthTokensOrMissing(getOwnEntry3(value, "tokens"));
|
|
4198
|
+
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && isStoredOAuthClient(getOwnEntry3(value, "client")) && isStoredOAuthDiscovery(getOwnEntry3(value, "discovery")) && (getOwnEntry3(value, "refreshState") === void 0 || getOwnEntry3(value, "refreshState") === "pending" && getOwnEntry3(value, "tokens") === void 0) && isStoredOAuthTokensOrMissing(getOwnEntry3(value, "tokens"));
|
|
4007
4199
|
}
|
|
4008
4200
|
function isStoredOAuthClient(value) {
|
|
4009
4201
|
if (!isObjectRecord(value) || !isNonBlankOwnString(value, "clientId")) {
|
|
@@ -4432,7 +4624,9 @@ var OAuthError = class extends Error {
|
|
|
4432
4624
|
status;
|
|
4433
4625
|
retryable;
|
|
4434
4626
|
terminal;
|
|
4435
|
-
|
|
4627
|
+
/** True only when a complete OAuth error response establishes rejection. */
|
|
4628
|
+
outcomeKnown;
|
|
4629
|
+
constructor(shape, status, outcomeKnown = true) {
|
|
4436
4630
|
super(shape.error_description ?? shape.error);
|
|
4437
4631
|
this.name = "OAuthError";
|
|
4438
4632
|
this.error = shape.error;
|
|
@@ -4443,6 +4637,7 @@ var OAuthError = class extends Error {
|
|
|
4443
4637
|
this.status = status;
|
|
4444
4638
|
this.retryable = isRetryableOAuthError(this);
|
|
4445
4639
|
this.terminal = !this.retryable;
|
|
4640
|
+
this.outcomeKnown = outcomeKnown;
|
|
4446
4641
|
}
|
|
4447
4642
|
};
|
|
4448
4643
|
function isRetryableOAuthError(error) {
|
|
@@ -4557,7 +4752,8 @@ async function readOAuthJsonObjectResponse(response, signal) {
|
|
|
4557
4752
|
}
|
|
4558
4753
|
const record2 = payload;
|
|
4559
4754
|
if (!response.ok) {
|
|
4560
|
-
|
|
4755
|
+
const error = getOwnEntry5(record2, "error");
|
|
4756
|
+
throw new OAuthError(readOAuthError(record2, fallbackError.error), response.status, typeof error === "string" && error.trim().length > 0);
|
|
4561
4757
|
}
|
|
4562
4758
|
return record2;
|
|
4563
4759
|
}
|
|
@@ -4576,7 +4772,7 @@ function getOwnEntry5(record2, key2) {
|
|
|
4576
4772
|
}
|
|
4577
4773
|
function createFallbackOAuthError(status) {
|
|
4578
4774
|
const error = status === 503 ? "temporarily_unavailable" : "server_error";
|
|
4579
|
-
return new OAuthError({ error }, status);
|
|
4775
|
+
return new OAuthError({ error }, status, false);
|
|
4580
4776
|
}
|
|
4581
4777
|
function normalizeBearerTokenType(value) {
|
|
4582
4778
|
if (typeof value !== "string") {
|
|
@@ -4585,6 +4781,55 @@ function normalizeBearerTokenType(value) {
|
|
|
4585
4781
|
return value.toLowerCase() === "bearer" ? "Bearer" : null;
|
|
4586
4782
|
}
|
|
4587
4783
|
|
|
4784
|
+
// ../mcp-oauth/dist/client/session-transaction.js
|
|
4785
|
+
var queues = /* @__PURE__ */ new WeakMap();
|
|
4786
|
+
async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
|
|
4787
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
4788
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
4789
|
+
throw new Error("sessionLockTimeoutMs must be an integer from 1 to 2147483647 milliseconds");
|
|
4790
|
+
options.signal?.throwIfAborted();
|
|
4791
|
+
const started = performance.now();
|
|
4792
|
+
const pending = queues.get(store) ?? /* @__PURE__ */ new Map();
|
|
4793
|
+
queues.set(store, pending);
|
|
4794
|
+
const previous = pending.get(resource) ?? Promise.resolve();
|
|
4795
|
+
let release;
|
|
4796
|
+
const current = new Promise((resolve) => {
|
|
4797
|
+
release = resolve;
|
|
4798
|
+
});
|
|
4799
|
+
const tail = previous.then(() => current);
|
|
4800
|
+
pending.set(resource, tail);
|
|
4801
|
+
try {
|
|
4802
|
+
let timer;
|
|
4803
|
+
let rejectWait;
|
|
4804
|
+
const waiting = new Promise((resolve, reject) => {
|
|
4805
|
+
rejectWait = reject;
|
|
4806
|
+
previous.then(resolve, reject);
|
|
4807
|
+
});
|
|
4808
|
+
const abort = () => rejectWait(options.signal?.reason);
|
|
4809
|
+
try {
|
|
4810
|
+
timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
|
|
4811
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
4812
|
+
if (options.signal?.aborted)
|
|
4813
|
+
abort();
|
|
4814
|
+
await waiting;
|
|
4815
|
+
} finally {
|
|
4816
|
+
clearTimeout(timer);
|
|
4817
|
+
options.signal?.removeEventListener("abort", abort);
|
|
4818
|
+
}
|
|
4819
|
+
options.signal?.throwIfAborted();
|
|
4820
|
+
return store.withLock === void 0 ? await operation() : await store.withLock(resource, operation, {
|
|
4821
|
+
signal: options.signal,
|
|
4822
|
+
timeoutMs: Math.max(0, timeoutMs - (performance.now() - started))
|
|
4823
|
+
});
|
|
4824
|
+
} finally {
|
|
4825
|
+
release();
|
|
4826
|
+
void tail.then(() => {
|
|
4827
|
+
if (pending.get(resource) === tail)
|
|
4828
|
+
pending.delete(resource);
|
|
4829
|
+
});
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4588
4833
|
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
4589
4834
|
var MAX_JS_DATE_MS3 = 864e13;
|
|
4590
4835
|
function createOAuthClientProvider(options) {
|
|
@@ -4599,8 +4844,6 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4599
4844
|
const clientStore = options.authStore === void 0 ? null : createAuthStoreClientStore(options.authStore);
|
|
4600
4845
|
const now = options.now ?? Date.now;
|
|
4601
4846
|
const registeredClients = /* @__PURE__ */ new Map();
|
|
4602
|
-
const refreshPromises = /* @__PURE__ */ new Map();
|
|
4603
|
-
const authorizationPromises = /* @__PURE__ */ new Map();
|
|
4604
4847
|
if (options.initialGrant !== void 0) {
|
|
4605
4848
|
let resource;
|
|
4606
4849
|
try {
|
|
@@ -4687,203 +4930,193 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4687
4930
|
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal, rejectedTokens) {
|
|
4688
4931
|
signal?.throwIfAborted();
|
|
4689
4932
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
if (discovery !== void 0 && getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
|
|
4695
|
-
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
4696
|
-
}
|
|
4697
|
-
if (session !== null && (canonicalizeResourceIndicator(session.resource) !== canonicalResource || getOwnString2(session.discovery.authorizationServerMetadata, "issuer") !== session.authorizationServer || discovery !== void 0 && discovery.authorizationServer !== session.authorizationServer)) {
|
|
4698
|
-
await clearSession(canonicalResource);
|
|
4699
|
-
session = null;
|
|
4700
|
-
}
|
|
4701
|
-
if (session === null && discovery !== void 0 && !initialGrantConsumed && initialGrant?.resource === canonicalResource && initialGrant.tokens !== void 0 && initialGrant.client !== null) {
|
|
4702
|
-
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
4703
|
-
session = {
|
|
4704
|
-
resource: canonicalResource,
|
|
4705
|
-
authorizationServer: discovery.authorizationServer,
|
|
4706
|
-
client: initialGrant.client,
|
|
4707
|
-
tokens: initialGrant.tokens,
|
|
4708
|
-
discovery: toStoredDiscovery(discovery)
|
|
4709
|
-
};
|
|
4710
|
-
await saveSession(canonicalResource, session);
|
|
4711
|
-
initialGrantConsumed = true;
|
|
4933
|
+
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
4934
|
+
let session = await loadSession(canonicalResource);
|
|
4935
|
+
if (session !== null && initialGrant?.resource === canonicalResource)
|
|
4936
|
+
initialGrantConsumed = true;
|
|
4712
4937
|
signal?.throwIfAborted();
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4938
|
+
if (discovery !== void 0 && getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
|
|
4939
|
+
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
4940
|
+
}
|
|
4941
|
+
if (session !== null && (canonicalizeResourceIndicator(session.resource) !== canonicalResource || getOwnString2(session.discovery.authorizationServerMetadata, "issuer") !== session.authorizationServer || discovery !== void 0 && discovery.authorizationServer !== session.authorizationServer)) {
|
|
4942
|
+
await clearSession(canonicalResource);
|
|
4943
|
+
session = null;
|
|
4944
|
+
}
|
|
4945
|
+
if (session === null && discovery !== void 0 && !initialGrantConsumed && initialGrant?.resource === canonicalResource && initialGrant.tokens !== void 0 && initialGrant.client !== null) {
|
|
4946
|
+
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
4947
|
+
session = {
|
|
4948
|
+
resource: canonicalResource,
|
|
4949
|
+
authorizationServer: discovery.authorizationServer,
|
|
4950
|
+
client: initialGrant.client,
|
|
4951
|
+
tokens: initialGrant.tokens,
|
|
4952
|
+
discovery: toStoredDiscovery(discovery)
|
|
4953
|
+
};
|
|
4954
|
+
await saveSession(canonicalResource, session);
|
|
4955
|
+
initialGrantConsumed = true;
|
|
4956
|
+
signal?.throwIfAborted();
|
|
4957
|
+
}
|
|
4958
|
+
if (forceRefresh && rejectedTokens !== void 0 && (rejectedTokens === null || session?.tokens === void 0 || !sameTokenGrant(session.tokens, rejectedTokens)))
|
|
4959
|
+
forceRefresh = false;
|
|
4960
|
+
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
4961
|
+
if (session?.refreshState === "pending") {
|
|
4962
|
+
if (!allowInteractive || options.allowInteractive === false || sessionDiscovery === void 0)
|
|
4963
|
+
throw new Error("OAuth refresh outcome is unknown; authorize again before using this resource");
|
|
4964
|
+
return authorizeSession(canonicalResource, clearSessionTokens(session), sessionDiscovery, fetch2, signal);
|
|
4965
|
+
}
|
|
4966
|
+
if (session?.tokens !== void 0 && !forceRefresh && !isExpired(session.tokens, now)) {
|
|
4723
4967
|
return session;
|
|
4724
4968
|
}
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4969
|
+
if (session?.tokens?.refreshToken !== void 0 && sessionDiscovery !== void 0 && (forceRefresh || isExpired(session.tokens, now))) {
|
|
4970
|
+
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
|
|
4971
|
+
if (session?.tokens !== void 0 && !isExpired(session.tokens, now)) {
|
|
4972
|
+
return session;
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
if (forceRefresh && session?.tokens !== void 0) {
|
|
4976
|
+
session = clearSessionTokens(session);
|
|
4977
|
+
await saveSession(canonicalResource, session);
|
|
4978
|
+
}
|
|
4979
|
+
if (!allowInteractive || sessionDiscovery === void 0) {
|
|
4980
|
+
return session;
|
|
4981
|
+
}
|
|
4982
|
+
if (options.allowInteractive === false)
|
|
4983
|
+
throw new Error("OAuth interactive authorization is disabled");
|
|
4984
|
+
return authorizeSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
|
|
4985
|
+
}, { signal, timeoutMs: options.sessionLockTimeoutMs });
|
|
4736
4986
|
}
|
|
4737
4987
|
async function refreshSession(resource, session, discovery, fetch2, signal) {
|
|
4738
4988
|
signal?.throwIfAborted();
|
|
4739
4989
|
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
return inFlight;
|
|
4990
|
+
if (session.tokens?.refreshToken === void 0) {
|
|
4991
|
+
return session;
|
|
4743
4992
|
}
|
|
4744
|
-
const
|
|
4993
|
+
const pendingSession = { ...clearSessionTokens(session), refreshState: "pending" };
|
|
4994
|
+
await saveSession(resource, pendingSession);
|
|
4995
|
+
signal?.throwIfAborted();
|
|
4996
|
+
let refreshAttempted = false;
|
|
4997
|
+
let refreshedTokens;
|
|
4998
|
+
while (true) {
|
|
4745
4999
|
try {
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
return null;
|
|
4775
|
-
}
|
|
4776
|
-
if (!refreshAttempted && isRetryableOAuthError(error)) {
|
|
4777
|
-
refreshAttempted = true;
|
|
4778
|
-
continue;
|
|
4779
|
-
}
|
|
4780
|
-
throw error;
|
|
4781
|
-
}
|
|
5000
|
+
refreshedTokens = await refreshAccessToken({
|
|
5001
|
+
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
5002
|
+
clientId: session.client.clientId,
|
|
5003
|
+
clientSecret: session.client.clientSecret,
|
|
5004
|
+
refreshToken: session.tokens.refreshToken,
|
|
5005
|
+
resource,
|
|
5006
|
+
fetch: fetch2,
|
|
5007
|
+
signal,
|
|
5008
|
+
now
|
|
5009
|
+
});
|
|
5010
|
+
break;
|
|
5011
|
+
} catch (error) {
|
|
5012
|
+
signal?.throwIfAborted();
|
|
5013
|
+
if (!(error instanceof OAuthError) || !error.outcomeKnown)
|
|
5014
|
+
throw error;
|
|
5015
|
+
if (error.error === "invalid_grant") {
|
|
5016
|
+
const clearedSession = clearSessionTokens(session);
|
|
5017
|
+
await saveSession(resource, clearedSession);
|
|
5018
|
+
return clearedSession;
|
|
5019
|
+
}
|
|
5020
|
+
if (shouldReRegisterStoredDynamicClient(error, await loadRegisteredClient(discovery.authorizationServer), false)) {
|
|
5021
|
+
await clearRegisteredClient(discovery.authorizationServer);
|
|
5022
|
+
await clearSession(resource);
|
|
5023
|
+
return null;
|
|
5024
|
+
}
|
|
5025
|
+
if (!refreshAttempted && isRetryableOAuthError(error)) {
|
|
5026
|
+
refreshAttempted = true;
|
|
5027
|
+
continue;
|
|
4782
5028
|
}
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
tokens: {
|
|
4786
|
-
...refreshedTokens,
|
|
4787
|
-
refreshToken: refreshedTokens.refreshToken ?? session.tokens.refreshToken
|
|
4788
|
-
},
|
|
4789
|
-
discovery: toStoredDiscovery(discovery)
|
|
4790
|
-
};
|
|
4791
|
-
await saveSession(resource, updatedSession);
|
|
4792
|
-
return updatedSession;
|
|
4793
|
-
} finally {
|
|
4794
|
-
refreshPromises.delete(resource);
|
|
5029
|
+
await saveSession(resource, session);
|
|
5030
|
+
throw error;
|
|
4795
5031
|
}
|
|
4796
|
-
}
|
|
4797
|
-
|
|
4798
|
-
|
|
5032
|
+
}
|
|
5033
|
+
const updatedSession = {
|
|
5034
|
+
...session,
|
|
5035
|
+
tokens: {
|
|
5036
|
+
...refreshedTokens,
|
|
5037
|
+
refreshToken: refreshedTokens.refreshToken ?? session.tokens.refreshToken
|
|
5038
|
+
},
|
|
5039
|
+
discovery: toStoredDiscovery(discovery)
|
|
5040
|
+
};
|
|
5041
|
+
await saveSession(resource, updatedSession);
|
|
5042
|
+
return updatedSession;
|
|
4799
5043
|
}
|
|
4800
5044
|
async function authorizeSession(resource, existingSession, discovery, fetch2, signal) {
|
|
4801
5045
|
signal?.throwIfAborted();
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
5046
|
+
assertS256PkceSupport(discovery.authorizationServerMetadata);
|
|
5047
|
+
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
5048
|
+
let currentSession = existingSession;
|
|
5049
|
+
let transientRetryAttempted = false;
|
|
5050
|
+
let reRegistrationAttempted = false;
|
|
5051
|
+
while (true) {
|
|
5052
|
+
const loopback = await createLoopbackAuthorizationSession({
|
|
5053
|
+
openBrowser: options.browser.openBrowser,
|
|
5054
|
+
readLine: options.browser.readLine,
|
|
5055
|
+
createServer: options.browser.createServer,
|
|
5056
|
+
landingPage: options.browser.landingPage,
|
|
5057
|
+
redirectUri: options.browser.redirectUri,
|
|
5058
|
+
signal: options.browser.signal === void 0 ? signal : signal === void 0 ? options.browser.signal : AbortSignal.any([signal, options.browser.signal]),
|
|
5059
|
+
timeoutMs: options.browser.timeoutMs
|
|
5060
|
+
});
|
|
5061
|
+
let resolvedClient = null;
|
|
5062
|
+
try {
|
|
5063
|
+
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2, signal);
|
|
5064
|
+
const sessionWithoutTokens = {
|
|
5065
|
+
resource,
|
|
5066
|
+
authorizationServer: discovery.authorizationServer,
|
|
5067
|
+
client: resolvedClient.client,
|
|
5068
|
+
discovery: toStoredDiscovery(discovery)
|
|
5069
|
+
};
|
|
5070
|
+
await saveSession(resource, sessionWithoutTokens);
|
|
5071
|
+
const verifier = generateCodeVerifier();
|
|
5072
|
+
const challenge = generateCodeChallenge(verifier);
|
|
5073
|
+
const authorizationUrl = buildAuthorizationUrl({
|
|
5074
|
+
metadata: discovery.authorizationServerMetadata,
|
|
5075
|
+
resource,
|
|
5076
|
+
clientId: resolvedClient.client.clientId,
|
|
5077
|
+
redirectUri: loopback.redirectUri,
|
|
5078
|
+
codeChallenge: challenge,
|
|
5079
|
+
clientMetadata: getClientMetadata(options.client)
|
|
4821
5080
|
});
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
const session = {
|
|
4856
|
-
...sessionWithoutTokens,
|
|
4857
|
-
tokens
|
|
4858
|
-
};
|
|
4859
|
-
await saveSession(resource, session);
|
|
4860
|
-
return session;
|
|
4861
|
-
} catch (error) {
|
|
4862
|
-
signal?.throwIfAborted();
|
|
4863
|
-
if (shouldReRegisterStoredDynamicClient(error, resolvedClient, reRegistrationAttempted)) {
|
|
4864
|
-
reRegistrationAttempted = true;
|
|
4865
|
-
await clearRegisteredClient(discovery.authorizationServer);
|
|
4866
|
-
await clearSession(resource);
|
|
4867
|
-
currentSession = null;
|
|
4868
|
-
continue;
|
|
4869
|
-
}
|
|
4870
|
-
if (!transientRetryAttempted && isRetryableOAuthError(error)) {
|
|
4871
|
-
transientRetryAttempted = true;
|
|
4872
|
-
await clearSession(resource);
|
|
4873
|
-
currentSession = null;
|
|
4874
|
-
continue;
|
|
4875
|
-
}
|
|
4876
|
-
throw error;
|
|
4877
|
-
} finally {
|
|
4878
|
-
loopback.close();
|
|
5081
|
+
const code = await loopback.waitForCode(authorizationUrl);
|
|
5082
|
+
const tokens = await exchangeAuthorizationCode({
|
|
5083
|
+
tokenEndpoint: requireOwnString(discovery.authorizationServerMetadata, "token_endpoint", "Authorization server metadata"),
|
|
5084
|
+
clientId: resolvedClient.client.clientId,
|
|
5085
|
+
clientSecret: resolvedClient.client.clientSecret,
|
|
5086
|
+
code,
|
|
5087
|
+
codeVerifier: verifier,
|
|
5088
|
+
redirectUri: loopback.redirectUri,
|
|
5089
|
+
resource,
|
|
5090
|
+
fetch: fetch2,
|
|
5091
|
+
signal,
|
|
5092
|
+
now
|
|
5093
|
+
});
|
|
5094
|
+
const session = {
|
|
5095
|
+
...sessionWithoutTokens,
|
|
5096
|
+
tokens
|
|
5097
|
+
};
|
|
5098
|
+
await saveSession(resource, session);
|
|
5099
|
+
return session;
|
|
5100
|
+
} catch (error) {
|
|
5101
|
+
signal?.throwIfAborted();
|
|
5102
|
+
if (shouldReRegisterStoredDynamicClient(error, resolvedClient, reRegistrationAttempted)) {
|
|
5103
|
+
reRegistrationAttempted = true;
|
|
5104
|
+
await clearRegisteredClient(discovery.authorizationServer);
|
|
5105
|
+
await clearSession(resource);
|
|
5106
|
+
currentSession = null;
|
|
5107
|
+
continue;
|
|
5108
|
+
}
|
|
5109
|
+
if (!transientRetryAttempted && isRetryableOAuthError(error)) {
|
|
5110
|
+
transientRetryAttempted = true;
|
|
5111
|
+
await clearSession(resource);
|
|
5112
|
+
currentSession = null;
|
|
5113
|
+
continue;
|
|
4879
5114
|
}
|
|
5115
|
+
throw error;
|
|
5116
|
+
} finally {
|
|
5117
|
+
loopback.close();
|
|
4880
5118
|
}
|
|
4881
|
-
}
|
|
4882
|
-
const finalPromise = promise.finally(() => {
|
|
4883
|
-
authorizationPromises.delete(resource);
|
|
4884
|
-
});
|
|
4885
|
-
authorizationPromises.set(resource, finalPromise);
|
|
4886
|
-
return finalPromise;
|
|
5119
|
+
}
|
|
4887
5120
|
}
|
|
4888
5121
|
async function resolveClient(existingSession, discovery, redirectUri, fetch2, parentSignal) {
|
|
4889
5122
|
parentSignal?.throwIfAborted();
|
|
@@ -5040,6 +5273,7 @@ function sameTokenGrant(left, right) {
|
|
|
5040
5273
|
function clearSessionTokens(session) {
|
|
5041
5274
|
const nextSession = { ...session };
|
|
5042
5275
|
delete nextSession.tokens;
|
|
5276
|
+
delete nextSession.refreshState;
|
|
5043
5277
|
return nextSession;
|
|
5044
5278
|
}
|
|
5045
5279
|
function hasCachedAccessToken(session) {
|
|
@@ -5049,6 +5283,9 @@ function normalizeLoadedSession(session) {
|
|
|
5049
5283
|
if (session === null) {
|
|
5050
5284
|
return null;
|
|
5051
5285
|
}
|
|
5286
|
+
const refreshState = getOwnEntry6(session, "refreshState");
|
|
5287
|
+
if (refreshState !== void 0 && (refreshState !== "pending" || getOwnEntry6(session, "tokens") !== void 0))
|
|
5288
|
+
throw new Error("Stored OAuth refresh state is invalid");
|
|
5052
5289
|
const client = normalizeStoredClient(getOwnEntry6(session, "client"));
|
|
5053
5290
|
if (client === null) {
|
|
5054
5291
|
return { ...session, client: { clientId: "" }, tokens: void 0 };
|
|
@@ -5321,7 +5558,7 @@ function getParameterHeaders(schema) {
|
|
|
5321
5558
|
const names = /* @__PURE__ */ new Set();
|
|
5322
5559
|
const ancestors = /* @__PURE__ */ new Set();
|
|
5323
5560
|
let nodes = 0;
|
|
5324
|
-
const visit = (value,
|
|
5561
|
+
const visit = (value, path5, reachable, depth) => {
|
|
5325
5562
|
if (++nodes > 1e4 || depth > 64)
|
|
5326
5563
|
throw new Error("MCP header schema traversal limit exceeded");
|
|
5327
5564
|
if (typeof value === "boolean")
|
|
@@ -5332,7 +5569,7 @@ function getParameterHeaders(schema) {
|
|
|
5332
5569
|
try {
|
|
5333
5570
|
if (Object.prototype.hasOwnProperty.call(value, "x-mcp-header")) {
|
|
5334
5571
|
const name = value["x-mcp-header"];
|
|
5335
|
-
if (!reachable ||
|
|
5572
|
+
if (!reachable || path5.length === 0 || typeof name !== "string" || name.length === 0 || [...name].some((character) => {
|
|
5336
5573
|
const code = character.charCodeAt(0);
|
|
5337
5574
|
return !tokenPunctuation.has(character) && !(code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122);
|
|
5338
5575
|
}))
|
|
@@ -5343,25 +5580,25 @@ function getParameterHeaders(schema) {
|
|
|
5343
5580
|
if (names.has(key2))
|
|
5344
5581
|
throw new Error("Duplicate x-mcp-header name");
|
|
5345
5582
|
names.add(key2);
|
|
5346
|
-
headers.push({ name: `Mcp-Param-${name}`, path: [...
|
|
5583
|
+
headers.push({ name: `Mcp-Param-${name}`, path: [...path5], type: value.type });
|
|
5347
5584
|
}
|
|
5348
5585
|
for (const [keyword, child] of Object.entries(value)) {
|
|
5349
5586
|
if (schemaMaps.has(keyword)) {
|
|
5350
5587
|
if (!isRecord3(child))
|
|
5351
5588
|
throw new Error("Invalid schema map");
|
|
5352
5589
|
for (const [key2, nested] of Object.entries(child))
|
|
5353
|
-
visit(nested, keyword === "properties" ? [...
|
|
5590
|
+
visit(nested, keyword === "properties" ? [...path5, key2] : path5, reachable && keyword === "properties", depth + 1);
|
|
5354
5591
|
} else if (schemaArrays.has(keyword)) {
|
|
5355
5592
|
if (!Array.isArray(child))
|
|
5356
5593
|
throw new Error("Invalid schema array");
|
|
5357
5594
|
for (const nested of child)
|
|
5358
|
-
visit(nested,
|
|
5595
|
+
visit(nested, path5, false, depth + 1);
|
|
5359
5596
|
} else if (schemaChildren.has(keyword)) {
|
|
5360
5597
|
if (Array.isArray(child))
|
|
5361
5598
|
for (const nested of child)
|
|
5362
|
-
visit(nested,
|
|
5599
|
+
visit(nested, path5, false, depth + 1);
|
|
5363
5600
|
else
|
|
5364
|
-
visit(child,
|
|
5601
|
+
visit(child, path5, false, depth + 1);
|
|
5365
5602
|
}
|
|
5366
5603
|
}
|
|
5367
5604
|
} finally {
|
|
@@ -5559,8 +5796,8 @@ function authorizationServerMetadataLocations(issuer) {
|
|
|
5559
5796
|
];
|
|
5560
5797
|
const issuerUrl = new URL(issuer);
|
|
5561
5798
|
if (issuerUrl.pathname !== "/") {
|
|
5562
|
-
const
|
|
5563
|
-
issuerUrl.pathname = `${
|
|
5799
|
+
const path5 = issuerUrl.pathname.endsWith("/") ? issuerUrl.pathname.slice(0, -1) : issuerUrl.pathname;
|
|
5800
|
+
issuerUrl.pathname = `${path5}/.well-known/openid-configuration`;
|
|
5564
5801
|
locations.push(issuerUrl.toString());
|
|
5565
5802
|
}
|
|
5566
5803
|
return locations;
|