@mantyx/sdk 0.9.0 → 0.10.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/index.cjs CHANGED
@@ -32,17 +32,23 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AgentSession: () => AgentSession,
34
34
  DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
35
+ DEFAULT_OAUTH_BASE_URL: () => DEFAULT_OAUTH_BASE_URL,
36
+ DEFAULT_REFRESH_SKEW_MS: () => DEFAULT_REFRESH_SKEW_MS,
35
37
  MantyxAuthError: () => MantyxAuthError,
36
38
  MantyxClient: () => MantyxClient,
37
39
  MantyxError: () => MantyxError,
38
40
  MantyxNetworkError: () => MantyxNetworkError,
41
+ MantyxOAuthClient: () => MantyxOAuthClient,
42
+ MantyxOAuthError: () => MantyxOAuthError,
39
43
  MantyxParseError: () => MantyxParseError,
40
44
  MantyxRunError: () => MantyxRunError,
45
+ MantyxScopeError: () => MantyxScopeError,
41
46
  MantyxToolError: () => MantyxToolError,
42
47
  SDK_VERSION: () => SDK_VERSION,
43
48
  defineLocalA2A: () => defineLocalA2A,
44
49
  defineLocalMcp: () => defineLocalMcp,
45
50
  defineLocalTool: () => defineLocalTool,
51
+ generatePkceVerifier: () => generatePkceVerifier,
46
52
  isLocalA2ATool: () => isLocalA2ATool,
47
53
  isLocalMcpServer: () => isLocalMcpServer,
48
54
  isLocalTool: () => isLocalTool,
