@mantyx/sdk 0.10.0 → 0.10.1

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/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as A2AToolRef, a as AgentSession, b as AgentSpecBase, c as AssistantDeltaEvent, d as AssistantMessageEvent, C as CancelledEvent, e as ClientCredentialsOptions, f as ClientCredentialsTokenSourceOptions, D as DEFAULT_BASE_URL, g as DEFAULT_OAUTH_BASE_URL, h as DEFAULT_REFRESH_SKEW_MS, i as DefineLocalA2AOptions, j as DefineLocalMcpOptions, k as DefineLocalToolOptions, E as ErrorEvent, l as ExchangeAuthorizationCodeOptions, L as LocalA2ATool, m as LocalHandlers, n as LocalMcpHttpTransport, o as LocalMcpServer, p as LocalMcpStdioTransport, q as LocalTool, r as LocalToolCallEvent, s as LocalToolResultInEvent, t as LoopDetectedEvent, u as LoopDetection, v as MantyxA2AOptions, w as MantyxAuthError, M as MantyxClient, x as MantyxClientOptions, y as MantyxError, z as MantyxMcpOptions, B as MantyxNetworkError, F as MantyxOAuthClient, G as MantyxOAuthClientOptions, H as MantyxOAuthError, I as MantyxParseError, J as MantyxPluginToolRef, K as MantyxRunError, N as MantyxRunErrorInit, O as MantyxScopeError, P as MantyxToolError, Q as MantyxToolRef, S as McpToolRef, U as ModelCatalog, V as ModelInfo, W as OAuthToken, X as OutputSchema, R as ReasoningLevel, Y as RefreshOptions, Z as RefreshTokenSourceOptions, _ as ResultEvent, $ as RevokeOptions, a0 as RunEvent, a1 as RunEventBase, a2 as RunResult, a3 as RunSpec, a4 as ServerToolResultEvent, a5 as SessionInfo, a6 as SessionSpec, a7 as ThinkingDeltaEvent, a8 as TokenRequestReason, a9 as TokenSource, aa as ToolBudget, ab as ToolBudgetExceededEvent, ac as ToolBudgets, T as ToolRef, ad as ZodLikeObject, ae as defineLocalA2A, af as defineLocalMcp, ag as defineLocalTool, ah as generatePkceVerifier, ai as isLocalA2ATool, aj as isLocalMcpServer, ak as isLocalTool, al as mantyxA2A, am as mantyxMcp, an as mantyxPluginTool, ao as mantyxTool, ap as parseRunOutput, aq as pkceChallenge } from './client-DHwh8MPj.cjs';
1
+ export { A as A2AToolRef, a as AgentSession, b as AgentSpecBase, c as AssistantDeltaEvent, d as AssistantMessageEvent, C as CancelledEvent, D as DEFAULT_BASE_URL, e as DEFAULT_OAUTH_BASE_URL, f as DEFAULT_REFRESH_SKEW_MS, g as DefineLocalA2AOptions, h as DefineLocalMcpOptions, i as DefineLocalToolOptions, E as ErrorEvent, L as LocalA2ATool, j as LocalHandlers, k as LocalMcpHttpTransport, l as LocalMcpServer, m as LocalMcpStdioTransport, n as LocalTool, o as LocalToolCallEvent, p as LocalToolResultInEvent, q as LoopDetectedEvent, r as LoopDetection, s as MantyxA2AOptions, t as MantyxAuthError, M as MantyxClient, u as MantyxClientOptions, v as MantyxError, w as MantyxMcpOptions, x as MantyxNetworkError, y as MantyxOAuthClient, z as MantyxOAuthClientOptions, B as MantyxOAuthError, F as MantyxParseError, G as MantyxPluginToolRef, H as MantyxRunError, I as MantyxRunErrorInit, J as MantyxScopeError, K as MantyxToolError, N as MantyxToolRef, O as McpToolRef, P as ModelCatalog, Q as ModelInfo, S as OAuthToken, U as OutputSchema, R as ReasoningLevel, V as RefreshOptions, W as RefreshTokenSourceOptions, X as ResultEvent, Y as RevokeOptions, Z as RunEvent, _ as RunEventBase, $ as RunResult, a0 as RunSpec, a1 as ServerToolResultEvent, a2 as SessionInfo, a3 as SessionSpec, a4 as ThinkingDeltaEvent, a5 as TokenRequestReason, a6 as TokenSource, a7 as ToolBudget, a8 as ToolBudgetExceededEvent, a9 as ToolBudgets, T as ToolRef, aa as ZodLikeObject, ab as defineLocalA2A, ac as defineLocalMcp, ad as defineLocalTool, ae as isLocalA2ATool, af as isLocalMcpServer, ag as isLocalTool, ah as mantyxA2A, ai as mantyxMcp, aj as mantyxPluginTool, ak as mantyxTool, al as parseRunOutput } from './client-CZUVldDx.cjs';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /**
@@ -52,6 +52,6 @@ declare function readSseStream(body: ReadableStream<Uint8Array> | null, opts?: S
52
52
  /**
53
53
  * Release version — synced from repo root VERSION (`npm run sync-version`).
54
54
  */
55
- declare const SDK_VERSION = "0.10.0";
55
+ declare const SDK_VERSION = "0.10.1";
56
56
 
