@mantyx/sdk 0.9.1 → 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/CHANGELOG.md +9 -2
- package/README.md +81 -2
- package/dist/a2a-server.cjs.map +1 -1
- package/dist/a2a-server.d.cts +1 -1
- package/dist/a2a-server.d.ts +1 -1
- package/dist/a2a-server.js +1 -1
- package/dist/{chunk-AE7ZSLBH.js → chunk-XMUCELMH.js} +126 -24
- package/dist/chunk-XMUCELMH.js.map +1 -0
- package/dist/{client-BB6cjfsz.d.cts → client-CZUVldDx.d.cts} +401 -3
- package/dist/{client-BB6cjfsz.d.ts → client-CZUVldDx.d.ts} +401 -3
- package/dist/index.cjs +354 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -93
- package/dist/index.d.ts +3 -93
- package/dist/index.js +227 -2
- package/dist/index.js.map +1 -1
- package/docs/agent-runs-protocol.md +123 -113
- package/docs/oauth.md +356 -0
- package/docs/wire-protocol.md +1102 -0
- package/package.json +1 -1
- package/dist/chunk-AE7ZSLBH.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -32,12 +32,17 @@ 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,
|
|
@@ -80,11 +85,23 @@ var MantyxNetworkError = class extends MantyxError {
|
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
var MantyxAuthError = class extends MantyxError {
|
|
83
|
-
constructor(message = "Invalid or missing API key") {
|
|
88
|
+
constructor(message = "Invalid or missing API key / OAuth access token") {
|
|
84
89
|
super(message, { code: "unauthorized", status: 401 });
|
|
85
90
|
this.name = "MantyxAuthError";
|
|
86
91
|
}
|
|
87
92
|
};
|
|
93
|
+
var MantyxScopeError = class extends MantyxError {
|
|
94
|
+
/**
|
|
95
|
+
* Scope(s) the route demanded. Always at least one entry; usually
|
|
96
|
+
* exactly one. New routes may demand more scopes in the future.
|
|
97
|
+
*/
|
|
98
|
+
requiredScopes;
|
|
99
|
+
constructor(message, requiredScopes) {
|
|
100
|
+
super(message, { code: "insufficient_scope", status: 403 });
|
|
101
|
+
this.name = "MantyxScopeError";
|
|
102
|
+
this.requiredScopes = [...requiredScopes];
|
|
103
|
+
}
|
|
104
|
+
};
|
|
88
105
|
var MantyxToolError = class extends MantyxError {
|
|
89
106
|
toolName;
|
|
90
107
|
constructor(toolName, message) {
|
|
@@ -635,9 +652,7 @@ var DEFAULT_BASE_URL = "https://app.mantyx.io";
|
|
|
635
652
|
var MantyxClient = class {
|
|
636
653
|
options;
|
|
637
654
|
constructor(opts) {
|
|
638
|
-
|
|
639
|
-
throw new MantyxError("apiKey is required");
|
|
640
|
-
}
|
|
655
|
+
const { credential, tokenSource } = resolveCredential(opts);
|
|
641
656
|
if (!opts.workspaceSlug || typeof opts.workspaceSlug !== "string") {
|
|
642
657
|
throw new MantyxError("workspaceSlug is required");
|
|
643
658
|
}
|
|
@@ -648,11 +663,12 @@ var MantyxClient = class {
|
|
|
648
663
|
);
|
|
649
664
|
}
|
|
650
665
|
this.options = {
|
|
651
|
-
apiKey:
|
|
666
|
+
apiKey: credential,
|
|
652
667
|
workspaceSlug: opts.workspaceSlug,
|
|
653
668
|
baseUrl: (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
654
669
|
fetch: f,
|
|
655
|
-
timeoutMs: opts.timeoutMs ?? 6e4
|
|
670
|
+
timeoutMs: opts.timeoutMs ?? 6e4,
|
|
671
|
+
tokenSource
|
|
656
672
|
};
|
|
657
673
|
}
|
|
658
674
|
// -------------------------------------------------------------- Models
|
|
@@ -788,19 +804,7 @@ var MantyxClient = class {
|
|
|
788
804
|
let lastSeq = 0;
|
|
789
805
|
while (true) {
|
|
790
806
|
const reqUrl = lastSeq > 0 ? `${url}?lastSeq=${lastSeq}` : url;
|
|
791
|
-
const res = await this.
|
|
792
|
-
method: "GET",
|
|
793
|
-
headers: {
|
|
794
|
-
...this.authHeaders(),
|
|
795
|
-
Accept: "text/event-stream",
|
|
796
|
-
...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
|
|
797
|
-
},
|
|
798
|
-
...signal ? { signal } : {}
|
|
799
|
-
}).catch((err) => {
|
|
800
|
-
throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
|
|
801
|
-
cause: err
|
|
802
|
-
});
|
|
803
|
-
});
|
|
807
|
+
const res = await this.openSseStream(reqUrl, lastSeq, signal);
|
|
804
808
|
if (!res.ok) {
|
|
805
809
|
throw await this.errorFromResponse(res);
|
|
806
810
|
}
|
|
@@ -903,18 +907,72 @@ var MantyxClient = class {
|
|
|
903
907
|
absoluteUrl(path) {
|
|
904
908
|
return `${this.options.baseUrl}/api/v1/workspaces/${encodeURIComponent(this.options.workspaceSlug)}${path}`;
|
|
905
909
|
}
|
|
906
|
-
|
|
907
|
-
|
|
910
|
+
/**
|
|
911
|
+
* Resolve the bearer credential to send on the next request. With a
|
|
912
|
+
* static `apiKey` / `accessToken` this is a synchronous reach into
|
|
913
|
+
* `options.apiKey`; with a {@link TokenSource} it delegates so the
|
|
914
|
+
* source can refresh expired access tokens before we hit the wire.
|
|
915
|
+
*
|
|
916
|
+
* The `reason` is forwarded to the source verbatim. Pass
|
|
917
|
+
* `"unauthorized"` immediately after a 401 so the source forces a
|
|
918
|
+
* refresh rather than handing back its (now-invalid) cached value.
|
|
919
|
+
*/
|
|
920
|
+
async resolveBearer(reason = "initial") {
|
|
921
|
+
if (this.options.tokenSource) return this.options.tokenSource(reason);
|
|
922
|
+
return this.options.apiKey;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Open an SSE stream against `reqUrl` with at-most-one refresh +
|
|
926
|
+
* retry on 401. The caller is responsible for the subsequent
|
|
927
|
+
* `readSseStream` loop; this helper only handles the initial GET.
|
|
928
|
+
* Mid-stream 401s propagate as `MantyxNetworkError` from the read
|
|
929
|
+
* loop and trigger a reconnect via the outer `while` in
|
|
930
|
+
* {@link streamRunEvents}.
|
|
931
|
+
*/
|
|
932
|
+
async openSseStream(reqUrl, lastSeq, signal) {
|
|
933
|
+
const openOnce = async (reason) => {
|
|
934
|
+
const auth = await this.authHeaders(reason);
|
|
935
|
+
return this.options.fetch(reqUrl, {
|
|
936
|
+
method: "GET",
|
|
937
|
+
headers: {
|
|
938
|
+
...auth,
|
|
939
|
+
Accept: "text/event-stream",
|
|
940
|
+
...lastSeq > 0 ? { "Last-Event-ID": String(lastSeq) } : {}
|
|
941
|
+
},
|
|
942
|
+
...signal ? { signal } : {}
|
|
943
|
+
}).catch((err) => {
|
|
944
|
+
throw new MantyxNetworkError(`Failed to open SSE stream: ${err.message}`, {
|
|
945
|
+
cause: err
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
};
|
|
949
|
+
const res = await openOnce("initial");
|
|
950
|
+
if (res.status === 401 && this.options.tokenSource !== null) {
|
|
951
|
+
try {
|
|
952
|
+
await res.text();
|
|
953
|
+
} catch {
|
|
954
|
+
}
|
|
955
|
+
return openOnce("unauthorized");
|
|
956
|
+
}
|
|
957
|
+
return res;
|
|
958
|
+
}
|
|
959
|
+
async authHeaders(reason = "initial") {
|
|
960
|
+
const bearer = await this.resolveBearer(reason);
|
|
961
|
+
return { Authorization: `Bearer ${bearer}` };
|
|
908
962
|
}
|
|
909
963
|
async request(args) {
|
|
964
|
+
return this.requestWithRetry(args, "initial");
|
|
965
|
+
}
|
|
966
|
+
async requestWithRetry(args, reason) {
|
|
910
967
|
const url = this.absoluteUrl(args.path);
|
|
911
968
|
const ctrl = new AbortController();
|
|
912
969
|
const t = setTimeout(() => ctrl.abort(), args.timeoutMs ?? this.options.timeoutMs);
|
|
913
970
|
try {
|
|
971
|
+
const auth = await this.authHeaders(reason);
|
|
914
972
|
const res = await this.options.fetch(url, {
|
|
915
973
|
method: args.method,
|
|
916
974
|
headers: {
|
|
917
|
-
...
|
|
975
|
+
...auth,
|
|
918
976
|
...args.body !== void 0 ? { "Content-Type": "application/json" } : {},
|
|
919
977
|
Accept: "application/json"
|
|
920
978
|
},
|
|
@@ -927,6 +985,14 @@ var MantyxClient = class {
|
|
|
927
985
|
throw new MantyxNetworkError(`Network error: ${err.message}`, { cause: err });
|
|
928
986
|
});
|
|
929
987
|
if (!res.ok) {
|
|
988
|
+
if (res.status === 401 && this.options.tokenSource !== null && reason === "initial") {
|
|
989
|
+
try {
|
|
990
|
+
await res.text();
|
|
991
|
+
} catch {
|
|
992
|
+
}
|
|
993
|
+
clearTimeout(t);
|
|
994
|
+
return this.requestWithRetry(args, "unauthorized");
|
|
995
|
+
}
|
|
930
996
|
throw await this.errorFromResponse(res);
|
|
931
997
|
}
|
|
932
998
|
const text = await res.text();
|
|
@@ -947,7 +1013,12 @@ var MantyxClient = class {
|
|
|
947
1013
|
} catch {
|
|
948
1014
|
}
|
|
949
1015
|
if (res.status === 401) {
|
|
950
|
-
return new MantyxAuthError(body.error ?? "Invalid API key");
|
|
1016
|
+
return new MantyxAuthError(body.error ?? "Invalid API key or OAuth access token");
|
|
1017
|
+
}
|
|
1018
|
+
if (res.status === 403 && (body.error === "insufficient_scope" || body.code === "insufficient_scope")) {
|
|
1019
|
+
const required = parseRequiredScopes(body.required, res.headers.get("WWW-Authenticate"));
|
|
1020
|
+
const msg = required.length > 0 ? `Missing OAuth scope${required.length > 1 ? "s" : ""}: ${required.join(", ")}` : "OAuth access token is missing a required scope";
|
|
1021
|
+
return new MantyxScopeError(msg, required);
|
|
951
1022
|
}
|
|
952
1023
|
return new MantyxError(body.error ?? `HTTP ${res.status}`, {
|
|
953
1024
|
code: body.code ?? `http_${res.status}`,
|
|
@@ -1313,19 +1384,278 @@ function parseRunOutput(result, validator) {
|
|
|
1313
1384
|
function sleep(ms) {
|
|
1314
1385
|
return new Promise((r) => setTimeout(r, ms));
|
|
1315
1386
|
}
|
|
1387
|
+
function resolveCredential(opts) {
|
|
1388
|
+
const apiKey = typeof opts.apiKey === "string" ? opts.apiKey : "";
|
|
1389
|
+
const accessToken = typeof opts.accessToken === "string" ? opts.accessToken : "";
|
|
1390
|
+
const tokenSource = typeof opts.tokenSource === "function" ? opts.tokenSource : null;
|
|
1391
|
+
const provided = [apiKey ? "apiKey" : "", accessToken ? "accessToken" : "", tokenSource ? "tokenSource" : ""].filter((s) => s.length > 0);
|
|
1392
|
+
if (provided.length > 1) {
|
|
1393
|
+
throw new MantyxError(
|
|
1394
|
+
`Pass exactly one of \`apiKey\`, \`accessToken\`, or \`tokenSource\` \u2014 got ${provided.join(" + ")}.`
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
if (provided.length === 0) {
|
|
1398
|
+
throw new MantyxError(
|
|
1399
|
+
"One of `apiKey` (workspace API key), `accessToken` (OAuth access token), or `tokenSource` (dynamic credential provider) is required"
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
return {
|
|
1403
|
+
credential: apiKey || accessToken,
|
|
1404
|
+
tokenSource
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
function parseRequiredScopes(bodyRequired, wwwAuthenticate) {
|
|
1408
|
+
if (Array.isArray(bodyRequired)) {
|
|
1409
|
+
return bodyRequired.filter((s) => typeof s === "string" && s.length > 0);
|
|
1410
|
+
}
|
|
1411
|
+
if (typeof bodyRequired === "string" && bodyRequired.length > 0) {
|
|
1412
|
+
return [bodyRequired];
|
|
1413
|
+
}
|
|
1414
|
+
if (typeof wwwAuthenticate === "string") {
|
|
1415
|
+
const m = /scope="([^"]+)"/i.exec(wwwAuthenticate);
|
|
1416
|
+
if (m && m[1]) {
|
|
1417
|
+
return m[1].split(/\s+/).filter((s) => s.length > 0);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return [];
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// src/oauth.ts
|
|
1424
|
+
var DEFAULT_OAUTH_BASE_URL = "https://app.mantyx.io";
|
|
1425
|
+
var DEFAULT_REFRESH_SKEW_MS = 6e4;
|
|
1426
|
+
var MantyxOAuthError = class extends MantyxError {
|
|
1427
|
+
oauthError;
|
|
1428
|
+
oauthErrorDescription;
|
|
1429
|
+
constructor(oauthError, oauthErrorDescription, status) {
|
|
1430
|
+
const message = oauthErrorDescription ? `OAuth ${oauthError}: ${oauthErrorDescription}` : `OAuth ${oauthError}`;
|
|
1431
|
+
super(message, { code: oauthError, status });
|
|
1432
|
+
this.name = "MantyxOAuthError";
|
|
1433
|
+
this.oauthError = oauthError;
|
|
1434
|
+
this.oauthErrorDescription = oauthErrorDescription;
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
var MantyxOAuthClient = class {
|
|
1438
|
+
clientId;
|
|
1439
|
+
baseUrl;
|
|
1440
|
+
clientSecret;
|
|
1441
|
+
fetchImpl;
|
|
1442
|
+
timeoutMs;
|
|
1443
|
+
constructor(opts) {
|
|
1444
|
+
if (!opts.clientId) {
|
|
1445
|
+
throw new MantyxError("`clientId` is required for MantyxOAuthClient");
|
|
1446
|
+
}
|
|
1447
|
+
if (!opts.clientSecret) {
|
|
1448
|
+
throw new MantyxError("`clientSecret` is required for MantyxOAuthClient");
|
|
1449
|
+
}
|
|
1450
|
+
const f = opts.fetch ?? globalThis.fetch;
|
|
1451
|
+
if (typeof f !== "function") {
|
|
1452
|
+
throw new MantyxError(
|
|
1453
|
+
"Global fetch is not available; pass a custom `fetch` implementation in MantyxOAuthClientOptions."
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
this.clientId = opts.clientId;
|
|
1457
|
+
this.clientSecret = opts.clientSecret;
|
|
1458
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_OAUTH_BASE_URL).replace(/\/+$/, "");
|
|
1459
|
+
this.fetchImpl = f;
|
|
1460
|
+
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Mint a fresh access token from a stored refresh token. The
|
|
1464
|
+
* returned `refreshToken` is identical to the input — refresh
|
|
1465
|
+
* tokens are persistent and non-rotating, so the field is
|
|
1466
|
+
* surfaced only for symmetry with the response shape.
|
|
1467
|
+
*
|
|
1468
|
+
* On `400 invalid_grant` the refresh token has been revoked (or its
|
|
1469
|
+
* grant / app was deleted); the SDK surfaces a
|
|
1470
|
+
* {@link MantyxOAuthError} and callers must drive a fresh sign-in.
|
|
1471
|
+
*/
|
|
1472
|
+
async refresh(opts) {
|
|
1473
|
+
if (!opts.refreshToken) {
|
|
1474
|
+
throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refresh");
|
|
1475
|
+
}
|
|
1476
|
+
const body = {
|
|
1477
|
+
grant_type: "refresh_token",
|
|
1478
|
+
refresh_token: opts.refreshToken
|
|
1479
|
+
};
|
|
1480
|
+
const scope = normalizeScope(opts.scope);
|
|
1481
|
+
if (scope !== void 0) body.scope = scope;
|
|
1482
|
+
return this.token(body);
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Revoke an access or refresh token (RFC 7009). The server always
|
|
1486
|
+
* returns 200, even for unknown tokens. Revoking a **refresh**
|
|
1487
|
+
* token kills the refresh and every live access token tied to its
|
|
1488
|
+
* grant; revoking an **access** token kills only that one.
|
|
1489
|
+
*/
|
|
1490
|
+
async revoke(opts) {
|
|
1491
|
+
if (!opts.token) {
|
|
1492
|
+
throw new MantyxError("`token` is required for MantyxOAuthClient.revoke");
|
|
1493
|
+
}
|
|
1494
|
+
await this.formPost("/api/oauth/revoke", {
|
|
1495
|
+
token: opts.token
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Build a long-lived {@link TokenSource} that re-mints access
|
|
1500
|
+
* tokens from the supplied refresh token. Pass the returned source
|
|
1501
|
+
* to `new MantyxClient({ tokenSource, workspaceSlug, ... })`. The
|
|
1502
|
+
* source caches the access token in-memory and refreshes
|
|
1503
|
+
* proactively when the cached value is within `refreshSkewMs` of
|
|
1504
|
+
* `expiresAt`, or eagerly when `MantyxClient` reports a 401.
|
|
1505
|
+
*
|
|
1506
|
+
* Pass `initialToken` if the calling app already has a non-expired
|
|
1507
|
+
* access token in hand (e.g. straight out of the sign-in flow) to
|
|
1508
|
+
* avoid an extra round-trip on the first request.
|
|
1509
|
+
*/
|
|
1510
|
+
refreshTokenSource(opts) {
|
|
1511
|
+
if (!opts.refreshToken) {
|
|
1512
|
+
throw new MantyxError("`refreshToken` is required for MantyxOAuthClient.refreshTokenSource");
|
|
1513
|
+
}
|
|
1514
|
+
const skew = opts.refreshSkewMs ?? DEFAULT_REFRESH_SKEW_MS;
|
|
1515
|
+
const cache = { token: opts.initialToken, inflight: null };
|
|
1516
|
+
const refreshToken = opts.refreshToken;
|
|
1517
|
+
return makeTokenSource(cache, skew, async () => {
|
|
1518
|
+
return this.refresh({ refreshToken, scope: opts.scope });
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
// -------------------------------------------------------------- internals
|
|
1522
|
+
/**
|
|
1523
|
+
* POST `application/x-www-form-urlencoded` to `/api/oauth/token` and
|
|
1524
|
+
* decode the {@link OAuthToken} response. Always injects `client_id`
|
|
1525
|
+
* + `client_secret` from the constructor.
|
|
1526
|
+
*/
|
|
1527
|
+
async token(body) {
|
|
1528
|
+
const res = await this.formPost("/api/oauth/token", body);
|
|
1529
|
+
let parsed = {};
|
|
1530
|
+
try {
|
|
1531
|
+
parsed = await res.json();
|
|
1532
|
+
} catch {
|
|
1533
|
+
throw new MantyxOAuthError(
|
|
1534
|
+
"invalid_response",
|
|
1535
|
+
"Token endpoint returned a non-JSON response",
|
|
1536
|
+
res.status
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
const accessToken = typeof parsed.access_token === "string" ? parsed.access_token : "";
|
|
1540
|
+
if (!accessToken) {
|
|
1541
|
+
throw new MantyxOAuthError(
|
|
1542
|
+
"invalid_response",
|
|
1543
|
+
"Token endpoint response is missing `access_token`",
|
|
1544
|
+
res.status
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
const expiresIn = typeof parsed.expires_in === "number" ? parsed.expires_in : 3600;
|
|
1548
|
+
return {
|
|
1549
|
+
accessToken,
|
|
1550
|
+
refreshToken: typeof parsed.refresh_token === "string" ? parsed.refresh_token : void 0,
|
|
1551
|
+
tokenType: typeof parsed.token_type === "string" ? parsed.token_type : "Bearer",
|
|
1552
|
+
expiresIn,
|
|
1553
|
+
expiresAt: Date.now() + expiresIn * 1e3,
|
|
1554
|
+
scope: typeof parsed.scope === "string" ? parsed.scope : void 0
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
async formPost(path, body) {
|
|
1558
|
+
const url = `${this.baseUrl}${path}`;
|
|
1559
|
+
const params = new URLSearchParams({
|
|
1560
|
+
...body,
|
|
1561
|
+
client_id: this.clientId,
|
|
1562
|
+
client_secret: this.clientSecret
|
|
1563
|
+
});
|
|
1564
|
+
const ctrl = new AbortController();
|
|
1565
|
+
const t = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
1566
|
+
let res;
|
|
1567
|
+
try {
|
|
1568
|
+
res = await this.fetchImpl(url, {
|
|
1569
|
+
method: "POST",
|
|
1570
|
+
headers: {
|
|
1571
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1572
|
+
Accept: "application/json"
|
|
1573
|
+
},
|
|
1574
|
+
body: params.toString(),
|
|
1575
|
+
signal: ctrl.signal
|
|
1576
|
+
});
|
|
1577
|
+
} catch (err) {
|
|
1578
|
+
if (ctrl.signal.aborted) {
|
|
1579
|
+
throw new MantyxNetworkError(`OAuth request timed out after ${this.timeoutMs}ms`);
|
|
1580
|
+
}
|
|
1581
|
+
throw new MantyxNetworkError(`OAuth network error: ${err.message}`, {
|
|
1582
|
+
cause: err
|
|
1583
|
+
});
|
|
1584
|
+
} finally {
|
|
1585
|
+
clearTimeout(t);
|
|
1586
|
+
}
|
|
1587
|
+
if (!res.ok) {
|
|
1588
|
+
let errBody = {};
|
|
1589
|
+
try {
|
|
1590
|
+
errBody = await res.json();
|
|
1591
|
+
} catch {
|
|
1592
|
+
}
|
|
1593
|
+
const oauthError = typeof errBody.error === "string" ? errBody.error : `http_${res.status}`;
|
|
1594
|
+
const desc = typeof errBody.error_description === "string" ? errBody.error_description : void 0;
|
|
1595
|
+
throw new MantyxOAuthError(oauthError, desc, res.status);
|
|
1596
|
+
}
|
|
1597
|
+
return res;
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
function makeTokenSource(cache, skewMs, mint) {
|
|
1601
|
+
return async (reason = "initial") => {
|
|
1602
|
+
if (reason !== "unauthorized" && cache.token && !isExpiring(cache.token, skewMs)) {
|
|
1603
|
+
return cache.token.accessToken;
|
|
1604
|
+
}
|
|
1605
|
+
if (cache.inflight) {
|
|
1606
|
+
const t = await cache.inflight;
|
|
1607
|
+
if (reason === "unauthorized" && t === cache.token) {
|
|
1608
|
+
} else {
|
|
1609
|
+
return t.accessToken;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
cache.inflight = mint().then(
|
|
1613
|
+
(t) => {
|
|
1614
|
+
cache.token = t;
|
|
1615
|
+
return t;
|
|
1616
|
+
},
|
|
1617
|
+
(err) => {
|
|
1618
|
+
throw err;
|
|
1619
|
+
}
|
|
1620
|
+
);
|
|
1621
|
+
try {
|
|
1622
|
+
const t = await cache.inflight;
|
|
1623
|
+
return t.accessToken;
|
|
1624
|
+
} finally {
|
|
1625
|
+
cache.inflight = null;
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
function isExpiring(token, skewMs) {
|
|
1630
|
+
return token.expiresAt - Date.now() <= skewMs;
|
|
1631
|
+
}
|
|
1632
|
+
function normalizeScope(scope) {
|
|
1633
|
+
if (scope === void 0) return void 0;
|
|
1634
|
+
if (typeof scope === "string") {
|
|
1635
|
+
const trimmed = scope.trim();
|
|
1636
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1637
|
+
}
|
|
1638
|
+
const joined = scope.filter((s) => typeof s === "string" && s.length > 0).join(" ");
|
|
1639
|
+
return joined.length > 0 ? joined : void 0;
|
|
1640
|
+
}
|
|
1316
1641
|
|
|
1317
1642
|
// src/version.ts
|
|
1318
|
-
var SDK_VERSION = "0.
|
|
1643
|
+
var SDK_VERSION = "0.10.1";
|
|
1319
1644
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1320
1645
|
0 && (module.exports = {
|
|
1321
1646
|
AgentSession,
|
|
1322
1647
|
DEFAULT_BASE_URL,
|
|
1648
|
+
DEFAULT_OAUTH_BASE_URL,
|
|
1649
|
+
DEFAULT_REFRESH_SKEW_MS,
|
|
1323
1650
|
MantyxAuthError,
|
|
1324
1651
|
MantyxClient,
|
|
1325
1652
|
MantyxError,
|
|
1326
1653
|
MantyxNetworkError,
|
|
1654
|
+
MantyxOAuthClient,
|
|
1655
|
+
MantyxOAuthError,
|
|
1327
1656
|
MantyxParseError,
|
|
1328
1657
|
MantyxRunError,
|
|
1658
|
+
MantyxScopeError,
|
|
1329
1659
|
MantyxToolError,
|
|
1330
1660
|
SDK_VERSION,
|
|
1331
1661
|
defineLocalA2A,
|