@kontext-dev/js-sdk 0.3.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/ai/index.cjs +12 -2
- package/dist/adapters/ai/index.cjs.map +1 -1
- package/dist/adapters/ai/index.js +12 -2
- package/dist/adapters/ai/index.js.map +1 -1
- package/dist/adapters/cloudflare/index.cjs +13 -0
- package/dist/adapters/cloudflare/index.cjs.map +1 -1
- package/dist/adapters/cloudflare/index.js +13 -0
- package/dist/adapters/cloudflare/index.js.map +1 -1
- package/dist/adapters/cloudflare/react.cjs +12 -2
- package/dist/adapters/cloudflare/react.cjs.map +1 -1
- package/dist/adapters/cloudflare/react.js +12 -2
- package/dist/adapters/cloudflare/react.js.map +1 -1
- package/dist/adapters/react/index.cjs +12 -2
- package/dist/adapters/react/index.cjs.map +1 -1
- package/dist/adapters/react/index.js +12 -2
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/client/index.cjs +108 -69
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +2 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +109 -71
- package/dist/client/index.js.map +1 -1
- package/dist/errors.cjs +78 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +7 -1
- package/dist/errors.d.ts +7 -1
- package/dist/errors.js +78 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +151 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +152 -88
- package/dist/index.js.map +1 -1
- package/dist/{kontext-CgIBANFo.d.cts → kontext-CBPuE-hq.d.cts} +3 -0
- package/dist/{kontext-CgIBANFo.d.ts → kontext-CBPuE-hq.d.ts} +3 -0
- package/dist/management/index.cjs +15 -0
- package/dist/management/index.cjs.map +1 -1
- package/dist/management/index.d.cts +2 -2
- package/dist/management/index.d.ts +2 -2
- package/dist/management/index.js +15 -1
- package/dist/management/index.js.map +1 -1
- package/dist/mcp/index.cjs +32 -3
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.cts +7 -1
- package/dist/mcp/index.d.ts +7 -1
- package/dist/mcp/index.js +33 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/oauth/index.cjs +12 -2
- package/dist/oauth/index.cjs.map +1 -1
- package/dist/oauth/index.d.cts +1 -1
- package/dist/oauth/index.d.ts +1 -1
- package/dist/oauth/index.js +12 -2
- package/dist/oauth/index.js.map +1 -1
- package/dist/server/index.cjs +55 -20
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +56 -21
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CzhnlJHW.d.cts → types-DicGI7ix.d.cts} +23 -1
- package/dist/{types-CzhnlJHW.d.ts → types-DicGI7ix.d.ts} +23 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -76,8 +76,6 @@ var StorageKeys = {
|
|
|
76
76
|
function resourceTokenKey(resource) {
|
|
77
77
|
return `${StorageKeys.RESOURCE_TOKENS}:${resource}`;
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
// src/errors.ts
|
|
81
79
|
var KontextError = class extends Error {
|
|
82
80
|
/** Brand field for type narrowing without instanceof */
|
|
83
81
|
kontextError = true;
|
|
@@ -210,12 +208,20 @@ function isNetworkError(err) {
|
|
|
210
208
|
if (typeof causeCode === "string" && NETWORK_ERROR_CODES.has(causeCode))
|
|
211
209
|
return true;
|
|
212
210
|
}
|
|
211
|
+
if (err.name === "TypeError") {
|
|
212
|
+
const msg = err.message.toLowerCase();
|
|
213
|
+
if (msg === "failed to fetch" || msg === "load failed" || msg.includes("networkerror")) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
213
217
|
return false;
|
|
214
218
|
}
|
|
215
219
|
function isUnauthorizedError(err) {
|
|
216
220
|
const props = errorProps(err);
|
|
217
221
|
if (props.statusCode === 401 || props.status === 401) return true;
|
|
222
|
+
if (props.code === 401) return true;
|
|
218
223
|
if (err.name === "UnauthorizedError") return true;
|
|
224
|
+
if (err.constructor?.name === "UnauthorizedError") return true;
|
|
219
225
|
if (err.message === "Unauthorized") return true;
|
|
220
226
|
return false;
|
|
221
227
|
}
|
|
@@ -274,6 +280,73 @@ function parseHttpError(statusCode, body) {
|
|
|
274
280
|
});
|
|
275
281
|
}
|
|
276
282
|
}
|
|
283
|
+
var MCP_CODE_MAP = {
|
|
284
|
+
[types_js.ErrorCode.ParseError]: { code: "kontext_mcp_parse_error" },
|
|
285
|
+
[types_js.ErrorCode.InvalidRequest]: { code: "kontext_mcp_invalid_request" },
|
|
286
|
+
[types_js.ErrorCode.MethodNotFound]: { code: "kontext_mcp_method_not_found" },
|
|
287
|
+
[types_js.ErrorCode.InvalidParams]: { code: "kontext_mcp_invalid_params" },
|
|
288
|
+
[types_js.ErrorCode.InternalError]: {
|
|
289
|
+
code: "kontext_mcp_internal_error",
|
|
290
|
+
statusCode: 500
|
|
291
|
+
},
|
|
292
|
+
[types_js.ErrorCode.RequestTimeout]: {
|
|
293
|
+
code: "kontext_mcp_session_expired",
|
|
294
|
+
statusCode: 401
|
|
295
|
+
},
|
|
296
|
+
[types_js.ErrorCode.ConnectionClosed]: { code: "kontext_mcp_session_error" }
|
|
297
|
+
};
|
|
298
|
+
function translateError(err) {
|
|
299
|
+
if (isKontextError(err)) return err;
|
|
300
|
+
if (!(err instanceof Error)) {
|
|
301
|
+
return new KontextError(String(err), "kontext_unknown_error");
|
|
302
|
+
}
|
|
303
|
+
const props = err;
|
|
304
|
+
if (props.code === types_js.ErrorCode.UrlElicitationRequired) {
|
|
305
|
+
const elicitations = props.elicitations ?? props.data?.elicitations;
|
|
306
|
+
const elicitation = elicitations?.[0];
|
|
307
|
+
return new IntegrationConnectionRequiredError(
|
|
308
|
+
elicitation?.integrationId ?? "unknown",
|
|
309
|
+
{
|
|
310
|
+
integrationName: elicitation?.integrationName,
|
|
311
|
+
connectUrl: elicitation?.url,
|
|
312
|
+
message: elicitation?.message,
|
|
313
|
+
cause: err
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (typeof props.code === "number" && props.code < 0) {
|
|
318
|
+
const entry = MCP_CODE_MAP[props.code];
|
|
319
|
+
if (entry) {
|
|
320
|
+
return new KontextError(err.message, entry.code, {
|
|
321
|
+
statusCode: entry.statusCode,
|
|
322
|
+
cause: err
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return new KontextError(err.message, "kontext_mcp_error", {
|
|
326
|
+
cause: err,
|
|
327
|
+
meta: { mcpCode: props.code }
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
const statusCode = props.statusCode ?? props.status ?? (typeof props.code === "number" && props.code >= 400 && props.code < 600 ? props.code : void 0);
|
|
331
|
+
if (typeof statusCode === "number" && statusCode >= 400) {
|
|
332
|
+
if (statusCode === 401) {
|
|
333
|
+
return new AuthorizationRequiredError(err.message, { cause: err });
|
|
334
|
+
}
|
|
335
|
+
return new KontextError(err.message, "kontext_server_error", {
|
|
336
|
+
statusCode,
|
|
337
|
+
cause: err
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (isUnauthorizedError(err)) {
|
|
341
|
+
return new AuthorizationRequiredError(err.message, { cause: err });
|
|
342
|
+
}
|
|
343
|
+
if (isNetworkError(err)) {
|
|
344
|
+
return new NetworkError(err.message, { cause: err });
|
|
345
|
+
}
|
|
346
|
+
return new KontextError(err.message, "kontext_unknown_error", {
|
|
347
|
+
cause: err
|
|
348
|
+
});
|
|
349
|
+
}
|
|
277
350
|
|
|
278
351
|
// src/oauth/provider.ts
|
|
279
352
|
var KontextOAuthProvider = class {
|
|
@@ -711,7 +784,22 @@ var KontextMcp = class {
|
|
|
711
784
|
const url = typeof item.url === "string" ? item.url : "";
|
|
712
785
|
if (!id || !url) return null;
|
|
713
786
|
const category = item.category === "internal_mcp_credentials" ? "internal_mcp_credentials" : "gateway_remote_mcp";
|
|
714
|
-
const
|
|
787
|
+
const rawConnectType = item.connectType;
|
|
788
|
+
if (typeof rawConnectType !== "string") {
|
|
789
|
+
throw new KontextError(
|
|
790
|
+
"Runtime integration connectType is required in API response.",
|
|
791
|
+
"kontext_runtime_integrations_invalid_response",
|
|
792
|
+
{ meta: { integrationId: id, connectType: rawConnectType } }
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
const connectType = rawConnectType === "credentials" || rawConnectType === "oauth" || rawConnectType === "user_token" || rawConnectType === "none" ? rawConnectType : null;
|
|
796
|
+
if (!connectType) {
|
|
797
|
+
throw new KontextError(
|
|
798
|
+
`Unknown runtime integration connectType "${rawConnectType}".`,
|
|
799
|
+
"kontext_runtime_integrations_invalid_response",
|
|
800
|
+
{ meta: { integrationId: id, connectType: rawConnectType } }
|
|
801
|
+
);
|
|
802
|
+
}
|
|
715
803
|
const rawConnection = item.connection && typeof item.connection === "object" ? item.connection : void 0;
|
|
716
804
|
const connected = rawConnection && typeof rawConnection.connected === "boolean" ? rawConnection.connected : false;
|
|
717
805
|
const status = rawConnection?.status === "connected" ? "connected" : "disconnected";
|
|
@@ -722,6 +810,9 @@ var KontextMcp = class {
|
|
|
722
810
|
category,
|
|
723
811
|
connectType,
|
|
724
812
|
authMode: item.authMode === "oauth" || item.authMode === "user_token" || item.authMode === "server_token" || item.authMode === "none" ? item.authMode : void 0,
|
|
813
|
+
tokenLabel: typeof item.tokenLabel === "string" ? item.tokenLabel : void 0,
|
|
814
|
+
tokenHelpUrl: typeof item.tokenHelpUrl === "string" ? item.tokenHelpUrl : void 0,
|
|
815
|
+
tokenPlaceholder: typeof item.tokenPlaceholder === "string" ? item.tokenPlaceholder : void 0,
|
|
725
816
|
credentialSchema: item.credentialSchema,
|
|
726
817
|
requiresOauth: typeof item.requiresOauth === "boolean" ? item.requiresOauth : void 0,
|
|
727
818
|
connection: rawConnection ? {
|
|
@@ -1423,35 +1514,21 @@ async function withTransientRetry(operation, maxRetries = 1) {
|
|
|
1423
1514
|
function toKontextError(err, context) {
|
|
1424
1515
|
const contextMeta = context ? { ...context } : void 0;
|
|
1425
1516
|
const mergeMeta = (base) => contextMeta ? { ...base ?? {}, ...contextMeta } : base ?? {};
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
const cloned = Object.create(Object.getPrototypeOf(err));
|
|
1431
|
-
Object.defineProperties(cloned, Object.getOwnPropertyDescriptors(err));
|
|
1432
|
-
Object.defineProperty(cloned, "meta", {
|
|
1433
|
-
value: mergeMeta(err.meta),
|
|
1434
|
-
enumerable: true,
|
|
1435
|
-
writable: true,
|
|
1436
|
-
configurable: true
|
|
1437
|
-
});
|
|
1438
|
-
return cloned;
|
|
1439
|
-
}
|
|
1440
|
-
if (err instanceof Error) {
|
|
1441
|
-
if (isUnauthorizedError(err)) {
|
|
1442
|
-
return new AuthorizationRequiredError(err.message, {
|
|
1443
|
-
meta: mergeMeta(),
|
|
1444
|
-
cause: err
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
return new KontextError(err.message, "kontext_unknown_error", {
|
|
1448
|
-
meta: mergeMeta(),
|
|
1449
|
-
cause: err
|
|
1450
|
-
});
|
|
1517
|
+
const translated = translateError(err);
|
|
1518
|
+
if (!contextMeta) {
|
|
1519
|
+
return translated;
|
|
1451
1520
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1521
|
+
const cloned = Object.create(
|
|
1522
|
+
Object.getPrototypeOf(translated)
|
|
1523
|
+
);
|
|
1524
|
+
Object.defineProperties(cloned, Object.getOwnPropertyDescriptors(translated));
|
|
1525
|
+
Object.defineProperty(cloned, "meta", {
|
|
1526
|
+
value: mergeMeta(translated.meta),
|
|
1527
|
+
enumerable: true,
|
|
1528
|
+
writable: true,
|
|
1529
|
+
configurable: true
|
|
1454
1530
|
});
|
|
1531
|
+
return cloned;
|
|
1455
1532
|
}
|
|
1456
1533
|
function isAuthorizationRequired(err) {
|
|
1457
1534
|
if (err instanceof AuthorizationRequiredError) return true;
|
|
@@ -2165,45 +2242,6 @@ function extractTextContent(result) {
|
|
|
2165
2242
|
}
|
|
2166
2243
|
return JSON.stringify(result);
|
|
2167
2244
|
}
|
|
2168
|
-
function translateError(err) {
|
|
2169
|
-
if (isKontextError(err)) return err;
|
|
2170
|
-
if (!(err instanceof Error)) {
|
|
2171
|
-
return new KontextError(String(err), "kontext_unknown_error");
|
|
2172
|
-
}
|
|
2173
|
-
const props = err;
|
|
2174
|
-
if (props.code === -32042) {
|
|
2175
|
-
const elicitations = props.elicitations ?? props.data?.elicitations;
|
|
2176
|
-
const elicitation = elicitations?.[0];
|
|
2177
|
-
return new IntegrationConnectionRequiredError(
|
|
2178
|
-
elicitation?.integrationId ?? "unknown",
|
|
2179
|
-
{
|
|
2180
|
-
integrationName: elicitation?.integrationName,
|
|
2181
|
-
connectUrl: elicitation?.url,
|
|
2182
|
-
message: elicitation?.message,
|
|
2183
|
-
cause: err
|
|
2184
|
-
}
|
|
2185
|
-
);
|
|
2186
|
-
}
|
|
2187
|
-
const statusCode = props.statusCode ?? props.status;
|
|
2188
|
-
if (typeof statusCode === "number" && statusCode >= 400) {
|
|
2189
|
-
if (statusCode === 401) {
|
|
2190
|
-
return new AuthorizationRequiredError(err.message, { cause: err });
|
|
2191
|
-
}
|
|
2192
|
-
return new KontextError(err.message, "kontext_server_error", {
|
|
2193
|
-
statusCode,
|
|
2194
|
-
cause: err
|
|
2195
|
-
});
|
|
2196
|
-
}
|
|
2197
|
-
if (isUnauthorizedError(err)) {
|
|
2198
|
-
return new AuthorizationRequiredError(err.message, { cause: err });
|
|
2199
|
-
}
|
|
2200
|
-
if (isNetworkError(err)) {
|
|
2201
|
-
return new NetworkError(err.message, { cause: err });
|
|
2202
|
-
}
|
|
2203
|
-
return new KontextError(err.message, "kontext_unknown_error", {
|
|
2204
|
-
cause: err
|
|
2205
|
-
});
|
|
2206
|
-
}
|
|
2207
2245
|
function createSingleEndpointKontextClient(config) {
|
|
2208
2246
|
if (!config.clientId) {
|
|
2209
2247
|
throw new ConfigError(
|
|
@@ -2493,6 +2531,7 @@ function createKontextClient(config) {
|
|
|
2493
2531
|
// src/management/types.ts
|
|
2494
2532
|
var TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
2495
2533
|
var TOKEN_TYPE_ACCESS_TOKEN = "urn:ietf:params:oauth:token-type:access_token";
|
|
2534
|
+
var TOKEN_TYPE_USER_ID = "urn:kontext:user-id";
|
|
2496
2535
|
|
|
2497
2536
|
// src/oauth/token-exchange.ts
|
|
2498
2537
|
async function exchangeToken(config, subjectToken, resource, scope, subjectTokenType = TOKEN_TYPE_ACCESS_TOKEN) {
|
|
@@ -2972,7 +3011,7 @@ var Kontext = class _Kontext {
|
|
|
2972
3011
|
oauthMetadata = null;
|
|
2973
3012
|
metadataFetchedAt = 0;
|
|
2974
3013
|
metadataPromise = null;
|
|
2975
|
-
// Token exchange caching: keyed by `${integration}\0${subjectToken}`
|
|
3014
|
+
// Token exchange caching: keyed by `${mode}\0${integration}\0${subjectToken}`
|
|
2976
3015
|
credentialCache = /* @__PURE__ */ new Map();
|
|
2977
3016
|
resolvedCredentialCache = /* @__PURE__ */ new Map();
|
|
2978
3017
|
runtimeAuthCache = /* @__PURE__ */ new Map();
|
|
@@ -3130,23 +3169,28 @@ var Kontext = class _Kontext {
|
|
|
3130
3169
|
router.delete(mcpPath, mcpHandler.delete);
|
|
3131
3170
|
return router;
|
|
3132
3171
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3172
|
+
async require(integration, tokenOrOpts) {
|
|
3173
|
+
let isUserIdMode = false;
|
|
3174
|
+
let subjectToken = "";
|
|
3175
|
+
if (typeof tokenOrOpts === "string") {
|
|
3176
|
+
subjectToken = tokenOrOpts;
|
|
3177
|
+
} else if (tokenOrOpts !== null && typeof tokenOrOpts === "object" && typeof tokenOrOpts.userId === "string") {
|
|
3178
|
+
isUserIdMode = true;
|
|
3179
|
+
subjectToken = tokenOrOpts.userId.trim();
|
|
3180
|
+
if (!subjectToken) {
|
|
3181
|
+
throw new TypeError(
|
|
3182
|
+
"Kontext.require() expects a non-empty userId when called with { userId }."
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
} else {
|
|
3186
|
+
throw new TypeError(
|
|
3187
|
+
"Kontext.require() expects a token string or { userId: string }."
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
const subjectTokenType = isUserIdMode ? TOKEN_TYPE_USER_ID : void 0;
|
|
3147
3191
|
const now = Date.now();
|
|
3148
3192
|
this.evictExpiredCredentials(now);
|
|
3149
|
-
const cacheKey =
|
|
3193
|
+
const cacheKey = isUserIdMode ? `u\0${integration}\0${subjectToken}` : `t\0${integration}\0${subjectToken}`;
|
|
3150
3194
|
const cached = this.credentialCache.get(cacheKey);
|
|
3151
3195
|
if (cached && now < cached.expiresAt) {
|
|
3152
3196
|
this.credentialCache.delete(cacheKey);
|
|
@@ -3163,13 +3207,25 @@ var Kontext = class _Kontext {
|
|
|
3163
3207
|
};
|
|
3164
3208
|
let response;
|
|
3165
3209
|
try {
|
|
3166
|
-
response = await exchangeToken(
|
|
3210
|
+
response = await exchangeToken(
|
|
3211
|
+
exchangeConfig,
|
|
3212
|
+
subjectToken,
|
|
3213
|
+
integration,
|
|
3214
|
+
void 0,
|
|
3215
|
+
subjectTokenType
|
|
3216
|
+
);
|
|
3167
3217
|
} catch (err) {
|
|
3168
3218
|
if (err instanceof OAuthError) {
|
|
3169
3219
|
if (err.errorCode === "integration_required" || err.message.includes("not connected") || err.message.includes("expired") && err.message.includes("reconnect")) {
|
|
3170
3220
|
const integrationId = err.meta.integrationId || integration;
|
|
3221
|
+
if (isUserIdMode) {
|
|
3222
|
+
throw new IntegrationConnectionRequiredError(integrationId, {
|
|
3223
|
+
integrationName: err.meta.integrationName,
|
|
3224
|
+
message: err.message
|
|
3225
|
+
});
|
|
3226
|
+
}
|
|
3171
3227
|
const connectUrl = await this.fetchConnectUrl(
|
|
3172
|
-
|
|
3228
|
+
subjectToken,
|
|
3173
3229
|
integrationId,
|
|
3174
3230
|
exchangeConfig
|
|
3175
3231
|
);
|
|
@@ -3680,6 +3736,12 @@ var Kontext = class _Kontext {
|
|
|
3680
3736
|
// ===========================================================================
|
|
3681
3737
|
createAgentSession(userToken, mcpSessionId, metadata) {
|
|
3682
3738
|
if (!this.clientSecret || !userToken) return;
|
|
3739
|
+
if (!metadata?.authenticatedUserId) {
|
|
3740
|
+
console.warn(
|
|
3741
|
+
"[kontext:sessions] create skipped: missing authenticated user id"
|
|
3742
|
+
);
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3683
3745
|
const tokenIdentifier = crypto$1.createHash("sha256").update(userToken).digest("hex");
|
|
3684
3746
|
this.getServiceToken().then(
|
|
3685
3747
|
(token) => fetch(`${this.apiUrl}/api/v1/agent-sessions`, {
|
|
@@ -3690,6 +3752,7 @@ var Kontext = class _Kontext {
|
|
|
3690
3752
|
},
|
|
3691
3753
|
body: JSON.stringify({
|
|
3692
3754
|
tokenIdentifier,
|
|
3755
|
+
authenticatedUserId: metadata.authenticatedUserId,
|
|
3693
3756
|
clientSessionId: mcpSessionId,
|
|
3694
3757
|
hostname: metadata?.hostname,
|
|
3695
3758
|
userAgent: metadata?.userAgent,
|
|
@@ -3911,6 +3974,7 @@ var Kontext = class _Kontext {
|
|
|
3911
3974
|
status: "ok"
|
|
3912
3975
|
});
|
|
3913
3976
|
this.createAgentSession(authInfo?.token, sid, {
|
|
3977
|
+
authenticatedUserId: typeof authInfo?.extra?.sub === "string" ? authInfo.extra.sub : void 0,
|
|
3914
3978
|
hostname: req.headers["x-forwarded-for"],
|
|
3915
3979
|
userAgent: req.headers["user-agent"],
|
|
3916
3980
|
tokenExpiresAt: authInfo?.expiresAt
|