57
57
  export { SDK_VERSION, type SseEvent, type SseStreamOptions, readSseStream, toToolParametersWire, zodToJsonSchema };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as A2AToolRef, a as AgentSession, b as AgentSpecBase, c as AssistantDeltaEvent, d as AssistantMessageEvent, C as CancelledEvent, e as ClientCredentialsOptions, f as ClientCredentialsTokenSourceOptions, D as DEFAULT_BASE_URL, g as DEFAULT_OAUTH_BASE_URL, h as DEFAULT_REFRESH_SKEW_MS, i as DefineLocalA2AOptions, j as DefineLocalMcpOptions, k as DefineLocalToolOptions, E as ErrorEvent, l as ExchangeAuthorizationCodeOptions, L as LocalA2ATool, m as LocalHandlers, n as LocalMcpHttpTransport, o as LocalMcpServer, p as LocalMcpStdioTransport, q as LocalTool, r as LocalToolCallEvent, s as LocalToolResultInEvent, t as LoopDetectedEvent, u as LoopDetection, v as MantyxA2AOptions, w as MantyxAuthError, M as MantyxClient, x as MantyxClientOptions, y as MantyxError, z as MantyxMcpOptions, B as MantyxNetworkError, F as MantyxOAuthClient, G as MantyxOAuthClientOptions, H as MantyxOAuthError, I as MantyxParseError, J as MantyxPluginToolRef, K as MantyxRunError, N as MantyxRunErrorInit, O as MantyxScopeError, P as MantyxToolError, Q as MantyxToolRef, S as McpToolRef, U as ModelCatalog, V as ModelInfo, W as OAuthToken, X as OutputSchema, R as ReasoningLevel, Y as RefreshOptions, Z as RefreshTokenSourceOptions, _ as ResultEvent, $ as RevokeOptions, a0 as RunEvent, a1 as RunEventBase, a2 as RunResult, a3 as RunSpec, a4 as ServerToolResultEvent, a5 as SessionInfo, a6 as SessionSpec, a7 as ThinkingDeltaEvent, a8 as TokenRequestReason, a9 as TokenSource, aa as ToolBudget, ab as ToolBudgetExceededEvent, ac as ToolBudgets, T as ToolRef, ad as ZodLikeObject, ae as defineLocalA2A, af as defineLocalMcp, ag as defineLocalTool, ah as generatePkceVerifier, ai as isLocalA2ATool, aj as isLocalMcpServer, ak as isLocalTool, al as mantyxA2A, am as mantyxMcp, an as mantyxPluginTool, ao as mantyxTool, ap as parseRunOutput, aq as pkceChallenge } from './client-DHwh8MPj.js';