@@ -51,6 +57,7 @@ __export(index_exports, {
51
57
  mantyxPluginTool: () => mantyxPluginTool,
52
58
  mantyxTool: () => mantyxTool,
53
59
  parseRunOutput: () => parseRunOutput,
60
+ pkceChallenge: () => pkceChallenge,
54
61
  readSseStream: () => readSseStream,
55
62
  toToolParametersWire: () => toToolParametersWire,
56
63
  zodToJsonSchema: () => zodToJsonSchema
@@ -80,11 +87,23 @@ var MantyxNetworkError = class extends MantyxError {
80
87
  }
81
88
  };
82
89
  var MantyxAuthError = class extends MantyxError {
83
- constructor(message = "Invalid or missing API key") {
90
+ constructor(message = "Invalid or missing API key / OAuth access token") {
84
91
  super(message, { code: "unauthorized", status: 401 });
85
92
  this.name = "MantyxAuthError";
86
93
  }
87
94
  };
95
+ var MantyxScopeError = class extends MantyxError {
96
+ /**
97
+ * Scope(s) the route demanded. Always at least one entry; usually
98
+ * exactly one. New routes may demand more scopes in the future.
99
+ */
100
+ requiredScopes;
101
+ constructor(message, requiredScopes) {
102
+ super(message, { code: "insufficient_scope", status: 403 });
103
+ this.name = "MantyxScopeError";
104
+ this.requiredScopes = [...requiredScopes];
105
+ }
106
+ };
88
107
  var MantyxToolError = class extends MantyxError {
89
108
  toolName;
90
109
  constructor(toolName, message) {
@@ -98,11 +117,23 @@ var MantyxToolError = class extends MantyxError {
98
117
  var MantyxRunError = class extends MantyxError {
99
118
  runId;
100
119
  subtype;
101
- constructor(runId, subtype, message) {
120
+ /** See {@link MantyxRunErrorInit.errorClass}. */
121
+ errorClass;
122
+ /** See {@link MantyxRunErrorInit.finishReason}. */
123
+ finishReason;
124
+ /** See {@link MantyxRunErrorInit.partialText}. */
125
+ partialText;
126
+ /** See {@link MantyxRunErrorInit.retryable}. */
127
+ retryable;
128
+ constructor(runId, subtype, message, init = {}) {
102
129
  super(message, { code: subtype });
103
130
  this.name = "MantyxRunError";
104
131
  this.runId = runId;
105
132
  this.subtype = subtype;
133
+ this.errorClass = init.errorClass;
134
+ this.finishReason = init.finishReason;
135
+ this.partialText = init.partialText;
136
+ this.retryable = init.retryable;
106
137
  }
107
138
  };
108
139
  var MantyxParseError = class extends MantyxError {
@@ -623,9 +654,7 @@ var DEFAULT_BASE_URL = "https://app.mantyx.io";
623
654
  var MantyxClient = class {
624
655
  options;
625
656
  constructor(opts) {
626
- if (!opts.apiKey || typeof opts.apiKey !== "string") {
627
- throw new MantyxError("apiKey is required");
628
- }
657
+ const { credential, tokenSource } = resolveCredential(opts);
629
658
  if (!opts.workspaceSlug || typeof opts.workspaceSlug !== "string") {
630
659
  throw new MantyxError("workspaceSlug is required");
631
660
  }
@@ -636,11 +665,12 @@ var MantyxClient = class {
636
665
  );
637
666
  }
638
667
  this.options = {
639
- apiKey: opts.apiKey,
668
+ apiKey: credential,
640
669
  workspaceSlug: opts.workspaceSlug,
641
670
  baseUrl: (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
642
671
  fetch: f,
643
- timeoutMs: opts.timeoutMs ?? 6e4
672
+ timeoutMs: opts.timeoutMs ?? 6e4,
673
+ tokenSource
644
674
  };
645
675
  }
646
676
  // -------------------------------------------------------------- Models
@@ -758,7 +788,13 @@ var MantyxClient = class {
758
788
  }
759
789
  } else if (ev.type === "error") {
760
790
  const e = ev;
761
- throw new MantyxRunError(runId, e.code ?? "error", e.error);
791
+ const subtype = e.errorClass ?? e.code ?? "error";
792
+ throw new MantyxRunError(runId, subtype, e.error, {
793
+ ...e.errorClass !== void 0 ? { errorClass: e.errorClass } : {},
794
+ ...e.finishReason !== void 0 ? { finishReason: e.finishReason } : {},
795
+ ...typeof e.partialText === "string" ? { partialText: e.partialText } : {},
796
+ ...typeof e.retryable === "boolean" ? { retryable: e.retryable } : {}
797
+ });
762
798
  } else if (ev.type === "cancelled") {
763
799
  throw new MantyxRunError(runId, "cancelled", "Run was cancelled");
764
800
  }
@@ -770,19 +806,7 @@ var MantyxClient = class {
770
806
  let lastSeq = 0;
771
807
  while (true) {
772
808
  const reqUrl = lastSeq > 0 ? `${url}?lastSeq=${lastSeq}` : url;
773
- const res = await this.options.fetch(reqUrl, {
774
- method: "GET",
775
- headers: {
776
- ...this.authHeaders(),
777
- Accept: "text/event-stream",
778
- ...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
779
- },
780
- ...signal ? { signal } : {}
781
- }).catch((err) => {
782
- throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
783
- cause: err
784
- });
785
- });
809
+ const res = await this.openSseStream(reqUrl, lastSeq, signal);
786
810
  if (!res.ok) {
787
811
  throw await this.errorFromResponse(res);
788
812
  }
@@ -885,18 +909,72 @@ var MantyxClient = class {
885
909
  absoluteUrl(path) {
886
910
  return `${this.options.baseUrl}/api/v1/workspaces/${encodeURIComponent(this.options.workspaceSlug)}${path}`;
887
911
  }
888
- authHeaders() {
889
- return { Authorization: `Bearer ${this.options.apiKey}` };
912
+ /**
913
+ * Resolve the bearer credential to send on the next request. With a
914
+ * static `apiKey` / `accessToken` this is a synchronous reach into
915
+ * `options.apiKey`; with a {@link TokenSource} it delegates so the
916
+ * source can refresh expired access tokens before we hit the wire.
917
+ *
918
+ * The `reason` is forwarded to the source verbatim. Pass
919
+ * `"unauthorized"` immediately after a 401 so the source forces a
920
+ * refresh rather than handing back its (now-invalid) cached value.
921
+ */
922
+ async resolveBearer(reason = "initial") {
923
+ if (this.options.tokenSource) return this.options.tokenSource(reason);
924
+ return this.options.apiKey;
925
+ }
926
+ /**
927
+ * Open an SSE stream against `reqUrl` with at-most-one refresh +
928
+ * retry on 401. The caller is responsible for the subsequent
929
+ * `readSseStream` loop; this helper only handles the initial GET.
930
+ * Mid-stream 401s propagate as `MantyxNetworkError` from the read
931
+ * loop and trigger a reconnect via the outer `while` in
932
+ * {@link streamRunEvents}.
933
+ */
934
+ async openSseStream(reqUrl, lastSeq, signal) {
935
+ const openOnce = async (reason) => {
936
+ const auth = await this.authHeaders(reason);
937
+ return this.options.fetch(reqUrl, {
938
+ method: "GET",
939
+ headers: {
940
+ ...auth,
941
+ Accept: "text/event-stream",
942
+ ...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
943
+ },
944
+ ...signal ? { signal } : {}
945
+ }).catch((err) => {
946
+ throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
947
+ cause: err
948
+ });
949
+ });
950
+ };
951
+ const res = await openOnce("initial");
952
+ if (res.status === 401 && this.options.tokenSource !== null) {
953
+ try {
954
+ await res.text();
955
+ } catch {
956
+ }
957
+ return openOnce("unauthorized");
958
+ }
959
+ return res;
960
+ }
961
+ async authHeaders(reason = "initial") {
962
+ const bearer = await this.resolveBearer(reason);
963
+ return { Authorization: `Bearer ${bearer}` };
890
964
  }
891
965
  async request(args) {
966
+ return this.requestWithRetry(args, "initial");
967
+ }
968
+ async requestWithRetry(args, reason) {
892
969
  const url = this.absoluteUrl(args.path);
893
970
  const ctrl = new AbortController();
894
971
  const t = setTimeout(() => ctrl.abort(), args.timeoutMs ?? this.options.timeoutMs);
895
972
  try {
973
+ const auth = await this.authHeaders(reason);
896
974
  const res = await this.options.fetch(url, {
897
975
  method: args.method,
898
976
  headers: {
899
- ...this.authHeaders(),
977
+ ...auth,
900
978
  ...args.body !== void 0 ? { "Content-Type": "application/json" } : {},
901
979
  Accept: "application/json"
902
980
  },
@@ -909,6 +987,14 @@ var MantyxClient = class {
909
987
  throw new MantyxNetworkError(`Network error: ${err.message}`, { cause: err });
910
988
  });
911
989
  if (!res.ok) {
990
+ if (res.status === 401 && this.options.tokenSource !== null && reason === "initial") {
991
+ try {
992
+ await res.text();
993
+ } catch {
994
+ }
995
+ clearTimeout(t);
996
+ return this.requestWithRetry(args, "unauthorized");
997
+ }
912
998
  throw await this.errorFromResponse(res);
913
999
  }
914
1000
  const text = await res.text();
@@ -929,7 +1015,12 @@ var MantyxClient = class {
929
1015
  } catch {
930
1016
  }
931
1017
  if (res.status === 401) {
932
- return new MantyxAuthError(body.error ?? "Invalid API key");
1018
+ return new MantyxAuthError(body.error ?? "Invalid API key or OAuth access token");
1019
+ }
1020
+ if (res.status === 403 && (body.error === "insufficient_scope" || body.code === "insufficient_scope")) {
1021
+ const required = parseRequiredScopes(body.required, res.headers.get("WWW-Authenticate"));
1022
+ const msg = required.length > 0 ? `Missing OAuth scope${required.length > 1 ? "s" : ""}: ${required.join(", ")}` : "OAuth access token is missing a required scope";
1023
+ return new MantyxScopeError(msg, required);
933
1024
  }
934
1025
  return new MantyxError(body.error ?? `HTTP ${res.status}`, {
935
1026
  code: body.code ?? `http_${res.status}`,
@@ -1295,24 +1386,342 @@ function parseRunOutput(result, validator) {
1295
1386
  function sleep(ms) {
1296
1387
  return new Promise((r) => setTimeout(r, ms));
1297
1388
  }
1389
+ function resolveCredential(opts) {
1390
+ const apiKey = typeof opts.apiKey === "string" ? opts.apiKey : "";
1391
+ const accessToken = typeof opts.accessToken === "string" ? opts.accessToken : "";
1392
+ const tokenSource = typeof opts.tokenSource === "function" ? opts.tokenSource : null;
1393
+ const provided = [apiKey ? "apiKey" : "", accessToken ? "accessToken" : "", tokenSource ? "tokenSource" : ""].filter((s) => s.length > 0);
1394
+ if (provided.length > 1) {
1395
+ throw new MantyxError(
1396
+ `Pass exactly one of \`apiKey\`, \`accessToken\`, or \`tokenSource\` \u2014 got ${provided.join(" + ")}.`
1397
+ );
1398
+ }
1399
+ if (provided.length === 0) {
1400
+ throw new MantyxError(
1401
+ "One of `apiKey` (workspace API key), `accessToken` (OAuth access token), or `tokenSource` (dynamic credential provider) is required"
1402
+ );
1403
+ }
1404
+ return {
1405
+ credential: apiKey || accessToken,
1406
+ tokenSource
1407
+ };
1408
+ }
1409
+ function parseRequiredScopes(bodyRequired, wwwAuthenticate) {
1410
+ if (Array.isArray(bodyRequired)) {
1411
+ return bodyRequired.filter((s) => typeof s === "string" && s.length > 0);
1412
+ }
1413
+ if (typeof bodyRequired === "string" && bodyRequired.length > 0) {
1414
+ return [bodyRequired];
1415
+ }
1416
+ if (typeof wwwAuthenticate === "string") {
1417
+ const m = /scope="([^"]+)"/i.exec(wwwAuthenticate);
1418
+ if (m && m[1]) {
1419
+ return m[1].split(/\s+/).filter((s) => s.length > 0);
1420
+ }
1421
+ }
1422
+ return [];
1423
+ }
1424
+
1425
+ // src/oauth.ts
1426
+ var import_node_buffer = require("buffer");
1427
+ var import_node_crypto = require("crypto");
1428
+ var DEFAULT_OAUTH_BASE_URL = "https://app.mantyx.io";
1429
+ var DEFAULT_REFRESH_SKEW_MS = 6e4;
1430
+ var MantyxOAuthError = class extends MantyxError {
1431
+ oauthError;
1432
+ oauthErrorDescription;
1433
+ constructor(oauthError, oauthErrorDescription, status) {
1434
+ const message = oauthErrorDescription ? `OAuth ${oauthError}: ${oauthErrorDescription}` : `OAuth ${oauthError}`;
1435
+ super(message, { code: oauthError, status });
1436
+ this.name = "MantyxOAuthError";
1437
+ this.oauthError = oauthError;
1438
+ this.oauthErrorDescription = oauthErrorDescription;
1439
+ }
1440
+ };
1441
+ var MantyxOAuthClient = class {
1442
+ clientId;
1443
+ baseUrl;
1444
+ clientSecret;
1445
+ fetchImpl;
1446
+ timeoutMs;
1447
+ constructor(opts) {
1448
+ if (!opts.clientId) {
1449
+ throw new MantyxError("`clientId` is required for MantyxOAuthClient");
1450
+ }
1451
+ if (!opts.clientSecret) {
1452
+ throw new MantyxError("`clientSecret` is required for MantyxOAuthClient");
1453
+ }
1454
+ const f = opts.fetch ?? globalThis.fetch;
1455
+ if (typeof f !== "function") {
1456
+ throw new MantyxError(
1457
+ "Global fetch is not available; pass a custom `fetch` implementation in MantyxOAuthClientOptions."
1458
+ );
1459
+ }
1460
+ this.clientId = opts.clientId;
1461
+ this.clientSecret = opts.clientSecret;
1462
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_OAUTH_BASE_URL).replace(/\/+$/, "");
1463
+ this.fetchImpl = f;
1464
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
1465
+ }
1466
+ /**
1467
+ * Swap an authorization-code + PKCE verifier for the initial
1468
+ * `{access_token, refresh_token}` pair. Call this exactly once per
1469
+ * sign-in after the browser/native redirect lands back on your
1470
+ * `redirectUri` with a `code` parameter. Persist the returned
1471
+ * `refreshToken` against the user record — it is long-lived and
1472
+ * non-rotating per `docs/oauth.md` §"Token lifetimes & lifecycle".
1473
+ */
1474
+ async exchangeAuthorizationCode(opts) {
1475
+ return this.token({
1476
+ grant_type: "authorization_code",
1477
+ code: opts.code,
1478
+ redirect_uri: opts.redirectUri,
1479
+ code_verifier: opts.codeVerifier
1480
+ });
1481
+ }
1482
+ /**
1483
+ * Mint a fresh access token from a stored refresh token. The
1484
+ * returned `refreshToken` is identical to the input — the field is
1485
+ * surfaced for symmetry with {@link exchangeAuthorizationCode} only.
1486
+ *
1487
+ * On `400 invalid_grant` the refresh token has been revoked (or its
1488
+ * grant / app was deleted); the SDK surfaces a
1489
+ * {@link MantyxOAuthError} and callers must drive a fresh sign-in.
1490
+ */
1491
+ async refresh(opts) {
1492
+ if (!opts.refreshToken) {
1493
+ throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refresh");
1494
+ }
1495
+ const body = {
1496
+ grant_type: "refresh_token",
1497
+ refresh_token: opts.refreshToken
1498
+ };
1499
+ const scope = normalizeScope(opts.scope);
1500
+ if (scope !== void 0) body.scope = scope;
1501
+ return this.token(body);
1502
+ }
1503
+ /**
1504
+ * Request a workspace-scoped access token without a user via the
1505
+ * `client_credentials` grant. Available only on private OAuth apps
1506
+ * that were registered with `allowsClientCredentials: true`. No
1507
+ * refresh token is issued; re-call this method whenever a new
1508
+ * access token is needed.
1509
+ */
1510
+ async clientCredentials(opts = {}) {
1511
+ const body = {
1512
+ grant_type: "client_credentials"
1513
+ };
1514
+ const scope = normalizeScope(opts.scope);
1515
+ if (scope !== void 0) body.scope = scope;
1516
+ return this.token(body);
1517
+ }
1518
+ /**
1519
+ * Revoke an access or refresh token (RFC 7009). The server always
1520
+ * returns 200, even for unknown tokens. Revoking a **refresh**
1521
+ * token kills the refresh and every live access token tied to its
1522
+ * grant; revoking an **access** token kills only that one.
1523
+ */
1524
+ async revoke(opts) {
1525
+ if (!opts.token) {
1526
+ throw new MantyxError("`token` is required for MantyxOAuthClient.revoke");
1527
+ }
1528
+ await this.formPost("/api/oauth/revoke", {
1529
+ token: opts.token
1530
+ });
1531
+ }
1532
+ /**
1533
+ * Build a long-lived {@link TokenSource} that re-mints access
1534
+ * tokens from the supplied refresh token. Pass the returned source
1535
+ * to `new MantyxClient({ tokenSource, workspaceSlug, ... })`. The
1536
+ * source caches the access token in-memory and refreshes
1537
+ * proactively when the cached value is within `refreshSkewMs` of
1538
+ * `expiresAt`, or eagerly when `MantyxClient` reports a 401.
1539
+ */
1540
+ refreshTokenSource(opts) {
1541
+ if (!opts.refreshToken) {
1542
+ throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refreshTokenSource");
1543
+ }
1544
+ const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
1545
+ const cache = { token: opts.initialToken, inflight: null };
1546
+ const refreshToken = opts.refreshToken;
1547
+ return makeTokenSource(cache, skew, async () => {
1548
+ return this.refresh({ refreshToken, scope: opts.scope });
1549
+ });
1550
+ }
1551
+ /**
1552
+ * Build a long-lived {@link TokenSource} backed by the
1553
+ * `client_credentials` grant. On every refresh the source re-mints
1554
+ * a workspace-scoped access token by calling the token endpoint
1555
+ * with `grant_type=client_credentials`. Available only on private
1556
+ * apps with `allowsClientCredentials: true`.
1557
+ */
1558
+ clientCredentialsTokenSource(opts = {}) {
1559
+ const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
1560
+ const cache = { token: void 0, inflight: null };
1561
+ return makeTokenSource(cache, skew, async () => {
1562
+ return this.clientCredentials({ scope: opts.scope });
1563
+ });
1564
+ }
1565
+ // -------------------------------------------------------------- internals
1566
+ /**
1567
+ * POST `application/x-www-form-urlencoded` to `/api/oauth/token` and
1568
+ * decode the {@link OAuthToken} response. Always injects `client_id`
1569
+ * + `client_secret` from the constructor.
1570
+ */
1571
+ async token(body) {
1572
+ const res = await this.formPost("/api/oauth/token", body);
1573
+ let parsed = {};
1574
+ try {
1575
+ parsed = await res.json();
1576
+ } catch {
1577
+ throw new MantyxOAuthError(
1578
+ "invalid_response",
1579
+ "Token endpoint returned a non-JSON response",
1580
+ res.status
1581
+ );
1582
+ }
1583
+ const accessToken = typeof parsed.access_token === "string" ? parsed.access_token : "";
1584
+ if (!accessToken) {
1585
+ throw new MantyxOAuthError(
1586
+ "invalid_response",
1587
+ "Token endpoint response is missing `access_token`",
1588
+ res.status
1589
+ );
1590
+ }
1591
+ const expiresIn = typeof parsed.expires_in === "number" ? parsed.expires_in : 3600;
1592
+ return {
1593
+ accessToken,
1594
+ refreshToken: typeof parsed.refresh_token === "string" ? parsed.refresh_token : void 0,
1595
+ tokenType: typeof parsed.token_type === "string" ? parsed.token_type : "Bearer",
1596
+ expiresIn,
1597
+ expiresAt: Date.now() + expiresIn * 1e3,
1598
+ scope: typeof parsed.scope === "string" ? parsed.scope : void 0
1599
+ };
1600
+ }
1601
+ async formPost(path, body) {
1602
+ const url = `${this.baseUrl}${path}`;
1603
+ const params = new URLSearchParams({
1604
+ ...body,
1605
+ client_id: this.clientId,
1606
+ client_secret: this.clientSecret
1607
+ });
1608
+ const ctrl = new AbortController();
1609
+ const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
1610
+ let res;
1611
+ try {
1612
+ res = await this.fetchImpl(url, {
1613
+ method: "POST",
1614
+ headers: {
1615
+ "Content-Type": "application/x-www-form-urlencoded",
1616
+ Accept: "application/json"
1617
+ },
1618
+ body: params.toString(),
1619
+ signal: ctrl.signal
1620
+ });
1621
+ } catch (err) {
1622
+ if (ctrl.signal.aborted) {
1623
+ throw new MantyxNetworkError(`OAuth request timed out after ${this.timeoutMs}ms`);
1624
+ }
1625
+ throw new MantyxNetworkError(`OAuth network error: ${err.message}`, {
1626
+ cause: err
1627
+ });
1628
+ } finally {
1629
+ clearTimeout(t);
1630
+ }
1631
+ if (!res.ok) {
1632
+ let errBody = {};
1633
+ try {
1634
+ errBody = await res.json();
1635
+ } catch {
1636
+ }
1637
+ const oauthError = typeof errBody.error === "string" ? errBody.error : `http_${res.status}`;
1638
+ const desc = typeof errBody.error_description === "string" ? errBody.error_description : void 0;
1639
+ throw new MantyxOAuthError(oauthError, desc, res.status);
1640
+ }
1641
+ return res;
1642
+ }
1643
+ };
1644
+ function generatePkceVerifier(length = 64) {
1645
+ if (length < 43 || length > 128) {
1646
+ throw new MantyxError("PKCE code_verifier length must be in [43, 128]");
1647
+ }
1648
+ const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
1649
+ const bytes = (0, import_node_crypto.randomBytes)(length);
1650
+ let out = "";
1651
+ for (let i = 0; i < length; i++) {
1652
+ out += ALPHABET[bytes[i] % ALPHABET.length];
1653
+ }
1654
+ return out;
1655
+ }
1656
+ function pkceChallenge(verifier) {
1657
+ const hash = (0, import_node_crypto.createHash)("sha256").update(verifier, "utf8").digest();
1658
+ return import_node_buffer.Buffer.from(hash).toString("base64").replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
1659
+ }
1660
+ function makeTokenSource(cache, skewMs, mint) {
1661
+ return async (reason = "initial") => {
1662
+ if (reason !== "unauthorized" && cache.token && !isExpiring(cache.token, skewMs)) {
1663
+ return cache.token.accessToken;
1664
+ }
1665
+ if (cache.inflight) {
1666
+ const t = await cache.inflight;
1667
+ if (reason === "unauthorized" && t === cache.token) {
1668
+ } else {
1669
+ return t.accessToken;
1670
+ }
1671
+ }
1672
+ cache.inflight = mint().then(
1673
+ (t) => {
1674
+ cache.token = t;
1675
+ return t;
1676
+ },
1677
+ (err) => {
1678
+ throw err;
1679
+ }
1680
+ );
1681
+ try {
1682
+ const t = await cache.inflight;
1683
+ return t.accessToken;
1684
+ } finally {
1685
+ cache.inflight = null;
1686
+ }
1687
+ };
1688
+ }
1689
+ function isExpiring(token, skewMs) {
1690
+ return token.expiresAt - Date.now() <= skewMs;
1691
+ }
1692
+ function normalizeScope(scope) {
1693
+ if (scope === void 0) return void 0;
1694
+ if (typeof scope === "string") {
1695
+ const trimmed = scope.trim();
1696
+ return trimmed.length > 0 ? trimmed : void 0;
1697
+ }
1698
+ const joined = scope.filter((s) => typeof s === "string" && s.length > 0).join(" ");
1699
+ return joined.length > 0 ? joined : void 0;
1700
+ }
1298
1701
 
1299
1702
  // src/version.ts
1300
- var SDK_VERSION = "0.9.0";
1703
+ var SDK_VERSION = "0.10.0";
1301
1704
  // Annotate the CommonJS export names for ESM import in node:
1302
1705
  0 && (module.exports = {
1303
1706
  AgentSession,
1304
1707
  DEFAULT_BASE_URL,
1708
+ DEFAULT_OAUTH_BASE_URL,
1709
+ DEFAULT_REFRESH_SKEW_MS,
1305
1710
  MantyxAuthError,
1306
1711
  MantyxClient,
1307
1712
  MantyxError,
1308
1713
  MantyxNetworkError,
1714
+ MantyxOAuthClient,
1715
+ MantyxOAuthError,
1309
1716
  MantyxParseError,
1310
1717
  MantyxRunError,
1718
+ MantyxScopeError,
1311
1719
  MantyxToolError,
1312
1720
  SDK_VERSION,
1313
1721
  defineLocalA2A,
1314
1722
  defineLocalMcp,
1315
1723
  defineLocalTool,
1724
+ generatePkceVerifier,
1316
1725
  isLocalA2ATool,
1317
1726
  isLocalMcpServer,
1318
1727
  isLocalTool,
@@ -1321,6 +1730,7 @@ var SDK_VERSION = "0.9.0";
1321
1730
  mantyxPluginTool,
1322
1731
  mantyxTool,
1323
1732
  parseRunOutput,
1733
+ pkceChallenge,
1324
1734
  readSseStream,
1325
1735
  toToolParametersWire,
1326
1736
  zodToJsonSchema