@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.
Files changed (63) hide show
  1. package/dist/adapters/ai/index.cjs +12 -2
  2. package/dist/adapters/ai/index.cjs.map +1 -1
  3. package/dist/adapters/ai/index.js +12 -2
  4. package/dist/adapters/ai/index.js.map +1 -1
  5. package/dist/adapters/cloudflare/index.cjs +13 -0
  6. package/dist/adapters/cloudflare/index.cjs.map +1 -1
  7. package/dist/adapters/cloudflare/index.js +13 -0
  8. package/dist/adapters/cloudflare/index.js.map +1 -1
  9. package/dist/adapters/cloudflare/react.cjs +12 -2
  10. package/dist/adapters/cloudflare/react.cjs.map +1 -1
  11. package/dist/adapters/cloudflare/react.js +12 -2
  12. package/dist/adapters/cloudflare/react.js.map +1 -1
  13. package/dist/adapters/react/index.cjs +12 -2
  14. package/dist/adapters/react/index.cjs.map +1 -1
  15. package/dist/adapters/react/index.js +12 -2
  16. package/dist/adapters/react/index.js.map +1 -1
  17. package/dist/client/index.cjs +108 -69
  18. package/dist/client/index.cjs.map +1 -1
  19. package/dist/client/index.d.cts +2 -0
  20. package/dist/client/index.d.ts +2 -0
  21. package/dist/client/index.js +109 -71
  22. package/dist/client/index.js.map +1 -1
  23. package/dist/errors.cjs +78 -0
  24. package/dist/errors.cjs.map +1 -1
  25. package/dist/errors.d.cts +7 -1
  26. package/dist/errors.d.ts +7 -1
  27. package/dist/errors.js +78 -1
  28. package/dist/errors.js.map +1 -1
  29. package/dist/index.cjs +151 -87
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +152 -88
  34. package/dist/index.js.map +1 -1
  35. package/dist/{kontext-CgIBANFo.d.cts → kontext-CBPuE-hq.d.cts} +3 -0
  36. package/dist/{kontext-CgIBANFo.d.ts → kontext-CBPuE-hq.d.ts} +3 -0
  37. package/dist/management/index.cjs +15 -0
  38. package/dist/management/index.cjs.map +1 -1
  39. package/dist/management/index.d.cts +2 -2
  40. package/dist/management/index.d.ts +2 -2
  41. package/dist/management/index.js +15 -1
  42. package/dist/management/index.js.map +1 -1
  43. package/dist/mcp/index.cjs +32 -3
  44. package/dist/mcp/index.cjs.map +1 -1
  45. package/dist/mcp/index.d.cts +7 -1
  46. package/dist/mcp/index.d.ts +7 -1
  47. package/dist/mcp/index.js +33 -4
  48. package/dist/mcp/index.js.map +1 -1
  49. package/dist/oauth/index.cjs +12 -2
  50. package/dist/oauth/index.cjs.map +1 -1
  51. package/dist/oauth/index.d.cts +1 -1
  52. package/dist/oauth/index.d.ts +1 -1
  53. package/dist/oauth/index.js +12 -2
  54. package/dist/oauth/index.js.map +1 -1
  55. package/dist/server/index.cjs +55 -20
  56. package/dist/server/index.cjs.map +1 -1
  57. package/dist/server/index.d.cts +2 -2
  58. package/dist/server/index.d.ts +2 -2
  59. package/dist/server/index.js +56 -21
  60. package/dist/server/index.js.map +1 -1
  61. package/dist/{types-CzhnlJHW.d.cts → types-DicGI7ix.d.cts} +23 -1
  62. package/dist/{types-CzhnlJHW.d.ts → types-DicGI7ix.d.ts} +23 -1
  63. 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 connectType = item.connectType === "credentials" || item.connectType === "oauth" || item.connectType === "none" ? item.connectType : category === "internal_mcp_credentials" ? "credentials" : item.authMode === "oauth" ? "oauth" : "none";
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
- if (isKontextError(err)) {
1427
- if (!contextMeta) {
1428
- return err;
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
- return new KontextError(String(err), "kontext_unknown_error", {
1453
- meta: mergeMeta()
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
- // require()
3135
- // ===========================================================================
3136
- /**
3137
- * Exchange a user's access token for an integration credential.
3138
- *
3139
- * @param integration - Integration name (e.g., "github")
3140
- * @param token - The user's Bearer token (from `authInfo.token`)
3141
- * @returns Integration credential with `accessToken` and `authorization` header
3142
- *
3143
- * @throws {IntegrationConnectionRequiredError} User hasn't connected this integration
3144
- * @throws {OAuthError} Token exchange failed
3145
- */
3146
- async require(integration, token) {
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 = `${integration}\0${token}`;
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(exchangeConfig, token, integration);
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
- token,
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