1
+ export { A as A2AToolRef, a as AgentSession, b as AgentSpecBase, c as AssistantDeltaEvent, d as AssistantMessageEvent, C as CancelledEvent, D as DEFAULT_BASE_URL, e as DEFAULT_OAUTH_BASE_URL, f as DEFAULT_REFRESH_SKEW_MS, g as DefineLocalA2AOptions, h as DefineLocalMcpOptions, i as DefineLocalToolOptions, E as ErrorEvent, L as LocalA2ATool, j as LocalHandlers, k as LocalMcpHttpTransport, l as LocalMcpServer, m as LocalMcpStdioTransport, n as LocalTool, o as LocalToolCallEvent, p as LocalToolResultInEvent, q as LoopDetectedEvent, r as LoopDetection, s as MantyxA2AOptions, t as MantyxAuthError, M as MantyxClient, u as MantyxClientOptions, v as MantyxError, w as MantyxMcpOptions, x as MantyxNetworkError, y as MantyxOAuthClient, z as MantyxOAuthClientOptions, B as MantyxOAuthError, F as MantyxParseError, G as MantyxPluginToolRef, H as MantyxRunError, I as MantyxRunErrorInit, J as MantyxScopeError, K as MantyxToolError, N as MantyxToolRef, O as McpToolRef, P as ModelCatalog, Q as ModelInfo, S as OAuthToken, U as OutputSchema, R as ReasoningLevel, V as RefreshOptions, W as RefreshTokenSourceOptions, X as ResultEvent, Y as RevokeOptions, Z as RunEvent, _ as RunEventBase, $ as RunResult, a0 as RunSpec, a1 as ServerToolResultEvent, a2 as SessionInfo, a3 as SessionSpec, a4 as ThinkingDeltaEvent, a5 as TokenRequestReason, a6 as TokenSource, a7 as ToolBudget, a8 as ToolBudgetExceededEvent, a9 as ToolBudgets, T as ToolRef, aa as ZodLikeObject, ab as defineLocalA2A, ac as defineLocalMcp, ad as defineLocalTool, ae as isLocalA2ATool, af as isLocalMcpServer, ag as isLocalTool, ah as mantyxA2A, ai as mantyxMcp, aj as mantyxPluginTool, ak as mantyxTool, al as parseRunOutput } from './client-CZUVldDx.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /**
@@ -52,6 +52,6 @@ declare function readSseStream(body: ReadableStream<Uint8Array> | null, opts?: S
52
52
  /**
53
53
  * Release version — synced from repo root VERSION (`npm run sync-version`).
54
54
  */
55
- declare const SDK_VERSION = "0.10.0";
55
+ declare const SDK_VERSION = "0.10.1";
56
56
 
57
57
  export { SDK_VERSION, type SseEvent, type SseStreamOptions, readSseStream, toToolParametersWire, zodToJsonSchema };
package/dist/index.js CHANGED
@@ -26,8 +26,6 @@ import {
26
26
  } from "./chunk-XMUCELMH.js";
27
27
 
28
28
  // src/oauth.ts
29
- import { Buffer } from "buffer";
30
- import { createHash, randomBytes } from "crypto";
31
29
  var DEFAULT_OAUTH_BASE_URL = "https://app.mantyx.io";
32
30
  var DEFAULT_REFRESH_SKEW_MS = 6e4;
33
31
  var MantyxOAuthError = class extends MantyxError {
@@ -66,26 +64,11 @@ var MantyxOAuthClient = class {
66
64
  this.fetchImpl = f;
67
65
  this.timeoutMs = opts.timeoutMs ?? 3e4;
68
66
  }
69
- /**
70
- * Swap an authorization-code + PKCE verifier for the initial
71
- * `{access_token, refresh_token}` pair. Call this exactly once per
72
- * sign-in after the browser/native redirect lands back on your
73
- * `redirectUri` with a `code` parameter. Persist the returned
74
- * `refreshToken` against the user record — it is long-lived and
75
- * non-rotating per `docs/oauth.md` §"Token lifetimes & lifecycle".
76
- */
77
- async exchangeAuthorizationCode(opts) {
78
- return this.token({
79
- grant_type: "authorization_code",
80
- code: opts.code,
81
- redirect_uri: opts.redirectUri,
82
- code_verifier: opts.codeVerifier
83
- });
84
- }
85
67
  /**
86
68
  * Mint a fresh access token from a stored refresh token. The
87
- * returned `refreshToken` is identical to the input — the field is
88
- * surfaced for symmetry with {@link exchangeAuthorizationCode} only.
69
+ * returned `refreshToken` is identical to the input — refresh
70
+ * tokens are persistent and non-rotating, so the field is
71
+ * surfaced only for symmetry with the response shape.
89
72
  *
90
73
  * On `400 invalid_grant` the refresh token has been revoked (or its
91
74
  * grant / app was deleted); the SDK surfaces a
@@ -103,21 +86,6 @@ var MantyxOAuthClient = class {
103
86
  if (scope !== void 0) body.scope = scope;
104
87
  return this.token(body);
105
88
  }
106
- /**
107
- * Request a workspace-scoped access token without a user via the
108
- * `client_credentials` grant. Available only on private OAuth apps
109
- * that were registered with `allowsClientCredentials: true`. No
110
- * refresh token is issued; re-call this method whenever a new
111
- * access token is needed.
112
- */
113
- async clientCredentials(opts = {}) {
114
- const body = {
115
- grant_type: "client_credentials"
116
- };
117
- const scope = normalizeScope(opts.scope);
118
- if (scope !== void 0) body.scope = scope;
119
- return this.token(body);
120
- }
121
89
  /**
122
90
  * Revoke an access or refresh token (RFC 7009). The server always
123
91
  * returns 200, even for unknown tokens. Revoking a **refresh**
@@ -139,6 +107,10 @@ var MantyxOAuthClient = class {
139
107
  * source caches the access token in-memory and refreshes
140
108
  * proactively when the cached value is within `refreshSkewMs` of
141
109
  * `expiresAt`, or eagerly when `MantyxClient` reports a 401.
110
+ *
111
+ * Pass `initialToken` if the calling app already has a non-expired
112
+ * access token in hand (e.g. straight out of the sign-in flow) to
113
+ * avoid an extra round-trip on the first request.
142
114
  */
143
115
  refreshTokenSource(opts) {
144
116
  if (!opts.refreshToken) {
@@ -151,20 +123,6 @@ var MantyxOAuthClient = class {
151
123
  return this.refresh({ refreshToken, scope: opts.scope });
152
124
  });
153
125
  }
154
- /**
155
- * Build a long-lived {@link TokenSource} backed by the
156
- * `client_credentials` grant. On every refresh the source re-mints
157
- * a workspace-scoped access token by calling the token endpoint
158
- * with `grant_type=client_credentials`. Available only on private
159
- * apps with `allowsClientCredentials: true`.
160
- */
161
- clientCredentialsTokenSource(opts = {}) {
162
- const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
163
- const cache = { token: void 0, inflight: null };
164
- return makeTokenSource(cache, skew, async () => {
165
- return this.clientCredentials({ scope: opts.scope });
166
- });
167
- }
168
126
  // -------------------------------------------------------------- internals
169
127
  /**
170
128
  * POST `application/x-www-form-urlencoded` to `/api/oauth/token` and
@@ -244,22 +202,6 @@ var MantyxOAuthClient = class {
244
202
  return res;
245
203
  }
246
204
  };
247
- function generatePkceVerifier(length = 64) {
248
- if (length < 43 || length > 128) {
249
- throw new MantyxError("PKCE code_verifier length must be in [43, 128]");
250
- }
251
- const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
252
- const bytes = randomBytes(length);
253
- let out = "";
254
- for (let i = 0; i < length; i++) {
255
- out += ALPHABET[bytes[i] % ALPHABET.length];
256
- }
257
- return out;
258
- }
259
- function pkceChallenge(verifier) {
260
- const hash = createHash("sha256").update(verifier, "utf8").digest();
261
- return Buffer.from(hash).toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
262
- }
263
205
  function makeTokenSource(cache, skewMs, mint) {
264
206
  return async (reason = "initial") => {
265
207
  if (reason !== "unauthorized" && cache.token && !isExpiring(cache.token, skewMs)) {
@@ -303,7 +245,7 @@ function normalizeScope(scope) {
303
245
  }
304
246
 
305
247
  // src/version.ts
306
- var SDK_VERSION = "0.10.0";
248
+ var SDK_VERSION = "0.10.1";
307
249
  export {
308
250
  AgentSession,
309
251
  DEFAULT_BASE_URL,
@@ -323,7 +265,6 @@ export {
323
265
  defineLocalA2A,
324
266
  defineLocalMcp,
325
267
  defineLocalTool,
326
- generatePkceVerifier,
327
268
  isLocalA2ATool,
328
269
  isLocalMcpServer,
329
270
  isLocalTool,
@@ -332,7 +273,6 @@ export {
332
273
  mantyxPluginTool,
333
274
  mantyxTool,
334
275
  parseRunOutput,
335
- pkceChallenge,
336
276
  readSseStream,
337
277
  toToolParametersWire,
338
278
  zodToJsonSchema
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/oauth.ts","../src/version.ts"],"sourcesContent":["/**\n * MANTYX OAuth 2.0 client: authorization-code exchange, refresh-token\n * minting, client-credentials grant, and token revocation, plus typed\n * {@link TokenSource}s that {@link MantyxClient} can consume to refresh\n * access tokens transparently before they expire (and again on 401).\n *\n * The wire contract this implements is `docs/oauth.md` in the SDK monorepo:\n *\n * - Token endpoint: `POST <baseUrl>/api/oauth/token`, form-encoded.\n * - Revoke endpoint: `POST <baseUrl>/api/oauth/revoke`, form-encoded.\n * - Access tokens (`mantyx_at_…`) live 1 hour (`expires_in: 3600`).\n * - Refresh tokens (`mantyx_rt_…`) are **persistent and non-rotating**:\n * `grant_type=refresh_token` echoes back the same value the client\n * sent. The caller persists the refresh token once at first sign-in\n * (encrypted at rest) and the SDK re-mints access tokens from it on\n * demand.\n *\n * See also `docs/oauth.md` for the authorization-code + PKCE consent\n * flow (which the SDK does **not** drive — the calling app owns the\n * redirect dance; once it has the auth code, `exchangeAuthorizationCode`\n * swaps it for the initial `{access_token, refresh_token}` pair).\n */\n\nimport { Buffer } from \"node:buffer\";\nimport { createHash, randomBytes } from \"node:crypto\";\n\nimport { MantyxError, MantyxNetworkError } from \"./errors.js\";\n\nexport const DEFAULT_OAUTH_BASE_URL = \"https://app.mantyx.io\";\n\n/** Skew (ms) before `expiresAt` at which a TokenSource will pre-emptively refresh. Default 60s. */\nexport const DEFAULT_REFRESH_SKEW_MS = 60_000;\n\n/**\n * Raised on a non-2xx response from `POST /api/oauth/token` or\n * `POST /api/oauth/revoke`. Carries the RFC 6749 `error` discriminator\n * (`\"invalid_grant\"`, `\"invalid_client\"`, `\"unsupported_grant_type\"`,\n * …) and the optional `error_description` so callers can branch on\n * machine-readable values without parsing the human message.\n *\n * `invalid_grant` from the refresh path specifically signals that the\n * refresh token has been revoked (or the OAuth grant / application\n * was deleted). The SDK never loops on this — callers should route\n * the user back to a fresh sign-in.\n */\nexport class MantyxOAuthError extends MantyxError {\n readonly oauthError: string;\n readonly oauthErrorDescription: string | undefined;\n\n constructor(\n oauthError: string,\n oauthErrorDescription: string | undefined,\n status: number,\n ) {\n const message = oauthErrorDescription\n ? `OAuth ${oauthError}: ${oauthErrorDescription}`\n : `OAuth ${oauthError}`;\n super(message, { code: oauthError, status });\n this.name = \"MantyxOAuthError\";\n this.oauthError = oauthError;\n this.oauthErrorDescription = oauthErrorDescription;\n }\n}\n\n/**\n * Decoded `POST /api/oauth/token` response, augmented with an absolute\n * `expiresAt` timestamp the SDK can use to decide when to refresh.\n *\n * `refreshToken` is present on the initial `authorization_code` exchange\n * and on subsequent `refresh_token` calls (where it is identical to the\n * value the client just sent — refresh tokens never rotate). The\n * `client_credentials` grant never returns one.\n */\nexport interface OAuthToken {\n readonly accessToken: string;\n readonly refreshToken: string | undefined;\n readonly tokenType: string;\n readonly expiresIn: number;\n /** Absolute Unix-ms timestamp set when the SDK parsed the response. */\n readonly expiresAt: number;\n readonly scope: string | undefined;\n}\n\n/** Why the SDK asked the {@link TokenSource} for the current access token. */\nexport type TokenRequestReason = \"initial\" | \"expired\" | \"unauthorized\";\n\n/**\n * A `TokenSource` produces the current access token on demand. The\n * {@link MantyxClient} HTTP layer calls it before every request. When\n * called with `reason: \"unauthorized\"` the source MUST force a refresh\n * (do not return a cached value); this is how the SDK recovers from\n * 401s caused by a token that the server already invalidated.\n *\n * Implementations should be safe to call from many concurrent requests.\n */\nexport type TokenSource = (reason?: TokenRequestReason) => Promise<string>;\n\n/** Caller-supplied options for `MantyxOAuthClient`. */\nexport interface MantyxOAuthClientOptions {\n /**\n * OAuth `client_id` issued at app registration (token prefix\n * `mantyx_oa_`).\n */\n clientId: string;\n /**\n * OAuth `client_secret` issued at app registration (token prefix\n * `mantyx_oas_`). Every MANTYX OAuth app is a confidential client,\n * so this is always required for token + revoke calls. Treat as a\n * deployment secret — do not bundle into browser builds.\n */\n clientSecret: string;\n /**\n * Origin of the MANTYX deployment. Defaults to `https://app.mantyx.io`.\n * The OAuth endpoints are mounted at `<baseUrl>/api/oauth/...`.\n */\n baseUrl?: string;\n /** Optional `fetch` override (e.g. node-fetch wrapper). Default: global `fetch`. */\n fetch?: typeof fetch;\n /** Default per-request timeout in milliseconds. Default: 30s. */\n timeoutMs?: number;\n}\n\nexport interface ExchangeAuthorizationCodeOptions {\n code: string;\n redirectUri: string;\n codeVerifier: string;\n}\n\nexport interface RefreshOptions {\n refreshToken: string;\n /**\n * Optional scope narrowing. Must be a subset of the scopes already\n * granted to the refresh token (server enforces this). Useful when\n * an SDK consumer wants a short-scope access token for a specific\n * sub-operation.\n */\n scope?: string | readonly string[];\n}\n\nexport interface ClientCredentialsOptions {\n scope?: string | readonly string[];\n}\n\nexport interface RevokeOptions {\n token: string;\n}\n\nexport interface RefreshTokenSourceOptions {\n refreshToken: string;\n /** Optional scope narrowing applied on every refresh. */\n scope?: string | readonly string[];\n /**\n * How many ms before `expiresAt` the source proactively refreshes.\n * Defaults to {@link DEFAULT_REFRESH_SKEW_MS} (60s).\n */\n refreshSkewMs?: number;\n /**\n * Optional initial access token + expiry to seed the source's cache\n * with (e.g. the token already in hand from the authorization-code\n * exchange). When omitted, the source mints one on the first call.\n */\n initialToken?: OAuthToken;\n}\n\nexport interface ClientCredentialsTokenSourceOptions {\n scope?: string | readonly string[];\n refreshSkewMs?: number;\n}\n\n/**\n * Wraps the MANTYX OAuth 2.0 authorization-server endpoints. App-scoped\n * (one per `{clientId, clientSecret}` pair); construct independently of\n * {@link MantyxClient}, then either call its grant helpers directly or\n * hand a `TokenSource` it produces to `MantyxClient` for fully\n * transparent refresh.\n */\nexport class MantyxOAuthClient {\n readonly clientId: string;\n readonly baseUrl: string;\n private readonly clientSecret: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n\n constructor(opts: MantyxOAuthClientOptions) {\n if (!opts.clientId) {\n throw new MantyxError(\"`clientId` is required for MantyxOAuthClient\");\n }\n if (!opts.clientSecret) {\n throw new MantyxError(\"`clientSecret` is required for MantyxOAuthClient\");\n }\n const f = opts.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new MantyxError(\n \"Global fetch is not available; pass a custom `fetch` implementation in MantyxOAuthClientOptions.\",\n );\n }\n this.clientId = opts.clientId;\n this.clientSecret = opts.clientSecret;\n this.baseUrl = (opts.baseUrl ?? DEFAULT_OAUTH_BASE_URL).replace(/\\/+$/, \"\");\n this.fetchImpl = f;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n\n /**\n * Swap an authorization-code + PKCE verifier for the initial\n * `{access_token, refresh_token}` pair. Call this exactly once per\n * sign-in after the browser/native redirect lands back on your\n * `redirectUri` with a `code` parameter. Persist the returned\n * `refreshToken` against the user record — it is long-lived and\n * non-rotating per `docs/oauth.md` §\"Token lifetimes & lifecycle\".\n */\n async exchangeAuthorizationCode(opts: ExchangeAuthorizationCodeOptions): Promise<OAuthToken> {\n return this.token({\n grant_type: \"authorization_code\",\n code: opts.code,\n redirect_uri: opts.redirectUri,\n code_verifier: opts.codeVerifier,\n });\n }\n\n /**\n * Mint a fresh access token from a stored refresh token. The\n * returned `refreshToken` is identical to the input — the field is\n * surfaced for symmetry with {@link exchangeAuthorizationCode} only.\n *\n * On `400 invalid_grant` the refresh token has been revoked (or its\n * grant / app was deleted); the SDK surfaces a\n * {@link MantyxOAuthError} and callers must drive a fresh sign-in.\n */\n async refresh(opts: RefreshOptions): Promise<OAuthToken> {\n if (!opts.refreshToken) {\n throw new MantyxError(\"`refreshToken` is required for MantyxOAuthClient.refresh\");\n }\n const body: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: opts.refreshToken,\n };\n const scope = normalizeScope(opts.scope);\n if (scope !== undefined) body.scope = scope;\n return this.token(body);\n }\n\n /**\n * Request a workspace-scoped access token without a user via the\n * `client_credentials` grant. Available only on private OAuth apps\n * that were registered with `allowsClientCredentials: true`. No\n * refresh token is issued; re-call this method whenever a new\n * access token is needed.\n */\n async clientCredentials(opts: ClientCredentialsOptions = {}): Promise<OAuthToken> {\n const body: Record<string, string> = {\n grant_type: \"client_credentials\",\n };\n const scope = normalizeScope(opts.scope);\n if (scope !== undefined) body.scope = scope;\n return this.token(body);\n }\n\n /**\n * Revoke an access or refresh token (RFC 7009). The server always\n * returns 200, even for unknown tokens. Revoking a **refresh**\n * token kills the refresh and every live access token tied to its\n * grant; revoking an **access** token kills only that one.\n */\n async revoke(opts: RevokeOptions): Promise<void> {\n if (!opts.token) {\n throw new MantyxError(\"`token` is required for MantyxOAuthClient.revoke\");\n }\n await this.formPost(\"/api/oauth/revoke\", {\n token: opts.token,\n });\n }\n\n /**\n * Build a long-lived {@link TokenSource} that re-mints access\n * tokens from the supplied refresh token. Pass the returned source\n * to `new MantyxClient({ tokenSource, workspaceSlug, ... })`. The\n * source caches the access token in-memory and refreshes\n * proactively when the cached value is within `refreshSkewMs` of\n * `expiresAt`, or eagerly when `MantyxClient` reports a 401.\n */\n refreshTokenSource(opts: RefreshTokenSourceOptions): TokenSource {\n if (!opts.refreshToken) {\n throw new MantyxError(\"`refreshToken` is required for MantyxOAuthClient.refreshTokenSource\");\n }\n const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;\n const cache: TokenCache = { token: opts.initialToken, inflight: null };\n const refreshToken = opts.refreshToken;\n return makeTokenSource(cache, skew, async () => {\n return this.refresh({ refreshToken, scope: opts.scope });\n });\n }\n\n /**\n * Build a long-lived {@link TokenSource} backed by the\n * `client_credentials` grant. On every refresh the source re-mints\n * a workspace-scoped access token by calling the token endpoint\n * with `grant_type=client_credentials`. Available only on private\n * apps with `allowsClientCredentials: true`.\n */\n clientCredentialsTokenSource(opts: ClientCredentialsTokenSourceOptions = {}): TokenSource {\n const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;\n const cache: TokenCache = { token: undefined, inflight: null };\n return makeTokenSource(cache, skew, async () => {\n return this.clientCredentials({ scope: opts.scope });\n });\n }\n\n // -------------------------------------------------------------- internals\n\n /**\n * POST `application/x-www-form-urlencoded` to `/api/oauth/token` and\n * decode the {@link OAuthToken} response. Always injects `client_id`\n * + `client_secret` from the constructor.\n */\n private async token(body: Record<string, string>): Promise<OAuthToken> {\n const res = await this.formPost(\"/api/oauth/token\", body);\n let parsed: Record<string, unknown> = {};\n try {\n parsed = (await res.json()) as Record<string, unknown>;\n } catch {\n throw new MantyxOAuthError(\n \"invalid_response\",\n \"Token endpoint returned a non-JSON response\",\n res.status,\n );\n }\n const accessToken = typeof parsed.access_token === \"string\" ? parsed.access_token : \"\";\n if (!accessToken) {\n throw new MantyxOAuthError(\n \"invalid_response\",\n \"Token endpoint response is missing `access_token`\",\n res.status,\n );\n }\n const expiresIn = typeof parsed.expires_in === \"number\" ? parsed.expires_in : 3600;\n return {\n accessToken,\n refreshToken: typeof parsed.refresh_token === \"string\" ? parsed.refresh_token : undefined,\n tokenType: typeof parsed.token_type === \"string\" ? parsed.token_type : \"Bearer\",\n expiresIn,\n expiresAt: Date.now() + expiresIn * 1000,\n scope: typeof parsed.scope === \"string\" ? parsed.scope : undefined,\n };\n }\n\n private async formPost(path: string, body: Record<string, string>): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n const params = new URLSearchParams({\n ...body,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n });\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n signal: ctrl.signal,\n });\n } catch (err) {\n if (ctrl.signal.aborted) {\n throw new MantyxNetworkError(`OAuth request timed out after ${this.timeoutMs}ms`);\n }\n throw new MantyxNetworkError(`OAuth network error: ${(err as Error).message}`, {\n cause: err,\n });\n } finally {\n clearTimeout(t);\n }\n if (!res.ok) {\n let errBody: { error?: unknown; error_description?: unknown } = {};\n try {\n errBody = (await res.json()) as typeof errBody;\n } catch {\n // ignore\n }\n const oauthError = typeof errBody.error === \"string\" ? errBody.error : `http_${res.status}`;\n const desc =\n typeof errBody.error_description === \"string\" ? errBody.error_description : undefined;\n throw new MantyxOAuthError(oauthError, desc, res.status);\n }\n return res;\n }\n}\n\n// -------------------------------------------------------------- PKCE helpers\n\n/**\n * Generate a high-entropy PKCE `code_verifier` (RFC 7636 §4.1). The\n * verifier is the raw secret you keep across the redirect; the\n * `code_challenge` you send on `/api/oauth/authorize` is derived from\n * it via {@link pkceChallenge}.\n *\n * Default length is 64 characters (≈ 384 bits of entropy after\n * base64url-encoding the 32 random bytes). Pass `length` to clamp to\n * the RFC's 43..128 inclusive range.\n */\nexport function generatePkceVerifier(length = 64): string {\n if (length < 43 || length > 128) {\n throw new MantyxError(\"PKCE code_verifier length must be in [43, 128]\");\n }\n // 32 random bytes -> 43 base64url chars; we then slice / pad up to the\n // requested length using the unreserved RFC 7636 alphabet.\n const ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~\";\n const bytes = randomBytes(length);\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += ALPHABET[bytes[i]! % ALPHABET.length];\n }\n return out;\n}\n\n/**\n * Compute the PKCE `S256` `code_challenge` for a given verifier:\n * `base64url(sha256(verifier))` with no padding (RFC 7636 §4.2).\n */\nexport function pkceChallenge(verifier: string): string {\n const hash = createHash(\"sha256\").update(verifier, \"utf8\").digest();\n return Buffer.from(hash)\n .toString(\"base64\")\n .replace(/=+$/, \"\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\");\n}\n\n// -------------------------------------------------------------- internals\n\ninterface TokenCache {\n token: OAuthToken | undefined;\n inflight: Promise<OAuthToken> | null;\n}\n\n/**\n * Wrap a `mintToken` thunk into a single-flight {@link TokenSource}\n * with a cache + proactive-refresh skew. The cache is overwritten\n * atomically on every successful mint; the in-flight promise\n * collapses N concurrent expired-token observers into one mint call.\n *\n * Single-flight is an efficiency, not a correctness requirement —\n * `docs/oauth.md` explicitly allows multiple concurrent refreshes\n * against the same refresh token — but it keeps the token-endpoint\n * QPS reasonable when an SDK consumer fans out work in parallel.\n */\nfunction makeTokenSource(\n cache: TokenCache,\n skewMs: number,\n mint: () => Promise<OAuthToken>,\n): TokenSource {\n return async (reason: TokenRequestReason = \"initial\"): Promise<string> => {\n if (reason !== \"unauthorized\" && cache.token && !isExpiring(cache.token, skewMs)) {\n return cache.token.accessToken;\n }\n if (cache.inflight) {\n const t = await cache.inflight;\n // If the inflight refresh was triggered by a benign cache miss\n // and we observed an unauthorized hint after it started, retry\n // once with a forced mint so the caller never gets a stale token.\n if (reason === \"unauthorized\" && t === cache.token) {\n // fallthrough to fresh mint below\n } else {\n return t.accessToken;\n }\n }\n cache.inflight = mint().then(\n (t) => {\n cache.token = t;\n return t;\n },\n (err: unknown) => {\n throw err;\n },\n );\n try {\n const t = await cache.inflight;\n return t.accessToken;\n } finally {\n cache.inflight = null;\n }\n };\n}\n\nfunction isExpiring(token: OAuthToken, skewMs: number): boolean {\n return token.expiresAt - Date.now() <= skewMs;\n}\n\nfunction normalizeScope(scope: string | readonly string[] | undefined): string | undefined {\n if (scope === undefined) return undefined;\n if (typeof scope === \"string\") {\n const trimmed = scope.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n const joined = scope.filter((s) => typeof s === \"string\" && s.length > 0).join(\" \");\n return joined.length > 0 ? joined : undefined;\n}\n","/**\n * Release version — synced from repo root VERSION (`npm run sync-version`).\n */\nexport const SDK_VERSION = \"0.10.0\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/oauth.ts","../src/version.ts"],"sourcesContent":["/**\n * MANTYX OAuth 2.0 refresh client: trade a stored refresh token for\n * short-lived access tokens, revoke tokens at sign-out, and expose\n * a {@link TokenSource} the {@link MantyxClient} HTTP layer calls\n * before every request (and again on 401).\n *\n * The library is intentionally **refresh-only**. It assumes the caller\n * already obtained the refresh token through their own sign-in flow\n * (Authorization Code + PKCE in a browser, native redirect, server-\n * side exchange — whatever fits the host application). The SDK does\n * not drive consent, does not initiate auth-code exchanges, and does\n * not bundle PKCE helpers.\n *\n * Wire contract (`docs/oauth.md`):\n *\n * - Token endpoint: `POST <baseUrl>/api/oauth/token`, form-encoded,\n * `grant_type=refresh_token`. Echoes back the same `refresh_token`\n * the client sent (refresh tokens are persistent and non-rotating).\n * - Revoke endpoint: `POST <baseUrl>/api/oauth/revoke`, form-encoded.\n * - Access tokens (`mantyx_at_…`) live 1 hour (`expires_in: 3600`).\n * - Refresh tokens (`mantyx_rt_…`) are long-lived; the caller persists\n * them once at first sign-in (encrypted at rest) and the SDK re-mints\n * access tokens from the same value on demand.\n */\n\nimport { MantyxError, MantyxNetworkError } from \"./errors.js\";\n\nexport const DEFAULT_OAUTH_BASE_URL = \"https://app.mantyx.io\";\n\n/** Skew (ms) before `expiresAt` at which a TokenSource will pre-emptively refresh. Default 60s. */\nexport const DEFAULT_REFRESH_SKEW_MS = 60_000;\n\n/**\n * Raised on a non-2xx response from `POST /api/oauth/token` or\n * `POST /api/oauth/revoke`. Carries the RFC 6749 `error` discriminator\n * (`\"invalid_grant\"`, `\"invalid_client\"`, `\"unsupported_grant_type\"`,\n * …) and the optional `error_description` so callers can branch on\n * machine-readable values without parsing the human message.\n *\n * `invalid_grant` from the refresh path specifically signals that the\n * refresh token has been revoked (or the OAuth grant / application\n * was deleted). The SDK never loops on this — callers should route\n * the user back to a fresh sign-in.\n */\nexport class MantyxOAuthError extends MantyxError {\n readonly oauthError: string;\n readonly oauthErrorDescription: string | undefined;\n\n constructor(\n oauthError: string,\n oauthErrorDescription: string | undefined,\n status: number,\n ) {\n const message = oauthErrorDescription\n ? `OAuth ${oauthError}: ${oauthErrorDescription}`\n : `OAuth ${oauthError}`;\n super(message, { code: oauthError, status });\n this.name = \"MantyxOAuthError\";\n this.oauthError = oauthError;\n this.oauthErrorDescription = oauthErrorDescription;\n }\n}\n\n/**\n * Decoded `POST /api/oauth/token` response, augmented with an absolute\n * `expiresAt` timestamp the SDK uses to decide when to refresh.\n *\n * On the refresh grant the response's `refreshToken` is identical to\n * the value the client just sent (refresh tokens never rotate). The\n * field is surfaced for symmetry with whatever the calling app's\n * sign-in flow already does.\n */\nexport interface OAuthToken {\n readonly accessToken: string;\n readonly refreshToken: string | undefined;\n readonly tokenType: string;\n readonly expiresIn: number;\n /** Absolute Unix-ms timestamp set when the SDK parsed the response. */\n readonly expiresAt: number;\n readonly scope: string | undefined;\n}\n\n/** Why the SDK asked the {@link TokenSource} for the current access token. */\nexport type TokenRequestReason = \"initial\" | \"expired\" | \"unauthorized\";\n\n/**\n * A `TokenSource` produces the current access token on demand. The\n * {@link MantyxClient} HTTP layer calls it before every request. When\n * called with `reason: \"unauthorized\"` the source MUST force a refresh\n * (do not return a cached value); this is how the SDK recovers from\n * 401s caused by a token that the server already invalidated.\n *\n * Implementations should be safe to call from many concurrent requests.\n */\nexport type TokenSource = (reason?: TokenRequestReason) => Promise<string>;\n\n/** Caller-supplied options for `MantyxOAuthClient`. */\nexport interface MantyxOAuthClientOptions {\n /**\n * OAuth `client_id` issued at app registration (token prefix\n * `mantyx_oa_`).\n */\n clientId: string;\n /**\n * OAuth `client_secret` issued at app registration (token prefix\n * `mantyx_oas_`). Every MANTYX OAuth app is a confidential client,\n * so this is always required for token + revoke calls. Treat as a\n * deployment secret — do not bundle into browser builds.\n */\n clientSecret: string;\n /**\n * Origin of the MANTYX deployment. Defaults to `https://app.mantyx.io`.\n * The OAuth endpoints are mounted at `<baseUrl>/api/oauth/...`.\n */\n baseUrl?: string;\n /** Optional `fetch` override (e.g. node-fetch wrapper). Default: global `fetch`. */\n fetch?: typeof fetch;\n /** Default per-request timeout in milliseconds. Default: 30s. */\n timeoutMs?: number;\n}\n\nexport interface RefreshOptions {\n refreshToken: string;\n /**\n * Optional scope narrowing. Must be a subset of the scopes already\n * granted to the refresh token (server enforces this). Useful when\n * an SDK consumer wants a short-scope access token for a specific\n * sub-operation.\n */\n scope?: string | readonly string[];\n}\n\nexport interface RevokeOptions {\n token: string;\n}\n\nexport interface RefreshTokenSourceOptions {\n refreshToken: string;\n /** Optional scope narrowing applied on every refresh. */\n scope?: string | readonly string[];\n /**\n * How many ms before `expiresAt` the source proactively refreshes.\n * Defaults to {@link DEFAULT_REFRESH_SKEW_MS} (60s).\n */\n refreshSkewMs?: number;\n /**\n * Optional initial access token + expiry to seed the source's cache\n * with (e.g. the token already in hand from the host application's\n * sign-in flow). When omitted, the source mints one on the first\n * call.\n */\n initialToken?: OAuthToken;\n}\n\n/**\n * Refresh-only wrapper around the MANTYX OAuth 2.0 authorization-server\n * endpoints. App-scoped (one per `{clientId, clientSecret}` pair);\n * construct independently of {@link MantyxClient}, then either call\n * {@link refresh} / {@link revoke} directly or hand a `TokenSource`\n * produced by {@link refreshTokenSource} to `MantyxClient` for fully\n * transparent refresh on every request.\n *\n * The client deliberately does **not** drive the authorization-code\n * exchange or any other \"initiate sign-in\" grant. The caller is\n * expected to obtain the refresh token through their own consent flow\n * and persist it before constructing this client.\n */\nexport class MantyxOAuthClient {\n readonly clientId: string;\n readonly baseUrl: string;\n private readonly clientSecret: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n\n constructor(opts: MantyxOAuthClientOptions) {\n if (!opts.clientId) {\n throw new MantyxError(\"`clientId` is required for MantyxOAuthClient\");\n }\n if (!opts.clientSecret) {\n throw new MantyxError(\"`clientSecret` is required for MantyxOAuthClient\");\n }\n const f = opts.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new MantyxError(\n \"Global fetch is not available; pass a custom `fetch` implementation in MantyxOAuthClientOptions.\",\n );\n }\n this.clientId = opts.clientId;\n this.clientSecret = opts.clientSecret;\n this.baseUrl = (opts.baseUrl ?? DEFAULT_OAUTH_BASE_URL).replace(/\\/+$/, \"\");\n this.fetchImpl = f;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n\n /**\n * Mint a fresh access token from a stored refresh token. The\n * returned `refreshToken` is identical to the input — refresh\n * tokens are persistent and non-rotating, so the field is\n * surfaced only for symmetry with the response shape.\n *\n * On `400 invalid_grant` the refresh token has been revoked (or its\n * grant / app was deleted); the SDK surfaces a\n * {@link MantyxOAuthError} and callers must drive a fresh sign-in.\n */\n async refresh(opts: RefreshOptions): Promise<OAuthToken> {\n if (!opts.refreshToken) {\n throw new MantyxError(\"`refreshToken` is required for MantyxOAuthClient.refresh\");\n }\n const body: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: opts.refreshToken,\n };\n const scope = normalizeScope(opts.scope);\n if (scope !== undefined) body.scope = scope;\n return this.token(body);\n }\n\n /**\n * Revoke an access or refresh token (RFC 7009). The server always\n * returns 200, even for unknown tokens. Revoking a **refresh**\n * token kills the refresh and every live access token tied to its\n * grant; revoking an **access** token kills only that one.\n */\n async revoke(opts: RevokeOptions): Promise<void> {\n if (!opts.token) {\n throw new MantyxError(\"`token` is required for MantyxOAuthClient.revoke\");\n }\n await this.formPost(\"/api/oauth/revoke\", {\n token: opts.token,\n });\n }\n\n /**\n * Build a long-lived {@link TokenSource} that re-mints access\n * tokens from the supplied refresh token. Pass the returned source\n * to `new MantyxClient({ tokenSource, workspaceSlug, ... })`. The\n * source caches the access token in-memory and refreshes\n * proactively when the cached value is within `refreshSkewMs` of\n * `expiresAt`, or eagerly when `MantyxClient` reports a 401.\n *\n * Pass `initialToken` if the calling app already has a non-expired\n * access token in hand (e.g. straight out of the sign-in flow) to\n * avoid an extra round-trip on the first request.\n */\n refreshTokenSource(opts: RefreshTokenSourceOptions): TokenSource {\n if (!opts.refreshToken) {\n throw new MantyxError(\"`refreshToken` is required for MantyxOAuthClient.refreshTokenSource\");\n }\n const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;\n const cache: TokenCache = { token: opts.initialToken, inflight: null };\n const refreshToken = opts.refreshToken;\n return makeTokenSource(cache, skew, async () => {\n return this.refresh({ refreshToken, scope: opts.scope });\n });\n }\n\n // -------------------------------------------------------------- internals\n\n /**\n * POST `application/x-www-form-urlencoded` to `/api/oauth/token` and\n * decode the {@link OAuthToken} response. Always injects `client_id`\n * + `client_secret` from the constructor.\n */\n private async token(body: Record<string, string>): Promise<OAuthToken> {\n const res = await this.formPost(\"/api/oauth/token\", body);\n let parsed: Record<string, unknown> = {};\n try {\n parsed = (await res.json()) as Record<string, unknown>;\n } catch {\n throw new MantyxOAuthError(\n \"invalid_response\",\n \"Token endpoint returned a non-JSON response\",\n res.status,\n );\n }\n const accessToken = typeof parsed.access_token === \"string\" ? parsed.access_token : \"\";\n if (!accessToken) {\n throw new MantyxOAuthError(\n \"invalid_response\",\n \"Token endpoint response is missing `access_token`\",\n res.status,\n );\n }\n const expiresIn = typeof parsed.expires_in === \"number\" ? parsed.expires_in : 3600;\n return {\n accessToken,\n refreshToken: typeof parsed.refresh_token === \"string\" ? parsed.refresh_token : undefined,\n tokenType: typeof parsed.token_type === \"string\" ? parsed.token_type : \"Bearer\",\n expiresIn,\n expiresAt: Date.now() + expiresIn * 1000,\n scope: typeof parsed.scope === \"string\" ? parsed.scope : undefined,\n };\n }\n\n private async formPost(path: string, body: Record<string, string>): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n const params = new URLSearchParams({\n ...body,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n });\n const ctrl = new AbortController();\n const t = setTimeout(() => ctrl.abort(), this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n signal: ctrl.signal,\n });\n } catch (err) {\n if (ctrl.signal.aborted) {\n throw new MantyxNetworkError(`OAuth request timed out after ${this.timeoutMs}ms`);\n }\n throw new MantyxNetworkError(`OAuth network error: ${(err as Error).message}`, {\n cause: err,\n });\n } finally {\n clearTimeout(t);\n }\n if (!res.ok) {\n let errBody: { error?: unknown; error_description?: unknown } = {};\n try {\n errBody = (await res.json()) as typeof errBody;\n } catch {\n // ignore\n }\n const oauthError = typeof errBody.error === \"string\" ? errBody.error : `http_${res.status}`;\n const desc =\n typeof errBody.error_description === \"string\" ? errBody.error_description : undefined;\n throw new MantyxOAuthError(oauthError, desc, res.status);\n }\n return res;\n }\n}\n\n// -------------------------------------------------------------- internals\n\ninterface TokenCache {\n token: OAuthToken | undefined;\n inflight: Promise<OAuthToken> | null;\n}\n\n/**\n * Wrap a `mintToken` thunk into a single-flight {@link TokenSource}\n * with a cache + proactive-refresh skew. The cache is overwritten\n * atomically on every successful mint; the in-flight promise\n * collapses N concurrent expired-token observers into one mint call.\n *\n * Single-flight is an efficiency, not a correctness requirement —\n * `docs/oauth.md` explicitly allows multiple concurrent refreshes\n * against the same refresh token — but it keeps the token-endpoint\n * QPS reasonable when an SDK consumer fans out work in parallel.\n */\nfunction makeTokenSource(\n cache: TokenCache,\n skewMs: number,\n mint: () => Promise<OAuthToken>,\n): TokenSource {\n return async (reason: TokenRequestReason = \"initial\"): Promise<string> => {\n if (reason !== \"unauthorized\" && cache.token && !isExpiring(cache.token, skewMs)) {\n return cache.token.accessToken;\n }\n if (cache.inflight) {\n const t = await cache.inflight;\n if (reason === \"unauthorized\" && t === cache.token) {\n // If the inflight refresh was triggered by a benign cache miss\n // and we observed an unauthorized hint after it started, fall\n // through and mint again so the caller never gets a stale token.\n } else {\n return t.accessToken;\n }\n }\n cache.inflight = mint().then(\n (t) => {\n cache.token = t;\n return t;\n },\n (err: unknown) => {\n throw err;\n },\n );\n try {\n const t = await cache.inflight;\n return t.accessToken;\n } finally {\n cache.inflight = null;\n }\n };\n}\n\nfunction isExpiring(token: OAuthToken, skewMs: number): boolean {\n return token.expiresAt - Date.now() <= skewMs;\n}\n\nfunction normalizeScope(scope: string | readonly string[] | undefined): string | undefined {\n if (scope === undefined) return undefined;\n if (typeof scope === \"string\") {\n const trimmed = scope.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n const joined = scope.filter((s) => typeof s === \"string\" && s.length > 0).join(\" \");\n return joined.length > 0 ? joined : undefined;\n}\n","/**\n * Release version — synced from repo root VERSION (`npm run sync-version`).\n */\nexport const SDK_VERSION = \"0.10.1\";\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantyx/sdk",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "MANTYX as a hosted agent runtime: define ephemeral agents, mix server-side MANTYX tools with locally-executed tools, run them remotely.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",