Skip to content

Commit 4790356

Browse files
fix: getPlanStatus auth, queryConnector user token, example fixes (#244)
* fix: getPlanStatus auth header, queryConnector user token, example fixes - getPlanStatus was the only client method calling _fetch without buildAuthHeaders() - every call 401'd against a live stack - queryConnector gains an optional userToken param (hardcoded user_token:'' rejected on JWT-validating enterprise stacks); parity with Go QueryConnector - examples read AXONFLOW_USER_TOKEN, thread it through plan/connector calls, and exit non-zero on unexpected failures instead of printing 'completed' over swallowed 401s - runtime-e2e/plan_status_auth: live-agent authenticated round-trip + bad-creds 401 control Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com> * chore: prettier formatting + package-lock version sync Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com> --------- Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com>
1 parent fbf7f48 commit 4790356

13 files changed

Lines changed: 253 additions & 55 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,35 @@ All notable changes to the AxonFlow TypeScript SDK will be documented in this fi
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [8.5.1] - 2026-07-09 — getPlanStatus auth + queryConnector user token + example fixes
9+
10+
Hostile-testing sweep ahead of the BukuWarung integration
11+
(getaxonflow/axonflow-enterprise#2861).
12+
13+
### Fixed
14+
15+
- **`getPlanStatus` sends the Basic auth header.** It was the only client
16+
method calling `_fetch` without `buildAuthHeaders()` — every call 401'd
17+
("Authorization header required") against a live stack. Regression test
18+
asserts the header on the wire; `runtime-e2e/plan_status_auth/` proves the
19+
round-trip against a real agent.
20+
- **`queryConnector` accepts an optional `userToken` parameter** (4th arg,
21+
additive). It hardcoded `user_token: ''` with no way to supply a real
22+
token, so JWT-validating enterprise stacks rejected every connector query.
23+
Cross-SDK parity with Go's `QueryConnector(userToken, …)`.
24+
- **Examples pass `AXONFLOW_USER_TOKEN` and fail honestly.** Enterprise
25+
stacks validate user tokens as JWTs; `basic`, `proxy-mode`, `planning` and
26+
`connectors` passed hardcoded non-JWT literals — every governed call
27+
401'd while the examples still printed "completed" and exited 0. They now
28+
read `AXONFLOW_USER_TOKEN`, thread it through `generatePlan`/`executePlan`
29+
and `queryConnector`, and set a non-zero exit code on unexpected failures
30+
(`PolicyViolationError` remains an expected demo outcome).
31+
32+
### Added
33+
34+
- `runtime-e2e/plan_status_auth/` — live-agent assertion: generatePlan →
35+
getPlanStatus authenticated round-trip + wrong-credentials 401 control.
36+
837
## [8.5.0] - 2026-06-09 — Decision Mode PEP: decide → fulfill → forward
938

1039
Adds the SDK analog of the platform PEP client (`platform/shared/pep`,

examples/basic/index.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
* Run: npx tsx examples/basic/index.ts
1212
*/
1313

14-
import { AxonFlow } from '@axonflow/sdk';
14+
import { AxonFlow, PolicyViolationError } from '@axonflow/sdk';
1515

1616
async function main() {
1717
const agentURL = process.env.AXONFLOW_AGENT_URL || 'http://localhost:8080';
1818
const clientId = process.env.AXONFLOW_CLIENT_ID;
1919
const clientSecret = process.env.AXONFLOW_CLIENT_SECRET;
20+
// Enterprise stacks validate user tokens as JWTs - export AXONFLOW_USER_TOKEN.
21+
const userToken = process.env.AXONFLOW_USER_TOKEN || 'demo-user';
2022

2123
if (!clientId || !clientSecret) {
2224
console.error('AXONFLOW_CLIENT_ID and AXONFLOW_CLIENT_SECRET must be set');
@@ -47,7 +49,7 @@ async function main() {
4749
console.log('='.repeat(60));
4850
try {
4951
const ctx = await client.getPolicyApprovedContext({
50-
userToken: 'demo-user',
52+
userToken,
5153
query: 'What is the capital of France?',
5254
});
5355
console.log(`Approved: ${ctx.approved}`);
@@ -80,6 +82,7 @@ async function main() {
8082
}
8183
} catch (error) {
8284
console.log(`Gateway Mode error: ${(error as Error).message}`);
85+
process.exitCode = 1;
8386
}
8487

8588
// Step 3: Proxy Mode (single round-trip) ---------------------------------
@@ -88,7 +91,7 @@ async function main() {
8891
console.log('='.repeat(60));
8992
try {
9093
const result = await client.proxyLLMCall({
91-
userToken: 'demo-user',
94+
userToken,
9295
query: 'What is the capital of France?',
9396
requestType: 'chat',
9497
});
@@ -98,10 +101,10 @@ async function main() {
98101
console.log(`Policies: ${result.policyInfo.policiesEvaluated.length} evaluated`);
99102
}
100103
} catch (error) {
101-
// Community stacks without an LLM provider configured will return
102-
// non-success; that's normal here. Specific error subclasses (e.g.
103-
// PolicyViolationError) signal blocked content rather than a failure.
104+
// Specific error subclasses (e.g. PolicyViolationError) signal blocked
105+
// content; anything else here is a real failure.
104106
console.log(`Proxy Mode error: ${(error as Error).message}`);
107+
process.exitCode = 1;
105108
}
106109

107110
// Step 4: PII detection (Proxy Mode) -------------------------------------
@@ -110,7 +113,7 @@ async function main() {
110113
console.log('='.repeat(60));
111114
try {
112115
const result = await client.proxyLLMCall({
113-
userToken: 'demo-user',
116+
userToken,
114117
query: 'My email is john.doe@example.com and SSN is 123-45-6789',
115118
requestType: 'chat',
116119
});
@@ -124,6 +127,9 @@ async function main() {
124127
// is set to block. Community defaults to warn — caller may see
125128
// success=true with a policy match recorded.
126129
console.log(`PII step: ${(error as Error).message}`);
130+
if (!(error instanceof PolicyViolationError)) {
131+
process.exitCode = 1;
132+
}
127133
}
128134

129135
console.log('\nAll examples completed');

examples/connectors/index.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@ import { AxonFlow } from '@axonflow/sdk';
1212
async function main() {
1313
const clientId = process.env.AXONFLOW_CLIENT_ID || 'demo-client';
1414
const clientSecret = process.env.AXONFLOW_CLIENT_SECRET || 'demo-secret';
15+
// Enterprise stacks validate user tokens as JWTs - export AXONFLOW_USER_TOKEN.
16+
const userToken = process.env.AXONFLOW_USER_TOKEN || '';
1517
// Tenant id is independent of the auth principal in real installations,
1618
// even when a single demo collapses them to the same value.
1719
const tenantId = process.env.AXONFLOW_TENANT_ID || clientId;
1820

19-
const client = new AxonFlow({ clientId, clientSecret, debug: true });
21+
const client = new AxonFlow({
22+
clientId,
23+
clientSecret,
24+
endpoint: process.env.AXONFLOW_AGENT_URL || 'http://localhost:8080',
25+
debug: true,
26+
});
2027

2128
// List connectors
2229
console.log('='.repeat(60));
@@ -68,11 +75,12 @@ async function main() {
6875

6976
if (amadeusKey) {
7077
try {
71-
const result = await client.queryConnector('amadeus-prod', 'Find flights from Paris to Amsterdam', {
72-
origin: 'CDG',
73-
destination: 'AMS',
74-
date: '2025-12-15',
75-
});
78+
const result = await client.queryConnector(
79+
'amadeus-prod',
80+
'Find flights from Paris to Amsterdam',
81+
{ origin: 'CDG', destination: 'AMS', date: '2025-12-15' },
82+
userToken
83+
);
7684

7785
console.log('✓ Flight data retrieved:', result.data);
7886
} catch (error) {
@@ -83,4 +91,7 @@ async function main() {
8391
console.log('\n✅ Connector examples completed');
8492
}
8593

86-
main().catch(console.error);
94+
main().catch(error => {
95+
console.error(error);
96+
process.exit(1);
97+
});

examples/explain-decision/index.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ import { AxonFlow } from '@axonflow/sdk';
3131
async function main() {
3232
const decisionId = process.env.AXONFLOW_DECISION_ID;
3333
if (!decisionId) {
34-
console.error(
35-
'AXONFLOW_DECISION_ID must be set (a decision_id from a recent blocked call)',
36-
);
34+
console.error('AXONFLOW_DECISION_ID must be set (a decision_id from a recent blocked call)');
3735
process.exit(2);
3836
}
3937

@@ -65,7 +63,7 @@ async function main() {
6563
const action = m.action || '-';
6664
const risk = m.riskLevel || '-';
6765
console.log(
68-
` [${i}] ${m.policyId} (${name}) — action=${action} risk=${risk} allow_override=${!!m.allowOverride}`,
66+
` [${i}] ${m.policyId} (${name}) — action=${action} risk=${risk} allow_override=${!!m.allowOverride}`
6967
);
7068
});
7169

@@ -82,9 +80,7 @@ async function main() {
8280
if (exp.overrideExistingId) {
8381
console.log(` override_existing_id: ${exp.overrideExistingId}`);
8482
}
85-
console.log(
86-
` historical_hit_count_session: ${exp.historicalHitCountSession}`,
87-
);
83+
console.log(` historical_hit_count_session: ${exp.historicalHitCountSession}`);
8884
if (exp.policySourceLink) {
8985
console.log(` policy_source_link: ${exp.policySourceLink}`);
9086
}

examples/planning/index.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@ import { AxonFlow } from '@axonflow/sdk';
1212
async function main() {
1313
const clientId = process.env.AXONFLOW_CLIENT_ID || 'demo-client';
1414
const clientSecret = process.env.AXONFLOW_CLIENT_SECRET || 'demo-secret';
15+
// Enterprise stacks validate user tokens as JWTs - export AXONFLOW_USER_TOKEN.
16+
const userToken = process.env.AXONFLOW_USER_TOKEN || 'demo-user';
1517

16-
const client = new AxonFlow({ clientId, clientSecret, debug: true, timeout: 90000 });
18+
const client = new AxonFlow({
19+
clientId,
20+
clientSecret,
21+
endpoint: process.env.AXONFLOW_AGENT_URL || 'http://localhost:8080',
22+
debug: true,
23+
timeout: 90000,
24+
});
1725

1826
// Generate plan
1927
console.log('='.repeat(60));
@@ -27,7 +35,7 @@ async function main() {
2735
console.log('Generating plan...');
2836

2937
try {
30-
const plan = await client.generatePlan(planGoal, 'travel');
38+
const plan = await client.generatePlan(planGoal, 'travel', userToken);
3139

3240
console.log('✓ Plan generated successfully!');
3341
console.log(` Plan ID: ${plan.planId}`);
@@ -55,7 +63,7 @@ async function main() {
5563
console.log('Executing plan...');
5664
const startTime = Date.now();
5765

58-
const execResult = await client.executePlan(plan.planId);
66+
const execResult = await client.executePlan(plan.planId, userToken);
5967
const duration = Date.now() - startTime;
6068

6169
console.log(`\n✓ Plan execution completed in ${duration}ms`);
@@ -79,9 +87,13 @@ async function main() {
7987
console.log(`Plan Status: ${status.status}`);
8088
} catch (error) {
8189
console.log('⚠ Plan operation failed:', (error as Error).message);
90+
process.exitCode = 1;
8291
}
8392

8493
console.log('\n✅ Planning examples completed');
8594
}
8695

87-
main().catch(console.error);
96+
main().catch(error => {
97+
console.error(error);
98+
process.exit(1);
99+
});

examples/proxy-mode/index.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ async function main() {
1919
debug: true,
2020
});
2121

22+
// Enterprise stacks validate user tokens as JWTs - export AXONFLOW_USER_TOKEN.
23+
const userToken = process.env.AXONFLOW_USER_TOKEN || 'user-123';
24+
2225
console.log('='.repeat(60));
2326
console.log('AxonFlow Proxy Mode Example');
2427
console.log('='.repeat(60));
@@ -35,7 +38,7 @@ async function main() {
3538
console.log('-'.repeat(40));
3639
try {
3740
const chatResult = await axonflow.proxyLLMCall({
38-
userToken: 'user-123',
41+
userToken,
3942
query: 'What is the capital of France?',
4043
requestType: 'chat',
4144
});
@@ -46,14 +49,17 @@ async function main() {
4649
}
4750
} catch (error) {
4851
console.log(` Error: ${error instanceof Error ? error.message : error}`);
52+
if (!(error instanceof PolicyViolationError)) {
53+
process.exitCode = 1;
54+
}
4955
}
5056

5157
// 3. Query with Context
5258
console.log('\n3. Query with Context (LLM Provider Info)');
5359
console.log('-'.repeat(40));
5460
try {
5561
const contextResult = await axonflow.proxyLLMCall({
56-
userToken: 'user-123',
62+
userToken,
5763
query: 'Explain quantum computing in simple terms',
5864
requestType: 'chat',
5965
context: {
@@ -69,14 +75,17 @@ async function main() {
6975
}
7076
} catch (error) {
7177
console.log(` Error: ${error instanceof Error ? error.message : error}`);
78+
if (!(error instanceof PolicyViolationError)) {
79+
process.exitCode = 1;
80+
}
7281
}
7382

7483
// 4. PII Detection (should be blocked)
7584
console.log('\n4. PII Detection Test (should be blocked)');
7685
console.log('-'.repeat(40));
7786
try {
7887
await axonflow.proxyLLMCall({
79-
userToken: 'user-123',
88+
userToken,
8089
query: 'Process this SSN: 123-45-6789 and credit card: 4111-1111-1111-1111',
8190
requestType: 'chat',
8291
});
@@ -96,7 +105,7 @@ async function main() {
96105
console.log('-'.repeat(40));
97106
try {
98107
await axonflow.proxyLLMCall({
99-
userToken: 'user-123',
108+
userToken,
100109
query: "SELECT * FROM users WHERE id = '1'; DROP TABLE users;--",
101110
requestType: 'sql',
102111
});
@@ -115,22 +124,25 @@ async function main() {
115124
console.log('-'.repeat(40));
116125
try {
117126
const sqlResult = await axonflow.proxyLLMCall({
118-
userToken: 'user-123',
127+
userToken,
119128
query: 'SELECT name, email FROM customers WHERE status = active LIMIT 10',
120129
requestType: 'sql',
121130
});
122131
console.log(` Success: ${sqlResult.success}`);
123132
console.log(` Blocked: ${sqlResult.blocked}`);
124133
} catch (error) {
125134
console.log(` Error: ${error instanceof Error ? error.message : error}`);
135+
if (!(error instanceof PolicyViolationError)) {
136+
process.exitCode = 1;
137+
}
126138
}
127139

128140
// 7. MCP Connector Query
129141
console.log('\n7. MCP Connector Query');
130142
console.log('-'.repeat(40));
131143
try {
132144
const mcpResult = await axonflow.proxyLLMCall({
133-
userToken: 'user-123',
145+
userToken,
134146
query: 'Get recent orders',
135147
requestType: 'mcp-query',
136148
context: {
@@ -142,6 +154,9 @@ async function main() {
142154
console.log(` Has Data: ${!!mcpResult.data}`);
143155
} catch (error) {
144156
console.log(` Error: ${error instanceof Error ? error.message : error}`);
157+
if (!(error instanceof PolicyViolationError)) {
158+
process.exitCode = 1;
159+
}
145160
}
146161

147162
console.log('\n' + '='.repeat(60));

0 commit comments

Comments
 (0)