@axonflow/sdk 2.1.0 → 2.3.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/README.md +39 -36
- package/dist/cjs/client.d.ts +75 -2
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +196 -77
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.d.ts +93 -6
- package/dist/cjs/errors.d.ts.map +1 -1
- package/dist/cjs/errors.js +126 -12
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +7 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/code-governance.d.ts +2 -0
- package/dist/cjs/types/code-governance.d.ts.map +1 -1
- package/dist/cjs/types/config.d.ts +15 -7
- package/dist/cjs/types/config.d.ts.map +1 -1
- package/dist/cjs/types/connector.d.ts +28 -0
- package/dist/cjs/types/connector.d.ts.map +1 -1
- package/dist/cjs/types/proxy.d.ts +2 -2
- package/dist/cjs/types/proxy.d.ts.map +1 -1
- package/dist/esm/client.d.ts +75 -2
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +197 -78
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +93 -6
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +121 -11
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/code-governance.d.ts +2 -0
- package/dist/esm/types/code-governance.d.ts.map +1 -1
- package/dist/esm/types/config.d.ts +15 -7
- package/dist/esm/types/config.d.ts.map +1 -1
- package/dist/esm/types/connector.d.ts +28 -0
- package/dist/esm/types/connector.d.ts.map +1 -1
- package/dist/esm/types/proxy.d.ts +2 -2
- package/dist/esm/types/proxy.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cjs/client.js
CHANGED
|
@@ -12,15 +12,19 @@ class AxonFlow {
|
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.interceptors = [];
|
|
14
14
|
this.sessionCookie = null;
|
|
15
|
+
// Configuration validation
|
|
16
|
+
if (config.clientSecret && !config.clientId) {
|
|
17
|
+
throw new errors_1.ConfigurationError('clientSecret requires clientId to be set. ' +
|
|
18
|
+
'Provide both clientId and clientSecret for OAuth2-style authentication.');
|
|
19
|
+
}
|
|
15
20
|
// Set defaults first to determine endpoint
|
|
16
21
|
const endpoint = config.endpoint || 'https://staging-eu.getaxonflow.com';
|
|
17
|
-
// Credentials
|
|
18
|
-
|
|
19
|
-
const hasCredentials = !!(config.licenseKey || config.apiKey);
|
|
22
|
+
// Credentials check: OAuth2-style (clientId/clientSecret)
|
|
23
|
+
const hasCredentials = !!(config.clientId && config.clientSecret);
|
|
20
24
|
// Set configuration
|
|
21
25
|
this.config = {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
clientId: config.clientId,
|
|
27
|
+
clientSecret: config.clientSecret,
|
|
24
28
|
endpoint,
|
|
25
29
|
mode: config.mode || (hasCredentials ? 'production' : 'sandbox'),
|
|
26
30
|
tenant: config.tenant || 'default',
|
|
@@ -40,17 +44,33 @@ class AxonFlow {
|
|
|
40
44
|
// Initialize interceptors
|
|
41
45
|
this.interceptors = [new openai_1.OpenAIInterceptor(), new anthropic_1.AnthropicInterceptor()];
|
|
42
46
|
if (this.config.debug) {
|
|
47
|
+
// Determine auth method for logging
|
|
48
|
+
const authMethod = hasCredentials ? 'client-credentials' : 'community (no auth)';
|
|
43
49
|
(0, helpers_1.debugLog)('AxonFlow initialized', {
|
|
44
50
|
mode: this.config.mode,
|
|
45
51
|
endpoint: this.config.endpoint,
|
|
46
|
-
authMethod
|
|
47
|
-
? this.config.licenseKey
|
|
48
|
-
? 'license-key'
|
|
49
|
-
: 'api-key'
|
|
50
|
-
: 'community (no auth)',
|
|
52
|
+
authMethod,
|
|
51
53
|
});
|
|
52
54
|
}
|
|
53
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Get authentication headers based on configured credentials.
|
|
58
|
+
*
|
|
59
|
+
* Uses OAuth2-style Basic auth: Authorization: Basic base64(clientId:clientSecret)
|
|
60
|
+
* Also adds X-Tenant-ID header from clientId for tenant context.
|
|
61
|
+
*
|
|
62
|
+
* @returns Headers object with authentication headers
|
|
63
|
+
*/
|
|
64
|
+
getAuthHeaders() {
|
|
65
|
+
const headers = {};
|
|
66
|
+
// OAuth2-style client credentials
|
|
67
|
+
if (this.config.clientId && this.config.clientSecret) {
|
|
68
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
|
|
69
|
+
headers['Authorization'] = `Basic ${credentials}`;
|
|
70
|
+
headers['X-Tenant-ID'] = this.config.clientId;
|
|
71
|
+
}
|
|
72
|
+
return headers;
|
|
73
|
+
}
|
|
54
74
|
/**
|
|
55
75
|
* Main method to protect AI calls with governance
|
|
56
76
|
* @param aiCall The AI call to protect
|
|
@@ -169,8 +189,8 @@ class AxonFlow {
|
|
|
169
189
|
// Transform SDK request to Agent API format
|
|
170
190
|
const agentRequest = {
|
|
171
191
|
query: request.aiRequest.prompt,
|
|
172
|
-
user_token:
|
|
173
|
-
client_id: this.config.tenant,
|
|
192
|
+
user_token: '',
|
|
193
|
+
client_id: this.config.clientId || this.config.tenant,
|
|
174
194
|
request_type: 'llm_chat',
|
|
175
195
|
context: {
|
|
176
196
|
provider: request.aiRequest.provider,
|
|
@@ -182,12 +202,8 @@ class AxonFlow {
|
|
|
182
202
|
};
|
|
183
203
|
const headers = {
|
|
184
204
|
'Content-Type': 'application/json',
|
|
205
|
+
...this.getAuthHeaders(),
|
|
185
206
|
};
|
|
186
|
-
// Add auth headers only when credentials are provided
|
|
187
|
-
// Community/self-hosted mode works without credentials
|
|
188
|
-
if (this.config.licenseKey) {
|
|
189
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
190
|
-
}
|
|
191
207
|
const response = await fetch(url, {
|
|
192
208
|
method: 'POST',
|
|
193
209
|
headers,
|
|
@@ -250,9 +266,10 @@ class AxonFlow {
|
|
|
250
266
|
/**
|
|
251
267
|
* Create a sandbox client for testing
|
|
252
268
|
*/
|
|
253
|
-
static sandbox(
|
|
269
|
+
static sandbox(clientId = 'demo-client', clientSecret = 'demo-secret') {
|
|
254
270
|
return new AxonFlow({
|
|
255
|
-
|
|
271
|
+
clientId,
|
|
272
|
+
clientSecret,
|
|
256
273
|
mode: 'sandbox',
|
|
257
274
|
endpoint: 'https://staging-eu.getaxonflow.com',
|
|
258
275
|
debug: true,
|
|
@@ -389,9 +406,11 @@ class AxonFlow {
|
|
|
389
406
|
* ```
|
|
390
407
|
*/
|
|
391
408
|
async executeQuery(options) {
|
|
409
|
+
// Default to "anonymous" if userToken is empty/undefined (community mode)
|
|
410
|
+
const effectiveUserToken = options.userToken || 'anonymous';
|
|
392
411
|
const agentRequest = {
|
|
393
412
|
query: options.query,
|
|
394
|
-
user_token:
|
|
413
|
+
user_token: effectiveUserToken,
|
|
395
414
|
client_id: this.config.tenant,
|
|
396
415
|
request_type: options.requestType,
|
|
397
416
|
context: options.context || {},
|
|
@@ -399,15 +418,8 @@ class AxonFlow {
|
|
|
399
418
|
const url = `${this.config.endpoint}/api/request`;
|
|
400
419
|
const headers = {
|
|
401
420
|
'Content-Type': 'application/json',
|
|
421
|
+
...this.getAuthHeaders(),
|
|
402
422
|
};
|
|
403
|
-
// Add auth headers only when credentials are provided
|
|
404
|
-
// Community/self-hosted mode works without credentials
|
|
405
|
-
if (this.config.licenseKey) {
|
|
406
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
407
|
-
}
|
|
408
|
-
else if (this.config.apiKey) {
|
|
409
|
-
headers['X-Client-Secret'] = this.config.apiKey;
|
|
410
|
-
}
|
|
411
423
|
if (this.config.debug) {
|
|
412
424
|
(0, helpers_1.debugLog)('Proxy Mode: executeQuery', {
|
|
413
425
|
requestType: options.requestType,
|
|
@@ -532,8 +544,8 @@ class AxonFlow {
|
|
|
532
544
|
async queryConnector(connectorName, query, params) {
|
|
533
545
|
const agentRequest = {
|
|
534
546
|
query,
|
|
535
|
-
user_token:
|
|
536
|
-
client_id: this.config.tenant,
|
|
547
|
+
user_token: '',
|
|
548
|
+
client_id: this.config.clientId || this.config.tenant,
|
|
537
549
|
request_type: 'mcp-query',
|
|
538
550
|
context: {
|
|
539
551
|
connector: connectorName,
|
|
@@ -543,10 +555,8 @@ class AxonFlow {
|
|
|
543
555
|
const url = `${this.config.endpoint}/api/request`;
|
|
544
556
|
const headers = {
|
|
545
557
|
'Content-Type': 'application/json',
|
|
558
|
+
...this.getAuthHeaders(),
|
|
546
559
|
};
|
|
547
|
-
if (this.config.licenseKey) {
|
|
548
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
549
|
-
}
|
|
550
560
|
const response = await fetch(url, {
|
|
551
561
|
method: 'POST',
|
|
552
562
|
headers,
|
|
@@ -555,7 +565,7 @@ class AxonFlow {
|
|
|
555
565
|
});
|
|
556
566
|
if (!response.ok) {
|
|
557
567
|
const errorText = await response.text();
|
|
558
|
-
throw new
|
|
568
|
+
throw new errors_1.ConnectorError(`Connector query failed: ${response.status} ${response.statusText} - ${errorText}`, connectorName, 'query');
|
|
559
569
|
}
|
|
560
570
|
const agentResponse = await response.json();
|
|
561
571
|
if (this.config.debug) {
|
|
@@ -568,6 +578,94 @@ class AxonFlow {
|
|
|
568
578
|
meta: agentResponse.metadata,
|
|
569
579
|
};
|
|
570
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* Execute a query directly against the MCP connector endpoint.
|
|
583
|
+
*
|
|
584
|
+
* This method calls the agent's /mcp/resources/query endpoint which provides:
|
|
585
|
+
* - Request-phase policy evaluation (SQLi blocking, PII blocking)
|
|
586
|
+
* - Response-phase policy evaluation (PII redaction)
|
|
587
|
+
* - PolicyInfo metadata in responses
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```typescript
|
|
591
|
+
* const response = await axonflow.mcpQuery({
|
|
592
|
+
* connector: 'postgres',
|
|
593
|
+
* statement: 'SELECT * FROM customers LIMIT 10',
|
|
594
|
+
* });
|
|
595
|
+
*
|
|
596
|
+
* if (response.redacted) {
|
|
597
|
+
* console.log('Fields redacted:', response.redacted_fields);
|
|
598
|
+
* }
|
|
599
|
+
* console.log('Policies evaluated:', response.policy_info?.policies_evaluated);
|
|
600
|
+
* ```
|
|
601
|
+
*
|
|
602
|
+
* @param options - Query options including connector name and SQL statement
|
|
603
|
+
* @returns ConnectorResponse with data, redaction info, and policy_info
|
|
604
|
+
* @throws ConnectorError if the request is blocked by policy or fails
|
|
605
|
+
*/
|
|
606
|
+
async mcpQuery(options) {
|
|
607
|
+
if (!options.connector) {
|
|
608
|
+
throw new errors_1.ConnectorError('connector name is required', undefined, 'mcpQuery');
|
|
609
|
+
}
|
|
610
|
+
if (!options.statement) {
|
|
611
|
+
throw new errors_1.ConnectorError('statement is required', undefined, 'mcpQuery');
|
|
612
|
+
}
|
|
613
|
+
const url = `${this.config.endpoint}/mcp/resources/query`;
|
|
614
|
+
const headers = {
|
|
615
|
+
'Content-Type': 'application/json',
|
|
616
|
+
...this.getAuthHeaders(),
|
|
617
|
+
};
|
|
618
|
+
const body = {
|
|
619
|
+
connector: options.connector,
|
|
620
|
+
statement: options.statement,
|
|
621
|
+
options: options.options || {},
|
|
622
|
+
};
|
|
623
|
+
if (this.config.debug) {
|
|
624
|
+
(0, helpers_1.debugLog)('MCP Query', {
|
|
625
|
+
connector: options.connector,
|
|
626
|
+
statement: options.statement.substring(0, 50),
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
const response = await fetch(url, {
|
|
630
|
+
method: 'POST',
|
|
631
|
+
headers,
|
|
632
|
+
body: JSON.stringify(body),
|
|
633
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
634
|
+
});
|
|
635
|
+
const responseData = await response.json();
|
|
636
|
+
// Handle policy blocks (403 responses)
|
|
637
|
+
if (!response.ok) {
|
|
638
|
+
throw new errors_1.ConnectorError(responseData.error || `MCP query failed: ${response.status} ${response.statusText}`, options.connector, 'mcpQuery');
|
|
639
|
+
}
|
|
640
|
+
if (this.config.debug) {
|
|
641
|
+
(0, helpers_1.debugLog)('MCP Query result', {
|
|
642
|
+
connector: options.connector,
|
|
643
|
+
success: responseData.success,
|
|
644
|
+
redacted: responseData.redacted,
|
|
645
|
+
policiesEvaluated: responseData.policy_info?.policies_evaluated,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
success: responseData.success,
|
|
650
|
+
data: responseData.data,
|
|
651
|
+
error: responseData.error,
|
|
652
|
+
meta: responseData.meta,
|
|
653
|
+
redacted: responseData.redacted,
|
|
654
|
+
redacted_fields: responseData.redacted_fields,
|
|
655
|
+
policy_info: responseData.policy_info,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Execute a statement against an MCP connector (alias for mcpQuery).
|
|
660
|
+
*
|
|
661
|
+
* Same as mcpQuery but follows the naming convention of other execute* methods.
|
|
662
|
+
*
|
|
663
|
+
* @param options - Query options including connector name and SQL statement
|
|
664
|
+
* @returns ConnectorResponse with data, redaction info, and policy_info
|
|
665
|
+
*/
|
|
666
|
+
async mcpExecute(options) {
|
|
667
|
+
return this.mcpQuery(options);
|
|
668
|
+
}
|
|
571
669
|
/**
|
|
572
670
|
* Generate a multi-agent execution plan from a natural language query
|
|
573
671
|
* @param query - Natural language query describing the task
|
|
@@ -585,10 +683,8 @@ class AxonFlow {
|
|
|
585
683
|
const url = `${this.config.endpoint}/api/request`;
|
|
586
684
|
const headers = {
|
|
587
685
|
'Content-Type': 'application/json',
|
|
686
|
+
...this.getAuthHeaders(),
|
|
588
687
|
};
|
|
589
|
-
if (this.config.licenseKey) {
|
|
590
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
591
|
-
}
|
|
592
688
|
// Use mapTimeout for MAP operations (default 2 minutes)
|
|
593
689
|
const response = await fetch(url, {
|
|
594
690
|
method: 'POST',
|
|
@@ -598,11 +694,11 @@ class AxonFlow {
|
|
|
598
694
|
});
|
|
599
695
|
if (!response.ok) {
|
|
600
696
|
const errorText = await response.text();
|
|
601
|
-
throw new
|
|
697
|
+
throw new errors_1.PlanExecutionError(`Plan generation failed: ${response.status} ${response.statusText} - ${errorText}`, undefined, 'generation');
|
|
602
698
|
}
|
|
603
699
|
const agentResponse = await response.json();
|
|
604
700
|
if (!agentResponse.success) {
|
|
605
|
-
throw new
|
|
701
|
+
throw new errors_1.PlanExecutionError(`Plan generation failed: ${agentResponse.error}`, undefined, 'generation');
|
|
606
702
|
}
|
|
607
703
|
// plan_id can be at top level or inside data
|
|
608
704
|
const planId = agentResponse.plan_id || agentResponse.data?.plan_id;
|
|
@@ -634,10 +730,8 @@ class AxonFlow {
|
|
|
634
730
|
const url = `${this.config.endpoint}/api/request`;
|
|
635
731
|
const headers = {
|
|
636
732
|
'Content-Type': 'application/json',
|
|
733
|
+
...this.getAuthHeaders(),
|
|
637
734
|
};
|
|
638
|
-
if (this.config.licenseKey) {
|
|
639
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
640
|
-
}
|
|
641
735
|
// Use mapTimeout for MAP operations (default 2 minutes)
|
|
642
736
|
const response = await fetch(url, {
|
|
643
737
|
method: 'POST',
|
|
@@ -647,7 +741,7 @@ class AxonFlow {
|
|
|
647
741
|
});
|
|
648
742
|
if (!response.ok) {
|
|
649
743
|
const errorText = await response.text();
|
|
650
|
-
throw new
|
|
744
|
+
throw new errors_1.PlanExecutionError(`Plan execution failed: ${response.status} ${response.statusText} - ${errorText}`, planId, 'execution');
|
|
651
745
|
}
|
|
652
746
|
const agentResponse = await response.json();
|
|
653
747
|
if (this.config.debug) {
|
|
@@ -666,7 +760,7 @@ class AxonFlow {
|
|
|
666
760
|
* Get the status of a running or completed plan
|
|
667
761
|
*/
|
|
668
762
|
async getPlanStatus(planId) {
|
|
669
|
-
const url = `${this.config.endpoint}/api/
|
|
763
|
+
const url = `${this.config.endpoint}/api/v1/plan/${planId}`;
|
|
670
764
|
const response = await fetch(url, {
|
|
671
765
|
method: 'GET',
|
|
672
766
|
signal: AbortSignal.timeout(this.config.timeout),
|
|
@@ -749,15 +843,8 @@ class AxonFlow {
|
|
|
749
843
|
};
|
|
750
844
|
const headers = {
|
|
751
845
|
'Content-Type': 'application/json',
|
|
846
|
+
...this.getAuthHeaders(),
|
|
752
847
|
};
|
|
753
|
-
// Add auth headers only when credentials are provided
|
|
754
|
-
// Community/self-hosted mode works without credentials
|
|
755
|
-
if (this.config.licenseKey) {
|
|
756
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
757
|
-
}
|
|
758
|
-
else if (this.config.apiKey) {
|
|
759
|
-
headers['X-Client-Secret'] = this.config.apiKey;
|
|
760
|
-
}
|
|
761
848
|
if (this.config.debug) {
|
|
762
849
|
(0, helpers_1.debugLog)('Gateway Mode: Pre-check', { query: options.query.substring(0, 50) });
|
|
763
850
|
}
|
|
@@ -848,15 +935,8 @@ class AxonFlow {
|
|
|
848
935
|
};
|
|
849
936
|
const headers = {
|
|
850
937
|
'Content-Type': 'application/json',
|
|
938
|
+
...this.getAuthHeaders(),
|
|
851
939
|
};
|
|
852
|
-
// Add auth headers only when credentials are provided
|
|
853
|
-
// Community/self-hosted mode works without credentials
|
|
854
|
-
if (this.config.licenseKey) {
|
|
855
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
856
|
-
}
|
|
857
|
-
else if (this.config.apiKey) {
|
|
858
|
-
headers['X-Client-Secret'] = this.config.apiKey;
|
|
859
|
-
}
|
|
860
940
|
if (this.config.debug) {
|
|
861
941
|
(0, helpers_1.debugLog)('Gateway Mode: Audit', {
|
|
862
942
|
contextId: options.contextId,
|
|
@@ -1038,24 +1118,20 @@ class AxonFlow {
|
|
|
1038
1118
|
// Policy CRUD Methods - Static Policies
|
|
1039
1119
|
// ============================================================================
|
|
1040
1120
|
/**
|
|
1041
|
-
* Build authentication headers for API requests
|
|
1121
|
+
* Build authentication headers for API requests.
|
|
1122
|
+
* Includes Content-Type and X-Org-ID for policy APIs.
|
|
1123
|
+
* Uses getAuthHeaders() for authentication credentials.
|
|
1042
1124
|
*/
|
|
1043
1125
|
buildAuthHeaders() {
|
|
1044
1126
|
const headers = {
|
|
1045
1127
|
'Content-Type': 'application/json',
|
|
1128
|
+
...this.getAuthHeaders(),
|
|
1046
1129
|
};
|
|
1047
1130
|
// Always include tenant ID for policy APIs (X-Org-ID header for server compatibility)
|
|
1131
|
+
// Note: getAuthHeaders() already adds X-Tenant-ID when tenant is non-default
|
|
1048
1132
|
if (this.config.tenant) {
|
|
1049
1133
|
headers['X-Org-ID'] = this.config.tenant;
|
|
1050
1134
|
}
|
|
1051
|
-
// Add auth headers only when credentials are provided
|
|
1052
|
-
// Community/self-hosted mode works without credentials
|
|
1053
|
-
if (this.config.licenseKey) {
|
|
1054
|
-
headers['X-License-Key'] = this.config.licenseKey;
|
|
1055
|
-
}
|
|
1056
|
-
else if (this.config.apiKey) {
|
|
1057
|
-
headers['X-Client-Secret'] = this.config.apiKey;
|
|
1058
|
-
}
|
|
1059
1135
|
return headers;
|
|
1060
1136
|
}
|
|
1061
1137
|
/**
|
|
@@ -1080,8 +1156,8 @@ class AxonFlow {
|
|
|
1080
1156
|
}
|
|
1081
1157
|
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
1082
1158
|
}
|
|
1083
|
-
// Handle
|
|
1084
|
-
if (response.status === 204
|
|
1159
|
+
// Handle 204 No Content responses
|
|
1160
|
+
if (response.status === 204) {
|
|
1085
1161
|
return undefined;
|
|
1086
1162
|
}
|
|
1087
1163
|
return response.json();
|
|
@@ -1435,7 +1511,7 @@ class AxonFlow {
|
|
|
1435
1511
|
// API returns {"policies": [...]} wrapper via Agent proxy
|
|
1436
1512
|
const response = await this.orchestratorRequest('GET', path);
|
|
1437
1513
|
// Handle both wrapped and unwrapped responses for compatibility
|
|
1438
|
-
return Array.isArray(response) ? response : response.policies;
|
|
1514
|
+
return Array.isArray(response) ? response : response.policies || [];
|
|
1439
1515
|
}
|
|
1440
1516
|
/**
|
|
1441
1517
|
* Get a specific dynamic policy by ID.
|
|
@@ -1543,7 +1619,7 @@ class AxonFlow {
|
|
|
1543
1619
|
// API returns {"policies": [...]} wrapper via Agent proxy
|
|
1544
1620
|
const response = await this.orchestratorRequest('GET', path);
|
|
1545
1621
|
// Handle both wrapped and unwrapped responses for compatibility
|
|
1546
|
-
return Array.isArray(response) ? response : response.policies;
|
|
1622
|
+
return Array.isArray(response) ? response : response.policies || [];
|
|
1547
1623
|
}
|
|
1548
1624
|
// ============================================================================
|
|
1549
1625
|
// Portal Authentication Methods (Enterprise)
|
|
@@ -1928,6 +2004,49 @@ class AxonFlow {
|
|
|
1928
2004
|
providerType: response.provider_type,
|
|
1929
2005
|
};
|
|
1930
2006
|
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Close a PR without merging and optionally delete the branch.
|
|
2009
|
+
* Useful for cleaning up test PRs created by examples.
|
|
2010
|
+
*
|
|
2011
|
+
* @param prId - PR record ID
|
|
2012
|
+
* @param deleteBranch - Whether to delete the associated branch (default: true)
|
|
2013
|
+
* @returns Closed PR record
|
|
2014
|
+
*
|
|
2015
|
+
* @example
|
|
2016
|
+
* ```typescript
|
|
2017
|
+
* // Close PR and delete branch
|
|
2018
|
+
* const pr = await axonflow.closePR('pr_123');
|
|
2019
|
+
* console.log(`PR #${pr.prNumber} closed`);
|
|
2020
|
+
*
|
|
2021
|
+
* // Close PR but keep branch
|
|
2022
|
+
* const pr = await axonflow.closePR('pr_123', false);
|
|
2023
|
+
* ```
|
|
2024
|
+
*/
|
|
2025
|
+
async closePR(prId, deleteBranch = true) {
|
|
2026
|
+
if (this.config.debug) {
|
|
2027
|
+
(0, helpers_1.debugLog)('Closing PR', { prId, deleteBranch });
|
|
2028
|
+
}
|
|
2029
|
+
const query = deleteBranch ? '?delete_branch=true' : '';
|
|
2030
|
+
const response = await this.portalRequest('DELETE', `/api/v1/code-governance/prs/${prId}${query}`);
|
|
2031
|
+
return {
|
|
2032
|
+
id: response.id,
|
|
2033
|
+
prNumber: response.pr_number,
|
|
2034
|
+
prUrl: response.pr_url,
|
|
2035
|
+
title: response.title,
|
|
2036
|
+
state: response.state,
|
|
2037
|
+
owner: response.owner,
|
|
2038
|
+
repo: response.repo,
|
|
2039
|
+
headBranch: response.head_branch,
|
|
2040
|
+
baseBranch: response.base_branch,
|
|
2041
|
+
filesCount: response.files_count,
|
|
2042
|
+
secretsDetected: response.secrets_detected,
|
|
2043
|
+
unsafePatterns: response.unsafe_patterns,
|
|
2044
|
+
createdAt: response.created_at,
|
|
2045
|
+
closedAt: response.closed_at,
|
|
2046
|
+
createdBy: response.created_by,
|
|
2047
|
+
providerType: response.provider_type,
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
1931
2050
|
/**
|
|
1932
2051
|
* Sync PR status with the Git provider.
|
|
1933
2052
|
* This updates the local record with the current state from GitHub/GitLab/Bitbucket.
|
|
@@ -2121,8 +2240,8 @@ class AxonFlow {
|
|
|
2121
2240
|
}
|
|
2122
2241
|
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
2123
2242
|
}
|
|
2124
|
-
// Handle
|
|
2125
|
-
if (response.status === 204
|
|
2243
|
+
// Handle 204 No Content responses
|
|
2244
|
+
if (response.status === 204) {
|
|
2126
2245
|
return undefined;
|
|
2127
2246
|
}
|
|
2128
2247
|
return response.json();
|
|
@@ -2165,8 +2284,8 @@ class AxonFlow {
|
|
|
2165
2284
|
}
|
|
2166
2285
|
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
2167
2286
|
}
|
|
2168
|
-
// Handle
|
|
2169
|
-
if (response.status === 204
|
|
2287
|
+
// Handle 204 No Content responses
|
|
2288
|
+
if (response.status === 204) {
|
|
2170
2289
|
return undefined;
|
|
2171
2290
|
}
|
|
2172
2291
|
return response.json();
|