@axonflow/sdk 1.12.0 → 1.14.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.
@@ -8,6 +8,7 @@ import { generateRequestId, debugLog } from './utils/helpers.js';
8
8
  export class AxonFlow {
9
9
  constructor(config) {
10
10
  this.interceptors = [];
11
+ this.sessionCookie = null;
11
12
  // Set defaults first to determine endpoint
12
13
  const endpoint = config.endpoint || 'https://staging-eu.getaxonflow.com';
13
14
  // Credentials are optional for community/self-hosted deployments
@@ -19,6 +20,7 @@ export class AxonFlow {
19
20
  licenseKey: config.licenseKey,
20
21
  endpoint,
21
22
  orchestratorEndpoint: config.orchestratorEndpoint,
23
+ portalEndpoint: config.portalEndpoint,
22
24
  mode: config.mode || (hasCredentials ? 'production' : 'sandbox'),
23
25
  tenant: config.tenant || 'default',
24
26
  debug: config.debug || false,
@@ -309,6 +311,56 @@ export class AxonFlow {
309
311
  };
310
312
  }
311
313
  }
314
+ /**
315
+ * Check the health of the AxonFlow Orchestrator service.
316
+ *
317
+ * @returns Promise resolving to health status
318
+ * @example
319
+ * ```typescript
320
+ * const health = await axonflow.orchestratorHealthCheck();
321
+ * if (health.status === 'healthy') {
322
+ * console.log('Orchestrator is healthy');
323
+ * }
324
+ * ```
325
+ */
326
+ async orchestratorHealthCheck() {
327
+ const url = `${this.getOrchestratorUrl()}/health`;
328
+ try {
329
+ const response = await fetch(url, {
330
+ method: 'GET',
331
+ signal: AbortSignal.timeout(this.config.timeout),
332
+ });
333
+ if (!response.ok) {
334
+ return {
335
+ status: 'unhealthy',
336
+ components: {
337
+ orchestrator: { status: 'error', message: `HTTP ${response.status}` },
338
+ },
339
+ };
340
+ }
341
+ const data = await response.json();
342
+ return {
343
+ status: data.status === 'healthy' ? 'healthy' : 'degraded',
344
+ version: data.version,
345
+ uptime: data.uptime,
346
+ components: data.components,
347
+ };
348
+ }
349
+ catch (error) {
350
+ if (this.config.debug) {
351
+ debugLog('Orchestrator health check failed', error);
352
+ }
353
+ return {
354
+ status: 'unhealthy',
355
+ components: {
356
+ orchestrator: {
357
+ status: 'error',
358
+ message: error instanceof Error ? error.message : 'Unknown error',
359
+ },
360
+ },
361
+ };
362
+ }
363
+ }
312
364
  /**
313
365
  * Execute a query through AxonFlow with policy enforcement (Proxy Mode).
314
366
  *
@@ -425,15 +477,9 @@ export class AxonFlow {
425
477
  * List all available MCP connectors from the marketplace
426
478
  */
427
479
  async listConnectors() {
428
- const url = `${this.config.endpoint}/api/connectors`;
429
- const response = await fetch(url, {
430
- method: 'GET',
431
- signal: AbortSignal.timeout(this.config.timeout),
432
- });
433
- if (!response.ok) {
434
- throw new Error(`Failed to list connectors: ${response.status} ${response.statusText}`);
435
- }
436
- const connectors = await response.json();
480
+ const response = await this.orchestratorRequest('GET', '/api/v1/connectors');
481
+ // Handle wrapped response
482
+ const connectors = Array.isArray(response) ? response : response.connectors || [];
437
483
  if (this.config.debug) {
438
484
  debugLog('Listed connectors', { count: connectors.length });
439
485
  }
@@ -443,30 +489,41 @@ export class AxonFlow {
443
489
  * Install an MCP connector from the marketplace
444
490
  */
445
491
  async installConnector(request) {
446
- const url = `${this.config.endpoint}/api/connectors/install`;
447
- const headers = {
448
- 'Content-Type': 'application/json',
449
- };
450
- // Add authentication headers
451
- if (this.config.licenseKey) {
452
- headers['X-License-Key'] = this.config.licenseKey;
492
+ // Extract connector_id from request for URL path
493
+ const { connector_id, ...body } = request;
494
+ await this.orchestratorRequest('POST', `/api/v1/connectors/${connector_id}/install`, body);
495
+ if (this.config.debug) {
496
+ debugLog('Connector installed', { name: request.name });
453
497
  }
454
- else if (this.config.apiKey) {
455
- headers['X-Client-Secret'] = this.config.apiKey;
498
+ }
499
+ /**
500
+ * Uninstall an MCP connector
501
+ */
502
+ async uninstallConnector(connectorName) {
503
+ await this.orchestratorRequest('DELETE', `/api/v1/connectors/${connectorName}`);
504
+ if (this.config.debug) {
505
+ debugLog('Connector uninstalled', { name: connectorName });
456
506
  }
457
- const response = await fetch(url, {
458
- method: 'POST',
459
- headers,
460
- body: JSON.stringify(request),
461
- signal: AbortSignal.timeout(this.config.timeout),
462
- });
463
- if (!response.ok) {
464
- const errorText = await response.text();
465
- throw new Error(`Failed to install connector: ${response.status} ${response.statusText} - ${errorText}`);
507
+ }
508
+ /**
509
+ * Get details for a specific connector by ID
510
+ */
511
+ async getConnector(connectorId) {
512
+ const connector = await this.orchestratorRequest('GET', `/api/v1/connectors/${connectorId}`);
513
+ if (this.config.debug) {
514
+ debugLog('Got connector', { id: connectorId });
466
515
  }
516
+ return connector;
517
+ }
518
+ /**
519
+ * Get health status of an installed connector
520
+ */
521
+ async getConnectorHealth(connectorId) {
522
+ const health = await this.orchestratorRequest('GET', `/api/v1/connectors/${connectorId}/health`);
467
523
  if (this.config.debug) {
468
- debugLog('Connector installed', { name: request.name });
524
+ debugLog('Got connector health', { id: connectorId, healthy: health.healthy });
469
525
  }
526
+ return health;
470
527
  }
471
528
  /**
472
529
  * Execute a query against an installed MCP connector
@@ -1191,17 +1248,15 @@ export class AxonFlow {
1191
1248
  * @example
1192
1249
  * ```typescript
1193
1250
  * const policies = await axonflow.listDynamicPolicies({
1194
- * category: 'dynamic-cost',
1251
+ * type: 'cost',
1195
1252
  * enabled: true
1196
1253
  * });
1197
1254
  * ```
1198
1255
  */
1199
1256
  async listDynamicPolicies(options) {
1200
1257
  const params = new URLSearchParams();
1201
- if (options?.category)
1202
- params.set('category', options.category);
1203
- if (options?.tier)
1204
- params.set('tier', options.tier);
1258
+ if (options?.type)
1259
+ params.set('type', options.type);
1205
1260
  if (options?.enabled !== undefined)
1206
1261
  params.set('enabled', String(options.enabled));
1207
1262
  if (options?.limit)
@@ -1215,11 +1270,11 @@ export class AxonFlow {
1215
1270
  if (options?.search)
1216
1271
  params.set('search', options.search);
1217
1272
  const queryString = params.toString();
1218
- const path = `/api/v1/policies${queryString ? `?${queryString}` : ''}`;
1273
+ const path = `/api/v1/policies/dynamic${queryString ? `?${queryString}` : ''}`;
1219
1274
  if (this.config.debug) {
1220
1275
  debugLog('Listing dynamic policies', { options });
1221
1276
  }
1222
- return this.policyRequest('GET', path);
1277
+ return this.orchestratorRequest('GET', path);
1223
1278
  }
1224
1279
  /**
1225
1280
  * Get a specific dynamic policy by ID.
@@ -1231,7 +1286,7 @@ export class AxonFlow {
1231
1286
  if (this.config.debug) {
1232
1287
  debugLog('Getting dynamic policy', { id });
1233
1288
  }
1234
- return this.policyRequest('GET', `/api/v1/policies/${id}`);
1289
+ return this.orchestratorRequest('GET', `/api/v1/policies/dynamic/${id}`);
1235
1290
  }
1236
1291
  /**
1237
1292
  * Create a new dynamic policy.
@@ -1256,7 +1311,7 @@ export class AxonFlow {
1256
1311
  if (this.config.debug) {
1257
1312
  debugLog('Creating dynamic policy', { name: policy.name });
1258
1313
  }
1259
- return this.policyRequest('POST', '/api/v1/policies', policy);
1314
+ return this.orchestratorRequest('POST', '/api/v1/policies/dynamic', policy);
1260
1315
  }
1261
1316
  /**
1262
1317
  * Update an existing dynamic policy.
@@ -1269,7 +1324,7 @@ export class AxonFlow {
1269
1324
  if (this.config.debug) {
1270
1325
  debugLog('Updating dynamic policy', { id, updates: Object.keys(policy) });
1271
1326
  }
1272
- return this.policyRequest('PUT', `/api/v1/policies/${id}`, policy);
1327
+ return this.orchestratorRequest('PUT', `/api/v1/policies/dynamic/${id}`, policy);
1273
1328
  }
1274
1329
  /**
1275
1330
  * Delete a dynamic policy.
@@ -1280,7 +1335,7 @@ export class AxonFlow {
1280
1335
  if (this.config.debug) {
1281
1336
  debugLog('Deleting dynamic policy', { id });
1282
1337
  }
1283
- await this.policyRequest('DELETE', `/api/v1/policies/${id}`);
1338
+ await this.orchestratorRequest('DELETE', `/api/v1/policies/dynamic/${id}`);
1284
1339
  }
1285
1340
  /**
1286
1341
  * Toggle a dynamic policy's enabled status.
@@ -1293,7 +1348,9 @@ export class AxonFlow {
1293
1348
  if (this.config.debug) {
1294
1349
  debugLog('Toggling dynamic policy', { id, enabled });
1295
1350
  }
1296
- return this.policyRequest('PATCH', `/api/v1/policies/${id}`, { enabled });
1351
+ return this.orchestratorRequest('PATCH', `/api/v1/policies/dynamic/${id}`, {
1352
+ enabled,
1353
+ });
1297
1354
  }
1298
1355
  /**
1299
1356
  * Get effective dynamic policies with tier inheritance applied.
@@ -1308,11 +1365,95 @@ export class AxonFlow {
1308
1365
  if (options?.includeDisabled)
1309
1366
  params.set('include_disabled', 'true');
1310
1367
  const queryString = params.toString();
1311
- const path = `/api/v1/policies/effective${queryString ? `?${queryString}` : ''}`;
1368
+ const path = `/api/v1/policies/dynamic/effective${queryString ? `?${queryString}` : ''}`;
1312
1369
  if (this.config.debug) {
1313
1370
  debugLog('Getting effective dynamic policies', { options });
1314
1371
  }
1315
- return this.policyRequest('GET', path);
1372
+ return this.orchestratorRequest('GET', path);
1373
+ }
1374
+ // ============================================================================
1375
+ // Portal Authentication Methods (Enterprise)
1376
+ // ============================================================================
1377
+ /**
1378
+ * Login to Customer Portal and store session cookie.
1379
+ * Required before using Code Governance methods.
1380
+ *
1381
+ * @param orgId - Organization ID
1382
+ * @param password - Organization password
1383
+ * @returns Login response with session info
1384
+ *
1385
+ * @example
1386
+ * ```typescript
1387
+ * const login = await axonflow.loginToPortal('test-org-001', 'test123');
1388
+ * console.log(`Logged in as ${login.name}`);
1389
+ *
1390
+ * // Now you can use Code Governance methods
1391
+ * const providers = await axonflow.listGitProviders();
1392
+ * ```
1393
+ */
1394
+ async loginToPortal(orgId, password) {
1395
+ const url = `${this.getPortalUrl()}/api/v1/auth/login`;
1396
+ const response = await fetch(url, {
1397
+ method: 'POST',
1398
+ headers: { 'Content-Type': 'application/json' },
1399
+ body: JSON.stringify({ org_id: orgId, password }),
1400
+ signal: AbortSignal.timeout(this.config.timeout),
1401
+ });
1402
+ if (!response.ok) {
1403
+ const errorText = await response.text();
1404
+ throw new AuthenticationError(`Login failed: ${errorText}`);
1405
+ }
1406
+ const result = (await response.json());
1407
+ // Extract session cookie from response
1408
+ const cookies = response.headers.get('set-cookie');
1409
+ if (cookies) {
1410
+ const match = cookies.match(/axonflow_session=([^;]+)/);
1411
+ if (match) {
1412
+ this.sessionCookie = match[1];
1413
+ }
1414
+ }
1415
+ // Fallback to session_id in response body
1416
+ if (!this.sessionCookie && result.session_id) {
1417
+ this.sessionCookie = result.session_id;
1418
+ }
1419
+ if (this.config.debug) {
1420
+ debugLog('Portal login successful', { orgId });
1421
+ }
1422
+ return {
1423
+ sessionId: result.session_id,
1424
+ orgId: result.org_id,
1425
+ email: result.email,
1426
+ name: result.name,
1427
+ expiresAt: result.expires_at,
1428
+ };
1429
+ }
1430
+ /**
1431
+ * Logout from Customer Portal and clear session cookie.
1432
+ */
1433
+ async logoutFromPortal() {
1434
+ if (!this.sessionCookie) {
1435
+ return;
1436
+ }
1437
+ try {
1438
+ await fetch(`${this.getPortalUrl()}/api/v1/auth/logout`, {
1439
+ method: 'POST',
1440
+ headers: { Cookie: `axonflow_session=${this.sessionCookie}` },
1441
+ signal: AbortSignal.timeout(this.config.timeout),
1442
+ });
1443
+ }
1444
+ catch {
1445
+ // Ignore logout errors
1446
+ }
1447
+ this.sessionCookie = null;
1448
+ if (this.config.debug) {
1449
+ debugLog('Portal logout successful');
1450
+ }
1451
+ }
1452
+ /**
1453
+ * Check if logged in to Customer Portal.
1454
+ */
1455
+ isLoggedIn() {
1456
+ return this.sessionCookie !== null;
1316
1457
  }
1317
1458
  // ============================================================================
1318
1459
  // Code Governance Methods (Enterprise)
@@ -1356,7 +1497,7 @@ export class AxonFlow {
1356
1497
  apiRequest.installation_id = request.installationId;
1357
1498
  if (request.privateKey)
1358
1499
  apiRequest.private_key = request.privateKey;
1359
- return this.policyRequest('POST', '/api/v1/code-governance/git-providers/validate', apiRequest);
1500
+ return this.portalRequest('POST', '/api/v1/code-governance/git-providers/validate', apiRequest);
1360
1501
  }
1361
1502
  /**
1362
1503
  * Configure a Git provider for code governance.
@@ -1407,7 +1548,7 @@ export class AxonFlow {
1407
1548
  apiRequest.installation_id = request.installationId;
1408
1549
  if (request.privateKey)
1409
1550
  apiRequest.private_key = request.privateKey;
1410
- return this.policyRequest('POST', '/api/v1/code-governance/git-providers', apiRequest);
1551
+ return this.portalRequest('POST', '/api/v1/code-governance/git-providers', apiRequest);
1411
1552
  }
1412
1553
  /**
1413
1554
  * List all configured Git providers for the tenant.
@@ -1425,7 +1566,7 @@ export class AxonFlow {
1425
1566
  if (this.config.debug) {
1426
1567
  debugLog('Listing Git providers');
1427
1568
  }
1428
- return this.policyRequest('GET', '/api/v1/code-governance/git-providers');
1569
+ return this.portalRequest('GET', '/api/v1/code-governance/git-providers');
1429
1570
  }
1430
1571
  /**
1431
1572
  * Delete a configured Git provider.
@@ -1441,7 +1582,7 @@ export class AxonFlow {
1441
1582
  if (this.config.debug) {
1442
1583
  debugLog('Deleting Git provider', { type });
1443
1584
  }
1444
- await this.policyRequest('DELETE', `/api/v1/code-governance/git-providers/${type}`);
1585
+ await this.portalRequest('DELETE', `/api/v1/code-governance/git-providers/${type}`);
1445
1586
  }
1446
1587
  /**
1447
1588
  * Create a Pull Request from LLM-generated code.
@@ -1509,7 +1650,7 @@ export class AxonFlow {
1509
1650
  apiRequest.secrets_detected = request.secretsDetected;
1510
1651
  if (request.unsafePatterns !== undefined)
1511
1652
  apiRequest.unsafe_patterns = request.unsafePatterns;
1512
- const response = await this.policyRequest('POST', '/api/v1/code-governance/prs', apiRequest);
1653
+ const response = await this.portalRequest('POST', '/api/v1/code-governance/prs', apiRequest);
1513
1654
  // Transform snake_case response to camelCase
1514
1655
  return {
1515
1656
  prId: response.pr_id,
@@ -1554,10 +1695,10 @@ export class AxonFlow {
1554
1695
  if (this.config.debug) {
1555
1696
  debugLog('Listing PRs', { options });
1556
1697
  }
1557
- const response = await this.policyRequest('GET', path);
1698
+ const response = await this.portalRequest('GET', path);
1558
1699
  // Transform snake_case response to camelCase
1559
1700
  return {
1560
- prs: response.prs.map(pr => ({
1701
+ prs: (response.prs || []).map(pr => ({
1561
1702
  id: pr.id,
1562
1703
  prNumber: pr.pr_number,
1563
1704
  prUrl: pr.pr_url,
@@ -1593,7 +1734,7 @@ export class AxonFlow {
1593
1734
  if (this.config.debug) {
1594
1735
  debugLog('Getting PR', { prId });
1595
1736
  }
1596
- const response = await this.policyRequest('GET', `/api/v1/code-governance/prs/${prId}`);
1737
+ const response = await this.portalRequest('GET', `/api/v1/code-governance/prs/${prId}`);
1597
1738
  // Transform snake_case response to camelCase
1598
1739
  return {
1599
1740
  id: response.id,
@@ -1630,7 +1771,7 @@ export class AxonFlow {
1630
1771
  if (this.config.debug) {
1631
1772
  debugLog('Syncing PR status', { prId });
1632
1773
  }
1633
- const response = await this.policyRequest('POST', `/api/v1/code-governance/prs/${prId}/sync`);
1774
+ const response = await this.portalRequest('POST', `/api/v1/code-governance/prs/${prId}/sync`);
1634
1775
  // Transform snake_case response to camelCase
1635
1776
  return {
1636
1777
  id: response.id,
@@ -1670,7 +1811,7 @@ export class AxonFlow {
1670
1811
  if (this.config.debug) {
1671
1812
  debugLog('Getting code governance metrics');
1672
1813
  }
1673
- const response = await this.policyRequest('GET', '/api/v1/code-governance/metrics');
1814
+ const response = await this.portalRequest('GET', '/api/v1/code-governance/metrics');
1674
1815
  return {
1675
1816
  tenantId: response.tenant_id,
1676
1817
  totalPrs: response.total_prs,
@@ -1718,9 +1859,9 @@ export class AxonFlow {
1718
1859
  if (this.config.debug) {
1719
1860
  debugLog('Exporting code governance data', { path });
1720
1861
  }
1721
- const response = await this.policyRequest('GET', path);
1862
+ const response = await this.portalRequest('GET', path);
1722
1863
  return {
1723
- records: response.records.map(r => ({
1864
+ records: (response.records || []).map(r => ({
1724
1865
  id: r.id,
1725
1866
  prNumber: r.pr_number,
1726
1867
  prUrl: r.pr_url,
@@ -1741,6 +1882,36 @@ export class AxonFlow {
1741
1882
  exportedAt: response.exported_at,
1742
1883
  };
1743
1884
  }
1885
+ /**
1886
+ * Export code governance data as CSV.
1887
+ *
1888
+ * Returns raw CSV data suitable for saving to file or streaming.
1889
+ *
1890
+ * @param options - Export options (date filters, state filter)
1891
+ * @returns Raw CSV data
1892
+ *
1893
+ * @example
1894
+ * ```typescript
1895
+ * const csvData = await axonflow.exportCodeGovernanceDataCSV();
1896
+ * fs.writeFileSync('pr-audit.csv', csvData);
1897
+ * ```
1898
+ */
1899
+ async exportCodeGovernanceDataCSV(options) {
1900
+ const params = new URLSearchParams();
1901
+ params.set('format', 'csv');
1902
+ if (options?.startDate)
1903
+ params.set('start_date', options.startDate);
1904
+ if (options?.endDate)
1905
+ params.set('end_date', options.endDate);
1906
+ if (options?.state)
1907
+ params.set('state', options.state);
1908
+ const query = params.toString();
1909
+ const path = `/api/v1/code-governance/export${query ? '?' + query : ''}`;
1910
+ if (this.config.debug) {
1911
+ debugLog('Exporting code governance data as CSV', { path });
1912
+ }
1913
+ return this.portalRequestText('GET', path);
1914
+ }
1744
1915
  // ============================================================================
1745
1916
  // Execution Replay Methods
1746
1917
  // ============================================================================
@@ -1793,6 +1964,65 @@ export class AxonFlow {
1793
1964
  }
1794
1965
  return response.json();
1795
1966
  }
1967
+ /**
1968
+ * Get the portal URL for enterprise PR workflow features.
1969
+ * Falls back to agent endpoint with port 8082 if not configured.
1970
+ */
1971
+ getPortalUrl() {
1972
+ if (this.config.portalEndpoint) {
1973
+ return this.config.portalEndpoint;
1974
+ }
1975
+ // Default: assume portal is on same host as agent, port 8082
1976
+ try {
1977
+ const url = new URL(this.config.endpoint);
1978
+ url.port = '8082';
1979
+ return url.toString().replace(/\/$/, '');
1980
+ }
1981
+ catch {
1982
+ return 'http://localhost:8082';
1983
+ }
1984
+ }
1985
+ /**
1986
+ * Generic HTTP request helper for Customer Portal APIs (enterprise features).
1987
+ * Requires prior authentication via loginToPortal().
1988
+ */
1989
+ async portalRequest(method, path, body) {
1990
+ if (!this.sessionCookie) {
1991
+ throw new AuthenticationError('Not logged in to Customer Portal. Call loginToPortal() first.');
1992
+ }
1993
+ const url = `${this.getPortalUrl()}${path}`;
1994
+ const headers = {
1995
+ 'Content-Type': 'application/json',
1996
+ Cookie: `axonflow_session=${this.sessionCookie}`,
1997
+ };
1998
+ const options = {
1999
+ method,
2000
+ headers,
2001
+ signal: AbortSignal.timeout(this.config.timeout),
2002
+ };
2003
+ if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
2004
+ options.body = JSON.stringify(body);
2005
+ }
2006
+ if (this.config.debug) {
2007
+ debugLog('Portal request', { method, path });
2008
+ }
2009
+ const response = await fetch(url, options);
2010
+ if (!response.ok) {
2011
+ const errorText = await response.text();
2012
+ if (response.status === 401 || response.status === 403) {
2013
+ throw new AuthenticationError(`Request failed: ${errorText}`);
2014
+ }
2015
+ if (response.status === 404) {
2016
+ throw new APIError(404, 'Not Found', errorText);
2017
+ }
2018
+ throw new APIError(response.status, response.statusText, errorText);
2019
+ }
2020
+ // Handle DELETE responses with no body
2021
+ if (response.status === 204 || method === 'DELETE') {
2022
+ return undefined;
2023
+ }
2024
+ return response.json();
2025
+ }
1796
2026
  /**
1797
2027
  * List workflow executions with optional filtering and pagination.
1798
2028
  *
@@ -2349,5 +2579,36 @@ export class AxonFlow {
2349
2579
  },
2350
2580
  };
2351
2581
  }
2582
+ /**
2583
+ * Generic HTTP request helper for Customer Portal APIs that returns raw text.
2584
+ * Used for CSV exports and other non-JSON responses.
2585
+ * Requires prior authentication via loginToPortal().
2586
+ */
2587
+ async portalRequestText(method, path) {
2588
+ if (!this.sessionCookie) {
2589
+ throw new AuthenticationError('Not logged in to Customer Portal. Call loginToPortal() first.');
2590
+ }
2591
+ const url = `${this.getPortalUrl()}${path}`;
2592
+ const headers = {
2593
+ Cookie: `axonflow_session=${this.sessionCookie}`,
2594
+ };
2595
+ const options = {
2596
+ method,
2597
+ headers,
2598
+ signal: AbortSignal.timeout(this.config.timeout),
2599
+ };
2600
+ if (this.config.debug) {
2601
+ debugLog('Portal request (text)', { method, path });
2602
+ }
2603
+ const response = await fetch(url, options);
2604
+ if (!response.ok) {
2605
+ const errorText = await response.text();
2606
+ if (response.status === 401 || response.status === 403) {
2607
+ throw new AuthenticationError(`Request failed: ${errorText}`);
2608
+ }
2609
+ throw new APIError(response.status, response.statusText, errorText);
2610
+ }
2611
+ return response.text();
2612
+ }
2352
2613
  }
2353
2614
  //# sourceMappingURL=client.js.map