@poe-platform/safe-js 0.1.802 → 0.1.804
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/README.md +4 -4
- package/dist/safe-js/bundled-yaml/LICENSE +13 -0
- package/dist/safe-js/bundled-yaml/index.js +6601 -0
- package/dist/safe-js/chunks/{chunk-HFRBLE6Y.js → chunk-36PBWSDR.js} +23 -11
- package/dist/safe-js/chunks/{chunk-CK3YK2EC.js → chunk-6AO2T2VZ.js} +1 -1
- package/dist/safe-js/chunks/{chunk-5RLD4TVI.js → chunk-BKNQTNHP.js} +36 -23
- package/dist/safe-js/chunks/{chunk-YCFOANSU.js → chunk-BYDDUKS3.js} +3 -3
- package/dist/safe-js/chunks/{chunk-6H5VC5W5.js → chunk-CKOGIOB3.js} +470 -464
- package/dist/safe-js/chunks/{chunk-XOMGWE37.js → chunk-CVPZFRCC.js} +3 -3
- package/dist/safe-js/chunks/{chunk-4KMUWNZD.js → chunk-ECMGBHAB.js} +13 -137
- package/dist/safe-js/chunks/{chunk-A3LXCGMJ.js → chunk-EQUBWQJN.js} +57 -34
- package/dist/safe-js/chunks/{chunk-QRWRR6IA.js → chunk-JMDWLOJV.js} +26 -3
- package/dist/safe-js/chunks/{chunk-3NFTBEZR.js → chunk-JQ5QJV5Q.js} +2 -2
- package/dist/safe-js/chunks/{chunk-MMN3IESA.js → chunk-KMBCLXER.js} +1 -1
- package/dist/safe-js/chunks/{chunk-PMOXSLM5.js → chunk-MJH73KJI.js} +2 -2
- package/dist/safe-js/chunks/{chunk-IB6WAQAE.js → chunk-P66QKVQC.js} +3 -3
- package/dist/safe-js/chunks/{chunk-HPHCQNFV.js → chunk-RPGCF4KE.js} +130 -3
- package/dist/safe-js/chunks/{chunk-F6Y734YA.js → chunk-SIR35W6T.js} +5 -5
- package/dist/safe-js/chunks/{chunk-7THYP46K.js → chunk-SMGNJNDU.js} +1 -1
- package/dist/safe-js/chunks/{chunk-XV5OYKND.js → chunk-VLRTLEDI.js} +2 -2
- package/dist/safe-js/chunks/{chunk-JCHJGIQA.js → chunk-VVOPJZRG.js} +2 -0
- package/dist/safe-js/chunks/{cli-runtime-L4IABN2W.js → cli-runtime-A353G7DR.js} +15 -15
- package/dist/safe-js/chunks/{dump-KUPBSXSL.js → dump-KQZFULX2.js} +4 -4
- package/dist/safe-js/chunks/harness-XT6K6X67.js +10 -0
- package/dist/safe-js/chunks/mcp-VWE444VD.js +11 -0
- package/dist/safe-js/chunks/migration-file-NSKXOMAO.js +14 -0
- package/dist/safe-js/chunks/restore-SHPORPY4.js +13 -0
- package/dist/safe-js/chunks/run-FQOSICOT.js +19 -0
- package/dist/safe-js/chunks/{runtime-modules-MGSXY4BN.js → runtime-modules-DBRXXRND.js} +4 -4
- package/dist/safe-js/chunks/{source-files-BSDM22Y2.js → source-files-A2WSOO6G.js} +3 -3
- package/dist/safe-js/cli.js +2 -2
- package/dist/safe-js/core.d.ts +19 -0
- package/dist/safe-js/core.js +39 -15
- package/dist/safe-js/error/format.d.ts +3 -0
- package/dist/safe-js/index.d.ts +1 -12
- package/dist/safe-js/index.js +30 -39
- package/dist/safe-js/interp/host-call.d.ts +1 -0
- package/dist/safe-js/interp/interpreter.d.ts +1 -3
- package/dist/safe-js/modules/fs.js +5 -5
- package/dist/safe-js/platform/node.d.ts +1 -0
- package/dist/safe-js/platform/node.js +1 -0
- package/dist/safe-js/platform/workerd.d.ts +1 -0
- package/dist/safe-js/platform/workerd.js +2 -0
- package/dist/safe-js/portable/chunks/{chunk-XCTU25SU.js → chunk-NLGXLZCK.js} +1 -1
- package/dist/safe-js/portable/chunks/{chunk-2FNMGWDK.js → chunk-SRMMDXD3.js} +127 -91
- package/dist/safe-js/portable/chunks/{chunk-H5X3T5VP.js → chunk-W6MZOTYR.js} +2 -0
- package/dist/safe-js/portable/chunks/{source-files-CVU252FB.js → source-files-AEDWESAJ.js} +2 -2
- package/dist/safe-js/portable/core.js +467 -249
- package/dist/safe-js/portable/modules/fs.js +2 -2
- package/dist/safe-js/workerd.js +57987 -57738
- package/package.json +6 -5
- package/dist/safe-js/chunks/harness-3DDUS3RY.js +0 -10
- package/dist/safe-js/chunks/mcp-5TMFK3UE.js +0 -11
- package/dist/safe-js/chunks/migration-file-4CYSJ6LO.js +0 -14
- package/dist/safe-js/chunks/restore-GDHBT5ZD.js +0 -13
- package/dist/safe-js/chunks/run-M6N5KCPF.js +0 -19
- package/dist/safe-js/node-unavailable.d.ts +0 -1
|
@@ -1459,7 +1459,11 @@ var JSON_RPC_ERROR_CODES = Object.freeze({
|
|
|
1459
1459
|
// packages/tiny-stdio-mcp-server/src/base64.ts
|
|
1460
1460
|
function isBase64(value) {
|
|
1461
1461
|
if (value.length % 4 !== 0) return false;
|
|
1462
|
-
|
|
1462
|
+
try {
|
|
1463
|
+
return btoa(atob(value)) === value;
|
|
1464
|
+
} catch {
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1463
1467
|
}
|
|
1464
1468
|
|
|
1465
1469
|
// packages/tiny-stdio-mcp-server/src/uri.ts
|
|
@@ -3682,137 +3686,8 @@ function copyBoundedOAuthJson(value, message) {
|
|
|
3682
3686
|
}
|
|
3683
3687
|
}
|
|
3684
3688
|
|
|
3685
|
-
// packages/mcp-oauth/src/client/
|
|
3686
|
-
import http from "node:http";
|
|
3687
|
-
|
|
3688
|
-
// packages/mcp-oauth/src/client/authorization-state.ts
|
|
3689
|
-
import crypto2 from "node:crypto";
|
|
3690
|
-
function createAuthorizationState(input) {
|
|
3691
|
-
const payload = {
|
|
3692
|
-
v: 1,
|
|
3693
|
-
n: crypto2.randomBytes(16).toString("base64url"),
|
|
3694
|
-
i: input.issuer,
|
|
3695
|
-
r: input.requireIssuer
|
|
3696
|
-
};
|
|
3697
|
-
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
3698
|
-
}
|
|
3699
|
-
function parseAuthorizationState(value) {
|
|
3700
|
-
if (value === null || value.length === 0) {
|
|
3701
|
-
return null;
|
|
3702
|
-
}
|
|
3703
|
-
try {
|
|
3704
|
-
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
3705
|
-
const parsed = JSON.parse(decoded);
|
|
3706
|
-
if (!isObjectRecord(parsed)) {
|
|
3707
|
-
return null;
|
|
3708
|
-
}
|
|
3709
|
-
const version = getOwnEntry(parsed, "v");
|
|
3710
|
-
const nonce = getOwnEntry(parsed, "n");
|
|
3711
|
-
const issuer = getOwnEntry(parsed, "i");
|
|
3712
|
-
const requireIssuer = getOwnEntry(parsed, "r");
|
|
3713
|
-
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
3714
|
-
return null;
|
|
3715
|
-
}
|
|
3716
|
-
return {
|
|
3717
|
-
issuer,
|
|
3718
|
-
requireIssuer
|
|
3719
|
-
};
|
|
3720
|
-
} catch {
|
|
3721
|
-
return null;
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3724
|
-
function isObjectRecord(value) {
|
|
3725
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3726
|
-
}
|
|
3727
|
-
function getOwnEntry(record2, key2) {
|
|
3728
|
-
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
3729
|
-
}
|
|
3730
|
-
|
|
3731
|
-
// packages/mcp-oauth/src/client/authorization-error.ts
|
|
3732
|
-
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
3733
|
-
var OAuthAuthorizationError = class extends Error {
|
|
3734
|
-
constructor(error, errorDescription) {
|
|
3735
|
-
super(`OAuth authorization failed: ${error} \u2014 ${errorDescription}`);
|
|
3736
|
-
this.error = error;
|
|
3737
|
-
this.errorDescription = errorDescription;
|
|
3738
|
-
this.name = "OAuthAuthorizationError";
|
|
3739
|
-
Object.defineProperty(this, authorizationErrorBrand, { value: true });
|
|
3740
|
-
}
|
|
3741
|
-
error;
|
|
3742
|
-
errorDescription;
|
|
3743
|
-
/** Recognize errors from separately bundled copies of this package. */
|
|
3744
|
-
static is(value) {
|
|
3745
|
-
return value instanceof Error && Object.getOwnPropertyDescriptor(value, authorizationErrorBrand)?.value === true;
|
|
3746
|
-
}
|
|
3747
|
-
};
|
|
3748
|
-
|
|
3749
|
-
// packages/mcp-oauth/src/client/loopback-authorization.ts
|
|
3689
|
+
// packages/mcp-oauth/src/client/redirect-target.ts
|
|
3750
3690
|
var oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
3751
|
-
function snapshotLoopbackAuthorizationOptions(options) {
|
|
3752
|
-
return {
|
|
3753
|
-
...options,
|
|
3754
|
-
openBrowser: options.openBrowser?.bind(options),
|
|
3755
|
-
readLine: options.readLine?.bind(options),
|
|
3756
|
-
waitForCallback: options.waitForCallback?.bind(options),
|
|
3757
|
-
createServer: options.createServer?.bind(options),
|
|
3758
|
-
callbackPath: options.callbackPath,
|
|
3759
|
-
redirectUri: options.redirectUri,
|
|
3760
|
-
signal: options.signal,
|
|
3761
|
-
timeoutMs: options.timeoutMs,
|
|
3762
|
-
landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body }
|
|
3763
|
-
};
|
|
3764
|
-
}
|
|
3765
|
-
async function createLoopbackAuthorizationSession(options = {}) {
|
|
3766
|
-
options = snapshotLoopbackAuthorizationOptions(options);
|
|
3767
|
-
options.signal?.throwIfAborted();
|
|
3768
|
-
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3769
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
3770
|
-
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
3771
|
-
if (options.waitForCallback !== void 0) return createHostedAuthorizationSession(options, timeoutMs);
|
|
3772
|
-
const target = loopbackTarget(options);
|
|
3773
|
-
const server = options.createServer ? options.createServer() : http.createServer();
|
|
3774
|
-
const controller = new AbortController();
|
|
3775
|
-
let closed = false;
|
|
3776
|
-
let used = false;
|
|
3777
|
-
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
3778
|
-
const teardown = () => {
|
|
3779
|
-
if (closed) return;
|
|
3780
|
-
closed = true;
|
|
3781
|
-
clearTimeout(timer);
|
|
3782
|
-
options.signal?.removeEventListener("abort", callerAbort);
|
|
3783
|
-
server.closeAllConnections?.();
|
|
3784
|
-
server.close();
|
|
3785
|
-
};
|
|
3786
|
-
const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
3787
|
-
timer.unref?.();
|
|
3788
|
-
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
3789
|
-
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
3790
|
-
if (options.signal?.aborted) callerAbort();
|
|
3791
|
-
let port;
|
|
3792
|
-
try {
|
|
3793
|
-
port = await startServer(server, target.port, target.host, controller.signal);
|
|
3794
|
-
} catch (error) {
|
|
3795
|
-
controller.abort(error);
|
|
3796
|
-
throw error;
|
|
3797
|
-
}
|
|
3798
|
-
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
3799
|
-
return {
|
|
3800
|
-
redirectUri,
|
|
3801
|
-
async waitForCode(authorizationUrl) {
|
|
3802
|
-
controller.signal.throwIfAborted();
|
|
3803
|
-
if (used) throw new Error("OAuth authorization session has already been used");
|
|
3804
|
-
used = true;
|
|
3805
|
-
try {
|
|
3806
|
-
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
3807
|
-
} finally {
|
|
3808
|
-
clearTimeout(timer);
|
|
3809
|
-
}
|
|
3810
|
-
},
|
|
3811
|
-
close() {
|
|
3812
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3813
|
-
}
|
|
3814
|
-
};
|
|
3815
|
-
}
|
|
3816
3691
|
function loopbackTarget(options) {
|
|
3817
3692
|
if (options.redirectUri !== void 0) {
|
|
3818
3693
|
let url;
|
|
@@ -3845,303 +3720,69 @@ function validateHostedOAuthRedirect(redirectUri, callbackPath) {
|
|
|
3845
3720
|
if (url.protocol !== "https:" || url.username || url.password || url.href.includes("#") || url.port === "0" || oauthCallbackParameters.some((name) => url.searchParams.has(name)) || [...redirectUri].some((char) => char.codePointAt(0) <= 32) || callbackPath !== void 0 && callbackPath !== url.pathname)
|
|
3846
3721
|
throw new Error("Invalid hosted OAuth redirect URI");
|
|
3847
3722
|
}
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
const cleanup = () => {
|
|
3856
|
-
clearTimeout(timer);
|
|
3857
|
-
options.signal?.removeEventListener("abort", aborted);
|
|
3858
|
-
};
|
|
3859
|
-
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
3860
|
-
options.signal?.addEventListener("abort", aborted, { once: true });
|
|
3861
|
-
if (options.signal?.aborted) aborted();
|
|
3862
|
-
return {
|
|
3863
|
-
redirectUri,
|
|
3864
|
-
async waitForCode(authorizationUrl) {
|
|
3865
|
-
controller.signal.throwIfAborted();
|
|
3866
|
-
if (used) throw new Error("OAuth authorization session has already been used");
|
|
3867
|
-
used = true;
|
|
3868
|
-
try {
|
|
3869
|
-
const authorization = new URL(authorizationUrl);
|
|
3870
|
-
if (authorization.searchParams.getAll("redirect_uri").length !== 1 || authorization.searchParams.get("redirect_uri") !== redirectUri)
|
|
3871
|
-
throw new Error("OAuth authorization redirect mismatch");
|
|
3872
|
-
const callback = await new Promise((resolve, reject) => {
|
|
3873
|
-
const onAbort = () => reject(controller.signal.reason);
|
|
3874
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
3875
|
-
void Promise.resolve().then(() => {
|
|
3876
|
-
controller.signal.throwIfAborted();
|
|
3877
|
-
return options.waitForCallback({ authorizationUrl, redirectUri, signal: controller.signal });
|
|
3878
|
-
}).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
|
|
3879
|
-
});
|
|
3880
|
-
controller.signal.throwIfAborted();
|
|
3881
|
-
const url = new URL(callback), base = new URL(redirectUri);
|
|
3882
|
-
if (url.origin !== base.origin || url.pathname !== base.pathname || url.username || url.password || url.href.includes("#") || [...callback].some((char) => char.codePointAt(0) <= 32)) throw new Error("OAuth callback redirect mismatch");
|
|
3883
|
-
const params = readAuthorizationCallbackParameters(url);
|
|
3884
|
-
for (const name of oauthCallbackParameters) url.searchParams.delete(name);
|
|
3885
|
-
if (url.searchParams.toString() !== base.searchParams.toString()) throw new Error("OAuth callback redirect mismatch");
|
|
3886
|
-
const expected = readExpectedAuthorizationCallback(authorizationUrl);
|
|
3887
|
-
if (expected.state === null) throw new Error("OAuth authorization missing state");
|
|
3888
|
-
validateAuthorizationCallbackBinding(params, expected);
|
|
3889
|
-
if (params.error !== null) throw new OAuthAuthorizationError(params.error, params.errorDescription ?? params.error);
|
|
3890
|
-
return validateAuthorizationCallbackParameters(params, expected);
|
|
3891
|
-
} finally {
|
|
3892
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3893
|
-
}
|
|
3894
|
-
},
|
|
3895
|
-
close() {
|
|
3896
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3897
|
-
}
|
|
3898
|
-
};
|
|
3723
|
+
|
|
3724
|
+
// packages/mcp-oauth/src/client/token-auth-method.ts
|
|
3725
|
+
function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
3726
|
+
if (value === void 0 || value === null) return void 0;
|
|
3727
|
+
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
3728
|
+
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
3729
|
+
return value;
|
|
3899
3730
|
}
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
} catch {
|
|
3956
|
-
res.writeHead(400);
|
|
3957
|
-
res.end("Invalid callback URL");
|
|
3958
|
-
return;
|
|
3959
|
-
}
|
|
3960
|
-
if (url.pathname !== callbackPath) {
|
|
3961
|
-
res.writeHead(404);
|
|
3962
|
-
res.end("Not found");
|
|
3963
|
-
return;
|
|
3964
|
-
}
|
|
3965
|
-
try {
|
|
3966
|
-
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
3967
|
-
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3968
|
-
if (callbackParameters.error !== null)
|
|
3969
|
-
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3970
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3971
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3972
|
-
res.end(buildSuccessPage(options.landingPage));
|
|
3973
|
-
settle(() => resolve(code));
|
|
3974
|
-
} catch (error) {
|
|
3975
|
-
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
3976
|
-
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
3977
|
-
settle(() => reject(error));
|
|
3978
|
-
}
|
|
3979
|
-
};
|
|
3980
|
-
server.on("request", request);
|
|
3981
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
3982
|
-
if (options.readLine !== void 0) {
|
|
3983
|
-
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
3984
|
-
if (settled) return;
|
|
3985
|
-
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
3986
|
-
if (callbackParameters === null) throw new Error("OAuth callback missing authorization code");
|
|
3987
|
-
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3988
|
-
if (callbackParameters.error !== null)
|
|
3989
|
-
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3990
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3991
|
-
settle(() => resolve(code));
|
|
3992
|
-
}).catch((error) => settle(() => reject(error)));
|
|
3993
|
-
}
|
|
3994
|
-
if (options.openBrowser !== void 0) {
|
|
3995
|
-
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
3996
|
-
}
|
|
3997
|
-
});
|
|
3998
|
-
}
|
|
3999
|
-
function extractCallbackParametersFromInput(input) {
|
|
4000
|
-
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
4001
|
-
if (trimmed.length === 0) {
|
|
4002
|
-
return null;
|
|
4003
|
-
}
|
|
4004
|
-
let url;
|
|
4005
|
-
try {
|
|
4006
|
-
url = new URL(trimmed);
|
|
4007
|
-
} catch {
|
|
4008
|
-
return {
|
|
4009
|
-
code: trimmed,
|
|
4010
|
-
error: null,
|
|
4011
|
-
errorDescription: null,
|
|
4012
|
-
state: null,
|
|
4013
|
-
iss: null
|
|
4014
|
-
};
|
|
4015
|
-
}
|
|
4016
|
-
return readAuthorizationCallbackParameters(url);
|
|
4017
|
-
}
|
|
4018
|
-
function readAuthorizationCallbackParameters(url) {
|
|
4019
|
-
for (const parameter of oauthCallbackParameters) {
|
|
4020
|
-
if (url.searchParams.getAll(parameter).length > 1)
|
|
4021
|
-
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
4022
|
-
}
|
|
4023
|
-
return {
|
|
4024
|
-
code: url.searchParams.get("code"),
|
|
4025
|
-
error: url.searchParams.get("error"),
|
|
4026
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
4027
|
-
state: url.searchParams.get("state"),
|
|
4028
|
-
iss: url.searchParams.get("iss")
|
|
4029
|
-
};
|
|
4030
|
-
}
|
|
4031
|
-
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
4032
|
-
const url = new URL(authorizationUrl);
|
|
4033
|
-
const state = url.searchParams.get("state");
|
|
4034
|
-
const parsedState = parseAuthorizationState(state);
|
|
4035
|
-
return {
|
|
4036
|
-
state,
|
|
4037
|
-
issuer: parsedState?.issuer ?? null,
|
|
4038
|
-
requireIssuer: parsedState?.requireIssuer ?? false
|
|
4039
|
-
};
|
|
4040
|
-
}
|
|
4041
|
-
function validateAuthorizationCallbackParameters(callback, expected) {
|
|
4042
|
-
validateAuthorizationCallbackBinding(callback, expected);
|
|
4043
|
-
if (callback.code === null || callback.code.length === 0) {
|
|
4044
|
-
throw new Error("OAuth callback missing authorization code");
|
|
4045
|
-
}
|
|
4046
|
-
return callback.code;
|
|
4047
|
-
}
|
|
4048
|
-
function validateAuthorizationCallbackBinding(callback, expected) {
|
|
4049
|
-
if (expected.state !== null) {
|
|
4050
|
-
if (callback.state === null || callback.state.length === 0) {
|
|
4051
|
-
throw new Error("OAuth callback missing state");
|
|
4052
|
-
}
|
|
4053
|
-
if (callback.state !== expected.state) {
|
|
4054
|
-
throw new Error("OAuth callback state mismatch");
|
|
4055
|
-
}
|
|
4056
|
-
}
|
|
4057
|
-
if (expected.requireIssuer) {
|
|
4058
|
-
if (callback.iss === null || callback.iss.length === 0) {
|
|
4059
|
-
throw new Error("OAuth callback missing issuer");
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
if (callback.iss !== null && callback.iss.length > 0 && expected.issuer !== null && callback.iss !== expected.issuer) {
|
|
4063
|
-
throw new Error("OAuth callback issuer mismatch");
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
|
-
function escapeHtml(text) {
|
|
4067
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4068
|
-
}
|
|
4069
|
-
function buildSuccessPage(landingPage) {
|
|
4070
|
-
const title = landingPage?.title ?? "Connected";
|
|
4071
|
-
const body = landingPage?.body ?? "You can close this tab and return to your terminal.";
|
|
4072
|
-
return [
|
|
4073
|
-
"<!DOCTYPE html>",
|
|
4074
|
-
`<html><head><meta charset=utf-8><title>${escapeHtml(title)}</title></head>`,
|
|
4075
|
-
'<body style="font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0">',
|
|
4076
|
-
'<div style="text-align:center">',
|
|
4077
|
-
`<h1>${escapeHtml(title)}</h1>`,
|
|
4078
|
-
`<p style="color:#666">${escapeHtml(body)}</p>`,
|
|
4079
|
-
"</div></body></html>"
|
|
4080
|
-
].join("");
|
|
4081
|
-
}
|
|
4082
|
-
|
|
4083
|
-
// packages/mcp-oauth/src/client/token-auth-method.ts
|
|
4084
|
-
function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
4085
|
-
if (value === void 0 || value === null) return void 0;
|
|
4086
|
-
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
4087
|
-
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
4088
|
-
return value;
|
|
4089
|
-
}
|
|
4090
|
-
|
|
4091
|
-
// packages/mcp-oauth/src/client/client-registration.ts
|
|
4092
|
-
function parseOAuthClientRegistration(value) {
|
|
4093
|
-
const invalid = () => new Error("Invalid OAuth client registration metadata");
|
|
4094
|
-
const result = copyBoundedOAuthJson(value, "Invalid OAuth client registration metadata");
|
|
4095
|
-
if (typeof result !== "object" || result === null || Array.isArray(result)) throw invalid();
|
|
4096
|
-
const record2 = result;
|
|
4097
|
-
if (!Object.hasOwn(record2, "client_id") || typeof record2.client_id !== "string" || record2.client_id.trim() === "")
|
|
4098
|
-
throw new Error("OAuth client registration response missing client_id");
|
|
4099
|
-
for (const key2 of [
|
|
4100
|
-
"client_id",
|
|
4101
|
-
"client_secret",
|
|
4102
|
-
"token_endpoint_auth_method",
|
|
4103
|
-
"application_type",
|
|
4104
|
-
"client_name",
|
|
4105
|
-
"client_uri",
|
|
4106
|
-
"logo_uri",
|
|
4107
|
-
"scope",
|
|
4108
|
-
"tos_uri",
|
|
4109
|
-
"policy_uri",
|
|
4110
|
-
"jwks_uri",
|
|
4111
|
-
"software_id",
|
|
4112
|
-
"software_version",
|
|
4113
|
-
"software_statement",
|
|
4114
|
-
"registration_access_token",
|
|
4115
|
-
"registration_client_uri",
|
|
4116
|
-
"issuer"
|
|
4117
|
-
]) {
|
|
4118
|
-
if (Object.hasOwn(record2, key2) && record2[key2] !== null && typeof record2[key2] !== "string") throw invalid();
|
|
4119
|
-
}
|
|
4120
|
-
if (typeof record2.client_secret === "string" && record2.client_secret.trim() === "") throw invalid();
|
|
4121
|
-
for (const key2 of ["redirect_uris", "grant_types", "response_types", "contacts"]) {
|
|
4122
|
-
const entry = record2[key2];
|
|
4123
|
-
if (Object.hasOwn(record2, key2) && entry !== null && (!Array.isArray(entry) || entry.some((item) => typeof item !== "string"))) throw invalid();
|
|
4124
|
-
}
|
|
4125
|
-
for (const key2 of ["client_id_issued_at", "client_secret_expires_at"]) {
|
|
4126
|
-
const entry = record2[key2];
|
|
4127
|
-
if (Object.hasOwn(record2, key2) && entry !== null && (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 0)) throw invalid();
|
|
4128
|
-
}
|
|
4129
|
-
try {
|
|
4130
|
-
normalizeOAuthScope(Object.hasOwn(record2, "scope") && record2.scope !== null ? record2.scope : void 0);
|
|
4131
|
-
} catch {
|
|
4132
|
-
throw invalid();
|
|
4133
|
-
}
|
|
4134
|
-
return record2;
|
|
4135
|
-
}
|
|
4136
|
-
function normalizeStoredOAuthClient(value) {
|
|
4137
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
4138
|
-
const record2 = value;
|
|
4139
|
-
const clientId = Object.hasOwn(record2, "clientId") ? record2.clientId : void 0;
|
|
4140
|
-
const clientSecret = Object.hasOwn(record2, "clientSecret") ? record2.clientSecret : void 0;
|
|
4141
|
-
if (typeof clientId !== "string" || clientId.trim() === "" || clientSecret !== void 0 && (typeof clientSecret !== "string" || clientSecret.trim() === "")) return null;
|
|
4142
|
-
const client = { clientId: clientId.trim(), ...clientSecret === void 0 ? {} : { clientSecret: clientSecret.trim() } };
|
|
4143
|
-
const requestedRedirectUri = Object.hasOwn(record2, "requestedRedirectUri") ? record2.requestedRedirectUri : void 0;
|
|
4144
|
-
if (requestedRedirectUri !== void 0) {
|
|
3731
|
+
|
|
3732
|
+
// packages/mcp-oauth/src/client/client-registration.ts
|
|
3733
|
+
function parseOAuthClientRegistration(value) {
|
|
3734
|
+
const invalid = () => new Error("Invalid OAuth client registration metadata");
|
|
3735
|
+
const result = copyBoundedOAuthJson(value, "Invalid OAuth client registration metadata");
|
|
3736
|
+
if (typeof result !== "object" || result === null || Array.isArray(result)) throw invalid();
|
|
3737
|
+
const record2 = result;
|
|
3738
|
+
if (!Object.hasOwn(record2, "client_id") || typeof record2.client_id !== "string" || record2.client_id.trim() === "")
|
|
3739
|
+
throw new Error("OAuth client registration response missing client_id");
|
|
3740
|
+
for (const key2 of [
|
|
3741
|
+
"client_id",
|
|
3742
|
+
"client_secret",
|
|
3743
|
+
"token_endpoint_auth_method",
|
|
3744
|
+
"application_type",
|
|
3745
|
+
"client_name",
|
|
3746
|
+
"client_uri",
|
|
3747
|
+
"logo_uri",
|
|
3748
|
+
"scope",
|
|
3749
|
+
"tos_uri",
|
|
3750
|
+
"policy_uri",
|
|
3751
|
+
"jwks_uri",
|
|
3752
|
+
"software_id",
|
|
3753
|
+
"software_version",
|
|
3754
|
+
"software_statement",
|
|
3755
|
+
"registration_access_token",
|
|
3756
|
+
"registration_client_uri",
|
|
3757
|
+
"issuer"
|
|
3758
|
+
]) {
|
|
3759
|
+
if (Object.hasOwn(record2, key2) && record2[key2] !== null && typeof record2[key2] !== "string") throw invalid();
|
|
3760
|
+
}
|
|
3761
|
+
if (typeof record2.client_secret === "string" && record2.client_secret.trim() === "") throw invalid();
|
|
3762
|
+
for (const key2 of ["redirect_uris", "grant_types", "response_types", "contacts"]) {
|
|
3763
|
+
const entry = record2[key2];
|
|
3764
|
+
if (Object.hasOwn(record2, key2) && entry !== null && (!Array.isArray(entry) || entry.some((item) => typeof item !== "string"))) throw invalid();
|
|
3765
|
+
}
|
|
3766
|
+
for (const key2 of ["client_id_issued_at", "client_secret_expires_at"]) {
|
|
3767
|
+
const entry = record2[key2];
|
|
3768
|
+
if (Object.hasOwn(record2, key2) && entry !== null && (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 0)) throw invalid();
|
|
3769
|
+
}
|
|
3770
|
+
try {
|
|
3771
|
+
normalizeOAuthScope(Object.hasOwn(record2, "scope") && record2.scope !== null ? record2.scope : void 0);
|
|
3772
|
+
} catch {
|
|
3773
|
+
throw invalid();
|
|
3774
|
+
}
|
|
3775
|
+
return record2;
|
|
3776
|
+
}
|
|
3777
|
+
function normalizeStoredOAuthClient(value) {
|
|
3778
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
3779
|
+
const record2 = value;
|
|
3780
|
+
const clientId = Object.hasOwn(record2, "clientId") ? record2.clientId : void 0;
|
|
3781
|
+
const clientSecret = Object.hasOwn(record2, "clientSecret") ? record2.clientSecret : void 0;
|
|
3782
|
+
if (typeof clientId !== "string" || clientId.trim() === "" || clientSecret !== void 0 && (typeof clientSecret !== "string" || clientSecret.trim() === "")) return null;
|
|
3783
|
+
const client = { clientId: clientId.trim(), ...clientSecret === void 0 ? {} : { clientSecret: clientSecret.trim() } };
|
|
3784
|
+
const requestedRedirectUri = Object.hasOwn(record2, "requestedRedirectUri") ? record2.requestedRedirectUri : void 0;
|
|
3785
|
+
if (requestedRedirectUri !== void 0) {
|
|
4145
3786
|
try {
|
|
4146
3787
|
if (typeof requestedRedirectUri !== "string") throw new Error("Invalid redirect identity");
|
|
4147
3788
|
if (new URL(requestedRedirectUri).protocol === "https:")
|
|
@@ -4198,7 +3839,7 @@ function registrationMatchesRedirect(client, requestedUri, fresh = false) {
|
|
|
4198
3839
|
}
|
|
4199
3840
|
|
|
4200
3841
|
// packages/mcp-oauth/src/client/auth-store-session-store.ts
|
|
4201
|
-
import
|
|
3842
|
+
import crypto2 from "node:crypto";
|
|
4202
3843
|
import path4 from "node:path";
|
|
4203
3844
|
|
|
4204
3845
|
// packages/auth-store/src/encrypted-file-store.ts
|
|
@@ -4217,8 +3858,8 @@ import { randomUUID } from "node:crypto";
|
|
|
4217
3858
|
import path from "node:path";
|
|
4218
3859
|
async function withSecretStoreFileLock(fs2, lockDirectory, operation, options = {}) {
|
|
4219
3860
|
options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
|
|
4220
|
-
const timeoutMs = options.timeoutMs ??
|
|
4221
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647)
|
|
3861
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
3862
|
+
if (timeoutMs !== Infinity && (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647))
|
|
4222
3863
|
throw new Error("Invalid secret-store transaction lock timeout");
|
|
4223
3864
|
options.signal?.throwIfAborted();
|
|
4224
3865
|
const deadline = performance.now() + timeoutMs;
|
|
@@ -4613,10 +4254,10 @@ function parseEncryptedDocument(raw) {
|
|
|
4613
4254
|
if (!isRecord2(parsed)) {
|
|
4614
4255
|
return null;
|
|
4615
4256
|
}
|
|
4616
|
-
const version =
|
|
4617
|
-
const iv =
|
|
4618
|
-
const authTag =
|
|
4619
|
-
const ciphertext =
|
|
4257
|
+
const version = getOwnEntry(parsed, "version");
|
|
4258
|
+
const iv = getOwnEntry(parsed, "iv");
|
|
4259
|
+
const authTag = getOwnEntry(parsed, "authTag");
|
|
4260
|
+
const ciphertext = getOwnEntry(parsed, "ciphertext");
|
|
4620
4261
|
if (version !== ENCRYPTION_VERSION) {
|
|
4621
4262
|
return null;
|
|
4622
4263
|
}
|
|
@@ -4636,7 +4277,7 @@ function parseEncryptedDocument(raw) {
|
|
|
4636
4277
|
function isRecord2(value) {
|
|
4637
4278
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
4638
4279
|
}
|
|
4639
|
-
function
|
|
4280
|
+
function getOwnEntry(record2, key2) {
|
|
4640
4281
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4641
4282
|
}
|
|
4642
4283
|
function isNotFoundError(error) {
|
|
@@ -4808,14 +4449,14 @@ function createSecurityCliFailure(operation, result) {
|
|
|
4808
4449
|
return new Error(`Failed to ${operation}: security exited with code ${exitCode}`);
|
|
4809
4450
|
}
|
|
4810
4451
|
function getCommandExitCode(result) {
|
|
4811
|
-
const value =
|
|
4452
|
+
const value = getOwnEntry2(result, "exitCode");
|
|
4812
4453
|
return typeof value === "number" && Number.isInteger(value) ? value : 1;
|
|
4813
4454
|
}
|
|
4814
4455
|
function getCommandOutput(result, key2) {
|
|
4815
|
-
const value =
|
|
4456
|
+
const value = getOwnEntry2(result, key2);
|
|
4816
4457
|
return typeof value === "string" ? value : "";
|
|
4817
4458
|
}
|
|
4818
|
-
function
|
|
4459
|
+
function getOwnEntry2(record2, key2) {
|
|
4819
4460
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4820
4461
|
}
|
|
4821
4462
|
|
|
@@ -4935,7 +4576,7 @@ function createAuthStoreClientStore(options, namespace) {
|
|
|
4935
4576
|
} catch {
|
|
4936
4577
|
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4937
4578
|
}
|
|
4938
|
-
if (
|
|
4579
|
+
if (isObjectRecord(parsed) && typeof getOwnEntry3(parsed, "clientId") === "string")
|
|
4939
4580
|
return parsed;
|
|
4940
4581
|
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
4941
4582
|
},
|
|
@@ -4956,7 +4597,7 @@ function snapshotPersistenceOptions(options) {
|
|
|
4956
4597
|
}
|
|
4957
4598
|
function createNamedSecretStore(key2, options, defaults, namespace) {
|
|
4958
4599
|
options = snapshotOAuthPersistenceOptions(options);
|
|
4959
|
-
const hash =
|
|
4600
|
+
const hash = crypto2.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
|
|
4960
4601
|
const configuredFilePath = options.fileStore?.filePath;
|
|
4961
4602
|
const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
|
|
4962
4603
|
const fileStore = {
|
|
@@ -4997,47 +4638,47 @@ function assertPersistenceNamespace(namespace) {
|
|
|
4997
4638
|
if (namespace !== void 0 && (typeof namespace !== "string" || namespace.trim() === "" || Buffer.byteLength(namespace, "utf8") > 1024))
|
|
4998
4639
|
throw new Error("OAuth persistence namespace must be a nonempty string within 1024 bytes");
|
|
4999
4640
|
}
|
|
5000
|
-
function
|
|
4641
|
+
function isObjectRecord(value) {
|
|
5001
4642
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5002
4643
|
}
|
|
5003
|
-
function
|
|
4644
|
+
function getOwnEntry3(record2, key2) {
|
|
5004
4645
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
5005
4646
|
}
|
|
5006
4647
|
function getOwnString(record2, key2) {
|
|
5007
|
-
const value =
|
|
4648
|
+
const value = getOwnEntry3(record2, key2);
|
|
5008
4649
|
return typeof value === "string" ? value : void 0;
|
|
5009
4650
|
}
|
|
5010
4651
|
function isStoredOAuthSession(value) {
|
|
5011
|
-
if (!
|
|
4652
|
+
if (!isObjectRecord(value)) {
|
|
5012
4653
|
return false;
|
|
5013
4654
|
}
|
|
5014
|
-
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(
|
|
4655
|
+
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(getOwnEntry3(value, "client")) !== null && isStoredOAuthDiscovery(getOwnEntry3(value, "discovery")) && (getOwnEntry3(value, "requestedScope") === void 0 || isNonBlankOwnString(value, "requestedScope")) && (getOwnEntry3(value, "refreshState") === void 0 || getOwnEntry3(value, "refreshState") === "pending" && getOwnEntry3(value, "tokens") === void 0) && isStoredOAuthTokensOrMissing(getOwnEntry3(value, "tokens"));
|
|
5015
4656
|
}
|
|
5016
4657
|
function isStoredOAuthDiscovery(value) {
|
|
5017
|
-
if (!
|
|
4658
|
+
if (!isObjectRecord(value)) {
|
|
5018
4659
|
return false;
|
|
5019
4660
|
}
|
|
5020
|
-
return isNonBlankOwnString(value, "resourceMetadataUrl") &&
|
|
4661
|
+
return isNonBlankOwnString(value, "resourceMetadataUrl") && isObjectRecord(getOwnEntry3(value, "resourceMetadata")) && isObjectRecord(getOwnEntry3(value, "authorizationServerMetadata"));
|
|
5021
4662
|
}
|
|
5022
4663
|
function isStoredOAuthTokensOrMissing(value) {
|
|
5023
4664
|
if (value === void 0) {
|
|
5024
4665
|
return true;
|
|
5025
4666
|
}
|
|
5026
|
-
if (!
|
|
4667
|
+
if (!isObjectRecord(value)) {
|
|
5027
4668
|
return false;
|
|
5028
4669
|
}
|
|
5029
4670
|
if (!isNonBlankOwnString(value, "accessToken") || getOwnString(value, "tokenType") !== "Bearer") {
|
|
5030
4671
|
return false;
|
|
5031
4672
|
}
|
|
5032
|
-
const expiresAt =
|
|
4673
|
+
const expiresAt = getOwnEntry3(value, "expiresAt");
|
|
5033
4674
|
if (expiresAt !== null && (typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt > MAX_JS_DATE_MS || !Number.isFinite(new Date(expiresAt).getTime()))) {
|
|
5034
4675
|
return false;
|
|
5035
4676
|
}
|
|
5036
|
-
const refreshToken =
|
|
4677
|
+
const refreshToken = getOwnEntry3(value, "refreshToken");
|
|
5037
4678
|
if (refreshToken !== void 0 && (typeof refreshToken !== "string" || refreshToken.trim().length === 0)) {
|
|
5038
4679
|
return false;
|
|
5039
4680
|
}
|
|
5040
|
-
const scope =
|
|
4681
|
+
const scope = getOwnEntry3(value, "scope");
|
|
5041
4682
|
return scope === void 0 || scope === "" || typeof scope === "string" && scope.trim().length > 0;
|
|
5042
4683
|
}
|
|
5043
4684
|
function isNonBlankOwnString(record2, key2) {
|
|
@@ -5101,8 +4742,8 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
5101
4742
|
async function replace(record2, options2) {
|
|
5102
4743
|
options2 = { ...options2, signal: options2.signal, timeoutMs: options2.timeoutMs };
|
|
5103
4744
|
options2.signal?.throwIfAborted();
|
|
5104
|
-
const timeoutMs = options2.timeoutMs ??
|
|
5105
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
4745
|
+
const timeoutMs = options2.timeoutMs ?? Infinity;
|
|
4746
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
5106
4747
|
throw new Error("OAuth replacement timeoutMs must be a positive supported timer interval");
|
|
5107
4748
|
if (store.withLock === void 0) throw new Error("OAuth resource identity backend must support transaction locks");
|
|
5108
4749
|
await store.withLock(async () => {
|
|
@@ -5255,6 +4896,371 @@ async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
|
5255
4896
|
// packages/mcp-oauth/src/client/default-oauth-client-provider.ts
|
|
5256
4897
|
import { URL as URL2 } from "node:url";
|
|
5257
4898
|
|
|
4899
|
+
// packages/mcp-oauth/src/client/loopback-authorization.ts
|
|
4900
|
+
import http from "node:http";
|
|
4901
|
+
|
|
4902
|
+
// packages/mcp-oauth/src/client/authorization-state.ts
|
|
4903
|
+
import crypto3 from "node:crypto";
|
|
4904
|
+
function createAuthorizationState(input) {
|
|
4905
|
+
const payload = {
|
|
4906
|
+
v: 1,
|
|
4907
|
+
n: crypto3.randomBytes(16).toString("base64url"),
|
|
4908
|
+
i: input.issuer,
|
|
4909
|
+
r: input.requireIssuer
|
|
4910
|
+
};
|
|
4911
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
4912
|
+
}
|
|
4913
|
+
function parseAuthorizationState(value) {
|
|
4914
|
+
if (value === null || value.length === 0) {
|
|
4915
|
+
return null;
|
|
4916
|
+
}
|
|
4917
|
+
try {
|
|
4918
|
+
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
4919
|
+
const parsed = JSON.parse(decoded);
|
|
4920
|
+
if (!isObjectRecord2(parsed)) {
|
|
4921
|
+
return null;
|
|
4922
|
+
}
|
|
4923
|
+
const version = getOwnEntry4(parsed, "v");
|
|
4924
|
+
const nonce = getOwnEntry4(parsed, "n");
|
|
4925
|
+
const issuer = getOwnEntry4(parsed, "i");
|
|
4926
|
+
const requireIssuer = getOwnEntry4(parsed, "r");
|
|
4927
|
+
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
4928
|
+
return null;
|
|
4929
|
+
}
|
|
4930
|
+
return {
|
|
4931
|
+
issuer,
|
|
4932
|
+
requireIssuer
|
|
4933
|
+
};
|
|
4934
|
+
} catch {
|
|
4935
|
+
return null;
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
function isObjectRecord2(value) {
|
|
4939
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4940
|
+
}
|
|
4941
|
+
function getOwnEntry4(record2, key2) {
|
|
4942
|
+
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4943
|
+
}
|
|
4944
|
+
|
|
4945
|
+
// packages/mcp-oauth/src/client/authorization-error.ts
|
|
4946
|
+
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
4947
|
+
var OAuthAuthorizationError = class extends Error {
|
|
4948
|
+
constructor(error, errorDescription) {
|
|
4949
|
+
super(`OAuth authorization failed: ${error} \u2014 ${errorDescription}`);
|
|
4950
|
+
this.error = error;
|
|
4951
|
+
this.errorDescription = errorDescription;
|
|
4952
|
+
this.name = "OAuthAuthorizationError";
|
|
4953
|
+
Object.defineProperty(this, authorizationErrorBrand, { value: true });
|
|
4954
|
+
}
|
|
4955
|
+
error;
|
|
4956
|
+
errorDescription;
|
|
4957
|
+
/** Recognize errors from separately bundled copies of this package. */
|
|
4958
|
+
static is(value) {
|
|
4959
|
+
return value instanceof Error && Object.getOwnPropertyDescriptor(value, authorizationErrorBrand)?.value === true;
|
|
4960
|
+
}
|
|
4961
|
+
};
|
|
4962
|
+
|
|
4963
|
+
// packages/mcp-oauth/src/client/loopback-authorization.ts
|
|
4964
|
+
function snapshotLoopbackAuthorizationOptions(options) {
|
|
4965
|
+
return {
|
|
4966
|
+
...options,
|
|
4967
|
+
openBrowser: options.openBrowser?.bind(options),
|
|
4968
|
+
readLine: options.readLine?.bind(options),
|
|
4969
|
+
waitForCallback: options.waitForCallback?.bind(options),
|
|
4970
|
+
createServer: options.createServer?.bind(options),
|
|
4971
|
+
callbackPath: options.callbackPath,
|
|
4972
|
+
redirectUri: options.redirectUri,
|
|
4973
|
+
signal: options.signal,
|
|
4974
|
+
timeoutMs: options.timeoutMs,
|
|
4975
|
+
landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body }
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
async function createLoopbackAuthorizationSession(options = {}) {
|
|
4979
|
+
options = snapshotLoopbackAuthorizationOptions(options);
|
|
4980
|
+
options.signal?.throwIfAborted();
|
|
4981
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
4982
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
4983
|
+
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
4984
|
+
if (options.waitForCallback !== void 0) return createHostedAuthorizationSession(options, timeoutMs);
|
|
4985
|
+
const target = loopbackTarget(options);
|
|
4986
|
+
const server = options.createServer ? options.createServer() : http.createServer();
|
|
4987
|
+
const controller = new AbortController();
|
|
4988
|
+
let closed = false;
|
|
4989
|
+
let used = false;
|
|
4990
|
+
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
4991
|
+
const teardown = () => {
|
|
4992
|
+
if (closed) return;
|
|
4993
|
+
closed = true;
|
|
4994
|
+
clearTimeout(timer);
|
|
4995
|
+
options.signal?.removeEventListener("abort", callerAbort);
|
|
4996
|
+
server.closeAllConnections?.();
|
|
4997
|
+
server.close();
|
|
4998
|
+
};
|
|
4999
|
+
const timer = timeoutMs === Infinity ? void 0 : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
5000
|
+
timer?.unref?.();
|
|
5001
|
+
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
5002
|
+
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
5003
|
+
if (options.signal?.aborted) callerAbort();
|
|
5004
|
+
let port;
|
|
5005
|
+
try {
|
|
5006
|
+
port = await startServer(server, target.port, target.host, controller.signal);
|
|
5007
|
+
} catch (error) {
|
|
5008
|
+
controller.abort(error);
|
|
5009
|
+
throw error;
|
|
5010
|
+
}
|
|
5011
|
+
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
5012
|
+
return {
|
|
5013
|
+
redirectUri,
|
|
5014
|
+
async waitForCode(authorizationUrl) {
|
|
5015
|
+
controller.signal.throwIfAborted();
|
|
5016
|
+
if (used) throw new Error("OAuth authorization session has already been used");
|
|
5017
|
+
used = true;
|
|
5018
|
+
try {
|
|
5019
|
+
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
5020
|
+
} finally {
|
|
5021
|
+
clearTimeout(timer);
|
|
5022
|
+
}
|
|
5023
|
+
},
|
|
5024
|
+
close() {
|
|
5025
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
5026
|
+
}
|
|
5027
|
+
};
|
|
5028
|
+
}
|
|
5029
|
+
function createHostedAuthorizationSession(options, timeoutMs) {
|
|
5030
|
+
validateAuthorizationRedirect(options);
|
|
5031
|
+
const redirectUri = options.redirectUri, controller = new AbortController();
|
|
5032
|
+
let used = false;
|
|
5033
|
+
const aborted = () => controller.abort(options.signal?.reason);
|
|
5034
|
+
const timer = timeoutMs === Infinity ? void 0 : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
5035
|
+
timer?.unref?.();
|
|
5036
|
+
const cleanup = () => {
|
|
5037
|
+
clearTimeout(timer);
|
|
5038
|
+
options.signal?.removeEventListener("abort", aborted);
|
|
5039
|
+
};
|
|
5040
|
+
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
5041
|
+
options.signal?.addEventListener("abort", aborted, { once: true });
|
|
5042
|
+
if (options.signal?.aborted) aborted();
|
|
5043
|
+
return {
|
|
5044
|
+
redirectUri,
|
|
5045
|
+
async waitForCode(authorizationUrl) {
|
|
5046
|
+
controller.signal.throwIfAborted();
|
|
5047
|
+
if (used) throw new Error("OAuth authorization session has already been used");
|
|
5048
|
+
used = true;
|
|
5049
|
+
try {
|
|
5050
|
+
const authorization = new URL(authorizationUrl);
|
|
5051
|
+
if (authorization.searchParams.getAll("redirect_uri").length !== 1 || authorization.searchParams.get("redirect_uri") !== redirectUri)
|
|
5052
|
+
throw new Error("OAuth authorization redirect mismatch");
|
|
5053
|
+
const callback = await new Promise((resolve, reject) => {
|
|
5054
|
+
const onAbort = () => reject(controller.signal.reason);
|
|
5055
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
5056
|
+
void Promise.resolve().then(() => {
|
|
5057
|
+
controller.signal.throwIfAborted();
|
|
5058
|
+
return options.waitForCallback({ authorizationUrl, redirectUri, signal: controller.signal });
|
|
5059
|
+
}).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
|
|
5060
|
+
});
|
|
5061
|
+
controller.signal.throwIfAborted();
|
|
5062
|
+
const url = new URL(callback), base = new URL(redirectUri);
|
|
5063
|
+
if (url.origin !== base.origin || url.pathname !== base.pathname || url.username || url.password || url.href.includes("#") || [...callback].some((char) => char.codePointAt(0) <= 32)) throw new Error("OAuth callback redirect mismatch");
|
|
5064
|
+
const params = readAuthorizationCallbackParameters(url);
|
|
5065
|
+
for (const name of oauthCallbackParameters) url.searchParams.delete(name);
|
|
5066
|
+
if (url.searchParams.toString() !== base.searchParams.toString()) throw new Error("OAuth callback redirect mismatch");
|
|
5067
|
+
const expected = readExpectedAuthorizationCallback(authorizationUrl);
|
|
5068
|
+
if (expected.state === null) throw new Error("OAuth authorization missing state");
|
|
5069
|
+
validateAuthorizationCallbackBinding(params, expected);
|
|
5070
|
+
if (params.error !== null) throw new OAuthAuthorizationError(params.error, params.errorDescription ?? params.error);
|
|
5071
|
+
return validateAuthorizationCallbackParameters(params, expected);
|
|
5072
|
+
} finally {
|
|
5073
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
5074
|
+
}
|
|
5075
|
+
},
|
|
5076
|
+
close() {
|
|
5077
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
5078
|
+
}
|
|
5079
|
+
};
|
|
5080
|
+
}
|
|
5081
|
+
async function startServer(server, port, host, signal) {
|
|
5082
|
+
signal.throwIfAborted();
|
|
5083
|
+
return new Promise((resolve, reject) => {
|
|
5084
|
+
const cleanup = () => {
|
|
5085
|
+
server.off("error", handleError);
|
|
5086
|
+
signal.removeEventListener("abort", aborted);
|
|
5087
|
+
};
|
|
5088
|
+
const handleError = (error) => {
|
|
5089
|
+
cleanup();
|
|
5090
|
+
reject(error);
|
|
5091
|
+
};
|
|
5092
|
+
const aborted = () => {
|
|
5093
|
+
cleanup();
|
|
5094
|
+
reject(signal.reason);
|
|
5095
|
+
};
|
|
5096
|
+
server.once("error", handleError);
|
|
5097
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
5098
|
+
try {
|
|
5099
|
+
server.listen(port, host, () => {
|
|
5100
|
+
cleanup();
|
|
5101
|
+
if (signal.aborted) {
|
|
5102
|
+
server.close();
|
|
5103
|
+
reject(signal.reason);
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
5106
|
+
const address = server.address();
|
|
5107
|
+
if (address === null || typeof address === "string") {
|
|
5108
|
+
reject(new Error("OAuth listener has no TCP address"));
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5111
|
+
resolve(address.port);
|
|
5112
|
+
});
|
|
5113
|
+
} catch (error) {
|
|
5114
|
+
cleanup();
|
|
5115
|
+
reject(error);
|
|
5116
|
+
}
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5119
|
+
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
5120
|
+
signal.throwIfAborted();
|
|
5121
|
+
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
5122
|
+
return new Promise((resolve, reject) => {
|
|
5123
|
+
let settled = false;
|
|
5124
|
+
const settle = (fn) => {
|
|
5125
|
+
if (settled) return;
|
|
5126
|
+
settled = true;
|
|
5127
|
+
server.off("request", request);
|
|
5128
|
+
signal.removeEventListener("abort", aborted);
|
|
5129
|
+
fn();
|
|
5130
|
+
};
|
|
5131
|
+
const aborted = () => settle(() => reject(signal.reason));
|
|
5132
|
+
const request = (req, res) => {
|
|
5133
|
+
let url;
|
|
5134
|
+
try {
|
|
5135
|
+
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
5136
|
+
} catch {
|
|
5137
|
+
res.writeHead(400);
|
|
5138
|
+
res.end("Invalid callback URL");
|
|
5139
|
+
return;
|
|
5140
|
+
}
|
|
5141
|
+
if (url.pathname !== callbackPath) {
|
|
5142
|
+
res.writeHead(404);
|
|
5143
|
+
res.end("Not found");
|
|
5144
|
+
return;
|
|
5145
|
+
}
|
|
5146
|
+
try {
|
|
5147
|
+
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
5148
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
5149
|
+
if (callbackParameters.error !== null)
|
|
5150
|
+
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
5151
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
5152
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
5153
|
+
res.end(buildSuccessPage(options.landingPage));
|
|
5154
|
+
settle(() => resolve(code));
|
|
5155
|
+
} catch (error) {
|
|
5156
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
5157
|
+
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
5158
|
+
settle(() => reject(error));
|
|
5159
|
+
}
|
|
5160
|
+
};
|
|
5161
|
+
server.on("request", request);
|
|
5162
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
5163
|
+
if (options.readLine !== void 0) {
|
|
5164
|
+
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
5165
|
+
if (settled) return;
|
|
5166
|
+
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
5167
|
+
if (callbackParameters === null) throw new Error("OAuth callback missing authorization code");
|
|
5168
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
5169
|
+
if (callbackParameters.error !== null)
|
|
5170
|
+
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
5171
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
5172
|
+
settle(() => resolve(code));
|
|
5173
|
+
}).catch((error) => settle(() => reject(error)));
|
|
5174
|
+
}
|
|
5175
|
+
if (options.openBrowser !== void 0) {
|
|
5176
|
+
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
5177
|
+
}
|
|
5178
|
+
});
|
|
5179
|
+
}
|
|
5180
|
+
function extractCallbackParametersFromInput(input) {
|
|
5181
|
+
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
5182
|
+
if (trimmed.length === 0) {
|
|
5183
|
+
return null;
|
|
5184
|
+
}
|
|
5185
|
+
let url;
|
|
5186
|
+
try {
|
|
5187
|
+
url = new URL(trimmed);
|
|
5188
|
+
} catch {
|
|
5189
|
+
return {
|
|
5190
|
+
code: trimmed,
|
|
5191
|
+
error: null,
|
|
5192
|
+
errorDescription: null,
|
|
5193
|
+
state: null,
|
|
5194
|
+
iss: null
|
|
5195
|
+
};
|
|
5196
|
+
}
|
|
5197
|
+
return readAuthorizationCallbackParameters(url);
|
|
5198
|
+
}
|
|
5199
|
+
function readAuthorizationCallbackParameters(url) {
|
|
5200
|
+
for (const parameter of oauthCallbackParameters) {
|
|
5201
|
+
if (url.searchParams.getAll(parameter).length > 1)
|
|
5202
|
+
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
5203
|
+
}
|
|
5204
|
+
return {
|
|
5205
|
+
code: url.searchParams.get("code"),
|
|
5206
|
+
error: url.searchParams.get("error"),
|
|
5207
|
+
errorDescription: url.searchParams.get("error_description"),
|
|
5208
|
+
state: url.searchParams.get("state"),
|
|
5209
|
+
iss: url.searchParams.get("iss")
|
|
5210
|
+
};
|
|
5211
|
+
}
|
|
5212
|
+
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
5213
|
+
const url = new URL(authorizationUrl);
|
|
5214
|
+
const state = url.searchParams.get("state");
|
|
5215
|
+
const parsedState = parseAuthorizationState(state);
|
|
5216
|
+
return {
|
|
5217
|
+
state,
|
|
5218
|
+
issuer: parsedState?.issuer ?? null,
|
|
5219
|
+
requireIssuer: parsedState?.requireIssuer ?? false
|
|
5220
|
+
};
|
|
5221
|
+
}
|
|
5222
|
+
function validateAuthorizationCallbackParameters(callback, expected) {
|
|
5223
|
+
validateAuthorizationCallbackBinding(callback, expected);
|
|
5224
|
+
if (callback.code === null || callback.code.length === 0) {
|
|
5225
|
+
throw new Error("OAuth callback missing authorization code");
|
|
5226
|
+
}
|
|
5227
|
+
return callback.code;
|
|
5228
|
+
}
|
|
5229
|
+
function validateAuthorizationCallbackBinding(callback, expected) {
|
|
5230
|
+
if (expected.state !== null) {
|
|
5231
|
+
if (callback.state === null || callback.state.length === 0) {
|
|
5232
|
+
throw new Error("OAuth callback missing state");
|
|
5233
|
+
}
|
|
5234
|
+
if (callback.state !== expected.state) {
|
|
5235
|
+
throw new Error("OAuth callback state mismatch");
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
if (expected.requireIssuer) {
|
|
5239
|
+
if (callback.iss === null || callback.iss.length === 0) {
|
|
5240
|
+
throw new Error("OAuth callback missing issuer");
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
if (callback.iss !== null && callback.iss.length > 0 && expected.issuer !== null && callback.iss !== expected.issuer) {
|
|
5244
|
+
throw new Error("OAuth callback issuer mismatch");
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
function escapeHtml(text) {
|
|
5248
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
5249
|
+
}
|
|
5250
|
+
function buildSuccessPage(landingPage) {
|
|
5251
|
+
const title = landingPage?.title ?? "Connected";
|
|
5252
|
+
const body = landingPage?.body ?? "You can close this tab and return to your terminal.";
|
|
5253
|
+
return [
|
|
5254
|
+
"<!DOCTYPE html>",
|
|
5255
|
+
`<html><head><meta charset=utf-8><title>${escapeHtml(title)}</title></head>`,
|
|
5256
|
+
'<body style="font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0">',
|
|
5257
|
+
'<div style="text-align:center">',
|
|
5258
|
+
`<h1>${escapeHtml(title)}</h1>`,
|
|
5259
|
+
`<p style="color:#666">${escapeHtml(body)}</p>`,
|
|
5260
|
+
"</div></body></html>"
|
|
5261
|
+
].join("");
|
|
5262
|
+
}
|
|
5263
|
+
|
|
5258
5264
|
// packages/mcp-oauth/src/client/pkce.ts
|
|
5259
5265
|
import crypto4 from "node:crypto";
|
|
5260
5266
|
function generateCodeVerifier() {
|
|
@@ -5503,8 +5509,8 @@ function normalizeBearerTokenType(value) {
|
|
|
5503
5509
|
// packages/mcp-oauth/src/client/session-transaction.ts
|
|
5504
5510
|
var queues = /* @__PURE__ */ new WeakMap();
|
|
5505
5511
|
async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
|
|
5506
|
-
const timeoutMs = options.timeoutMs ??
|
|
5507
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
5512
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
5513
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
5508
5514
|
throw new Error("sessionLockTimeoutMs must be an integer from 1 to 2147483647 milliseconds");
|
|
5509
5515
|
options.signal?.throwIfAborted();
|
|
5510
5516
|
const started = performance.now();
|
|
@@ -5527,7 +5533,7 @@ async function withOAuthSessionTransaction(store, resource, operation, options =
|
|
|
5527
5533
|
});
|
|
5528
5534
|
const abort = () => rejectWait(options.signal?.reason);
|
|
5529
5535
|
try {
|
|
5530
|
-
timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
|
|
5536
|
+
if (timeoutMs !== Infinity) timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
|
|
5531
5537
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
5532
5538
|
if (options.signal?.aborted) abort();
|
|
5533
5539
|
await waiting;
|
|
@@ -6418,15 +6424,15 @@ import {
|
|
|
6418
6424
|
|
|
6419
6425
|
// packages/tiny-stdio-mcp-server/src/headers.ts
|
|
6420
6426
|
function encodeHeaderValue(value) {
|
|
6421
|
-
const bytes =
|
|
6422
|
-
if (
|
|
6427
|
+
const bytes = new TextEncoder().encode(value);
|
|
6428
|
+
if (new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes) !== value)
|
|
6423
6429
|
throw new Error("MCP header value must contain valid Unicode");
|
|
6424
6430
|
const sentinel = value.startsWith("=?base64?") && value.endsWith("?=");
|
|
6425
6431
|
const unsafe = value.trim() !== value || [...value].some((character) => {
|
|
6426
6432
|
const code = character.charCodeAt(0);
|
|
6427
6433
|
return code !== 9 && (code < 32 || code > 126);
|
|
6428
6434
|
});
|
|
6429
|
-
return sentinel || unsafe ? `=?base64?${bytes.
|
|
6435
|
+
return sentinel || unsafe ? `=?base64?${btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""))}?=` : value;
|
|
6430
6436
|
}
|
|
6431
6437
|
var schemaMaps = /* @__PURE__ */ new Set([
|
|
6432
6438
|
"properties",
|
|
@@ -8154,7 +8160,7 @@ var HttpTransport = class {
|
|
|
8154
8160
|
if (this.legacyProtocolVersion !== void 0) headers.set("MCP-Protocol-Version", this.legacyProtocolVersion);
|
|
8155
8161
|
if (this.lastEventId !== void 0) {
|
|
8156
8162
|
if (this.lastEventId === "") headers.delete("Last-Event-ID");
|
|
8157
|
-
else headers.set("Last-Event-ID",
|
|
8163
|
+
else headers.set("Last-Event-ID", Array.from(new TextEncoder().encode(this.lastEventId), (byte) => String.fromCharCode(byte)).join(""));
|
|
8158
8164
|
}
|
|
8159
8165
|
return this.authorizeRequestHeaders(headers, signal);
|
|
8160
8166
|
}
|
|
@@ -8583,7 +8589,7 @@ async function* readLines(stream, maxLineBytes = 16 * 1024 * 1024) {
|
|
|
8583
8589
|
let bytes = 0;
|
|
8584
8590
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
8585
8591
|
const append = (part) => {
|
|
8586
|
-
bytes +=
|
|
8592
|
+
bytes += new TextEncoder().encode(part).byteLength;
|
|
8587
8593
|
if (bytes > maxLineBytes) throw new Error(`Stdio input line byte limit exceeded (${maxLineBytes} bytes)`);
|
|
8588
8594
|
if (part.length > 0) parts.push(part);
|
|
8589
8595
|
};
|
|
@@ -8644,7 +8650,7 @@ var SseParser = class {
|
|
|
8644
8650
|
else if (index === this.buffer.length - 1) this.skipLf = true;
|
|
8645
8651
|
}
|
|
8646
8652
|
start = index + 1;
|
|
8647
|
-
if (
|
|
8653
|
+
if (new TextEncoder().encode(line).byteLength > this.maxEventBytes)
|
|
8648
8654
|
throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
|
|
8649
8655
|
this.consumeLine(line, messages);
|
|
8650
8656
|
this.assertEventSize("");
|
|
@@ -8676,7 +8682,7 @@ var SseParser = class {
|
|
|
8676
8682
|
return;
|
|
8677
8683
|
}
|
|
8678
8684
|
if (field === "data") {
|
|
8679
|
-
this.dataBytes +=
|
|
8685
|
+
this.dataBytes += new TextEncoder().encode(value).byteLength + (this.dataLines.length > 0 ? 1 : 0);
|
|
8680
8686
|
this.dataLines.push(value);
|
|
8681
8687
|
return;
|
|
8682
8688
|
}
|
|
@@ -8715,7 +8721,7 @@ var SseParser = class {
|
|
|
8715
8721
|
this.dataBytes = 0;
|
|
8716
8722
|
}
|
|
8717
8723
|
assertEventSize(partialLine) {
|
|
8718
|
-
if (this.dataBytes +
|
|
8724
|
+
if (this.dataBytes + new TextEncoder().encode(this.eventType ?? "").byteLength + new TextEncoder().encode(this.eventId).byteLength + new TextEncoder().encode(partialLine).byteLength > this.maxEventBytes)
|
|
8719
8725
|
throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
|
|
8720
8726
|
}
|
|
8721
8727
|
};
|