Skip to content

Commit 390514e

Browse files
review fixes: examples, integration.yml, integration test typecheck (#201)
Deep-review pass over the QF-10 workstream (#196, #197, #199) caught multiple issues. All P0+P1 findings are addressed here in one PR. Workflow correctness: - npm pack: clean tarball dir before each run + use --json output to get the exact filename instead of ls glob (avoids picking a stale .tgz). - Logs ordering: capture docker logs BEFORE compose down (compose down destroys the containers, so the previous step ran AFTER teardown and always returned empty). - Persist logs to disk + upload as actions/upload-artifact so failure triage doesn't require re-running. - npm install --prefer-offline --no-audit --no-fund — bound the example smoke against npm registry flakes. - timeout 30 → 60 for the basic example (fresh-runner cold-start budget). - docker compose up -d --wait --wait-timeout 120 — let compose's own healthcheck do the polling. - concurrency.group on workflow — back-to-back pushes won't spawn parallel docker stacks. - Cron moved from Monday 06:00 UTC to Tuesday 06:00 UTC so failures land during EU/IN working hours. Type-safety gates: - New tsconfig.tests.json that extends tsconfig.json (so strict + types + esModuleInterop are inherited rather than re-specified inline). The PR-time integration-test typecheck step now uses this config — the earlier inline `tsc --noEmit ...` flags were missing --strict, so null/undefined/implicit-any drift could slip through. - tsconfig.examples.json: defensive `outDir: ./.tsbuildinfo-examples` so if anyone removes `noEmit` later, output goes somewhere obvious instead of polluting `dist/`. Examples (user-facing): - Standardized all examples on `import { AxonFlow } from '@axonflow/sdk'` (proxy-mode and wcp-retry-idempotency previously used `'../../src'` which only worked when running inside the repo tree). - examples/basic: removed the "Sandbox Mode" demonstration. The sandbox client hard-codes `https://staging-eu.getaxonflow.com` (decommissioned 2026-04-09); the example would always fail at that step in any real user environment AND in the live-integration smoke. The AxonFlow.sandbox helper itself is unchanged in the SDK. - examples/connectors: `tenant_id` now sourced from AXONFLOW_TENANT_ID || AXONFLOW_CLIENT_ID. Real installations have tenant_id and client_id as distinct values; the previous version reused clientId, masking that semantics. - examples/README.md: corrected the env-var table (was claiming AXONFLOW_API_KEY / AXONFLOW_TENANT, but the examples actually read AXONFLOW_CLIENT_ID / AXONFLOW_CLIENT_SECRET); switched runner instructions from ts-node to tsx; added entries for the proxy-mode and wcp-retry-idempotency examples that were previously undocumented. Tests: - tests/integration.test.ts: replaced two `fail('...')` calls with `throw new Error('...')`. The bare `fail` global was removed in jest 27+; the bundled jest 29 throws ReferenceError at runtime rather than the descriptive message. PR-time tsc didn't catch it because it's a runtime global. - test/integration.test.ts (the older copy) removed: it pointed at decommissioned staging-eu, used deprecated protect(), and had a copy-pasted "VPC private endpoint" test that was identical to the public one. tests/integration.test.ts is the canonical version. - package.json `test:integration` script updated to drop the deleted file. CHANGELOG: Fixed-section entries for the example changes (user-facing). CI / workflow / typecheck plumbing intentionally not in CHANGELOG per feedback_changelog_no_internal_entries.md.
1 parent 77be034 commit 390514e

12 files changed

Lines changed: 114 additions & 143 deletions

File tree

.github/workflows/integration.yml

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ on:
88
workflow_dispatch: {}
99
# Weekly drift catch
1010
schedule:
11-
- cron: '0 6 * * 1' # Monday 06:00 UTC
11+
- cron: '0 6 * * 2' # Tuesday 06:00 UTC — failures land in EU/IN working hours
1212

1313
permissions:
1414
contents: read
1515

16+
# Avoid spawning parallel docker-compose stacks for back-to-back pushes;
17+
# also cancels stale PR runs when a new commit lands.
18+
concurrency:
19+
group: integration-${{ github.ref }}
20+
cancel-in-progress: true
21+
1622
env:
1723
DO_NOT_TRACK: '1'
1824

@@ -37,8 +43,9 @@ jobs:
3743
- name: Run contract tests
3844
run: npm run test:contract
3945

40-
# Example type-check on every PR and push. Catches API drift at PR time
41-
# without needing a live stack. Mirrors Python SDK demo-scripts job.
46+
# Example + integration-test type-check on every PR and push. Catches API
47+
# drift at PR time without needing a live stack. Mirrors Python SDK
48+
# demo-scripts job.
4249
example-typecheck:
4350
name: Example Type-Check
4451
runs-on: ubuntu-latest
@@ -62,14 +69,11 @@ jobs:
6269
# Integration test files live behind jest's testPathIgnorePatterns
6370
# (only run on push-to-main), so jest never type-checks them at PR
6471
# time. Drift in those files (e.g. method renames on AxonFlow) only
65-
# surfaced after merge. Catch it on every PR.
72+
# surfaced after merge. Catch it on every PR. The dedicated tsconfig
73+
# inherits strict + esModuleInterop + types from tsconfig.json so
74+
# the gate matches what ts-jest applies at runtime.
6675
- name: Type-check integration test files
67-
run: |
68-
npx tsc --noEmit \
69-
--target es2020 --module commonjs --moduleResolution node \
70-
--esModuleInterop --skipLibCheck \
71-
--types node,jest \
72-
tests/integration.test.ts test/integration.test.ts
76+
run: npx tsc -p tsconfig.tests.json
7377

7478
# Integration tests run against a live community stack via docker compose.
7579
# Runs on main merges, scheduled, and manual dispatch. Skipped on PRs to keep
@@ -103,8 +107,10 @@ jobs:
103107
- name: Start community stack
104108
run: |
105109
cd ../axonflow
106-
docker compose up -d
110+
docker compose up -d --wait --wait-timeout 120
107111
112+
# Belt-and-suspenders: also poll /health, since not every compose
113+
# service has a healthcheck wired.
108114
echo "Waiting for agent to be healthy..."
109115
timeout 120 bash -c 'until curl -sf http://localhost:8080/health; do sleep 2; done'
110116
echo "Agent is healthy"
@@ -131,27 +137,47 @@ jobs:
131137
AXONFLOW_CLIENT_SECRET: demo-secret
132138
run: |
133139
set -e
140+
# Clean tarball dir so a stale .tgz from a re-run can't shadow the
141+
# fresh one. `npm pack --json` gives us the exact filename — don't
142+
# rely on alphabetical glob.
143+
rm -rf /tmp/sdk-tarball /tmp/example-runner
134144
mkdir -p /tmp/sdk-tarball /tmp/example-runner
135-
npm pack --pack-destination /tmp/sdk-tarball
145+
PACK_JSON=$(npm pack --pack-destination /tmp/sdk-tarball --json)
146+
TARBALL_NAME=$(printf '%s' "$PACK_JSON" | node -e 'process.stdin.on("data",d=>{const o=JSON.parse(d);console.log(o[0].filename||o[0].name)})')
147+
TARBALL="/tmp/sdk-tarball/$TARBALL_NAME"
148+
test -f "$TARBALL" || { echo "Tarball not found at $TARBALL"; ls -la /tmp/sdk-tarball; exit 1; }
136149
cp examples/basic/index.ts /tmp/example-runner/
137150
cd /tmp/example-runner
138-
TARBALL=$(ls /tmp/sdk-tarball/*.tgz | head -1)
139151
npm init -y >/dev/null
140-
npm install "$TARBALL" tsx@4 typescript@5 @types/node@20
141-
timeout 30 npx tsx index.ts
152+
npm install --prefer-offline --no-audit --no-fund \
153+
"$TARBALL" tsx@4 typescript@5 @types/node@20
154+
timeout 60 npx tsx index.ts
142155
143-
- name: Stop community stack
144-
if: always()
156+
# Logs MUST be captured before `Stop community stack` runs — `compose
157+
# down` destroys the containers and `compose logs` then returns
158+
# nothing. `if: failure()` here, `if: always()` on teardown.
159+
- name: Show docker logs on failure
160+
if: failure()
145161
run: |
146162
if [ -d "../axonflow" ]; then
147163
cd ../axonflow
148-
docker compose down --volumes --remove-orphans || true
164+
docker compose logs --tail=200 || true
149165
fi
150166
151-
- name: Show docker logs on failure
167+
- name: Upload docker logs on failure
152168
if: failure()
169+
uses: actions/upload-artifact@v4
170+
with:
171+
name: docker-compose-logs
172+
path: ../axonflow/docker-compose-logs.txt
173+
if-no-files-found: ignore
174+
175+
- name: Stop community stack
176+
if: always()
153177
run: |
154178
if [ -d "../axonflow" ]; then
155179
cd ../axonflow
156-
docker compose logs --tail=200 || true
180+
# Persist logs to disk so the upload step can grab them even after teardown.
181+
docker compose logs --tail=500 > docker-compose-logs.txt 2>/dev/null || true
182+
docker compose down --volumes --remove-orphans || true
157183
fi

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- **`client.listProviders(options?)`** — list configured LLM providers and their per-provider health snapshot. Calls `GET /api/v1/llm-providers`. New `LLMProvider`, `LLMProviderHealth`, and `ListProvidersOptions` types in `@axonflow/sdk`. Supports `type` and `enabled` filters. Closes the parity gap with the Java SDK's `listLLMProviders()` and the Python SDK's `list_providers()`.
1313

14+
### Fixed
15+
16+
- **`examples/connectors/`**`installConnector()` call now uses the correct snake-case `connector_id` field (was `connectorId`) and supplies the required `tenant_id` (now sourced from `AXONFLOW_TENANT_ID`, falling back to `AXONFLOW_CLIENT_ID`).
17+
- **`examples/planning/`**`PlanStep` field reference fixed: `step.dependsOn` (was `step.dependencies`).
18+
- **`examples/proxy-mode/`** — replaced `client.executeQuery(...)` with `client.proxyLLMCall(...)` (the method was renamed in v6.0.0); removed the import of the `@internal` `ExecuteQueryOptions` type.
19+
- **`examples/basic/`, `examples/proxy-mode/`, `examples/README.md`** — replaced the decommissioned `https://staging-eu.getaxonflow.com` default endpoint with `http://localhost:8080` (the local docker-compose default). The staging-eu environment was torn down 2026-04-09.
20+
- **`examples/basic/`** — removed the "Sandbox Mode" demonstration that constructed `AxonFlow.sandbox(...)`. The sandbox client hard-codes the decommissioned staging-eu endpoint internally and the example would always fail at that step. The sandbox helper itself is unchanged in the SDK.
21+
- **`examples/README.md`** — corrected stale env-var docs: examples expect `AXONFLOW_CLIENT_ID` / `AXONFLOW_CLIENT_SECRET`, not `AXONFLOW_API_KEY` / `AXONFLOW_TENANT`. Updated runner instructions to use `tsx` (the examples no longer rely on `ts-node`).
22+
- **All examples** — standardized on `import { AxonFlow } from '@axonflow/sdk'` (was mixed; some examples imported from `'../../src'` which only worked when copy-pasted inside the repo tree).
23+
1424
## [6.1.0] - 2026-04-25 — Plugin Batch 1 explainability fields on MCP responses
1525

1626
Minor release. Surfaces fields the AxonFlow agent has emitted since v7.1.0 (Plugin Batch 1) but the SDK didn't declare. Pure field-additions on existing methods — no new SDK methods, no breaking changes. Documented in OpenAPI via platform v7.4.3.

examples/README.md

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ npm install @axonflow/sdk
1111
Set environment variables:
1212

1313
```bash
14-
export AXONFLOW_API_KEY="AXON-PLUS-yourorg-20351025-signature" # Your license key
15-
export AXONFLOW_TENANT="your-tenant-id"
14+
export AXONFLOW_CLIENT_ID="your-client-id"
15+
export AXONFLOW_CLIENT_SECRET="your-client-secret"
1616
export AXONFLOW_AGENT_URL="http://localhost:8080" # Optional (default for local docker-compose)
1717
```
1818

19-
**Note**: `AXONFLOW_API_KEY` should be your AxonFlow license key in the format `AXON-{TIER}-{ORG}-{EXPIRY}-{SIGNATURE}`
20-
2119
## Examples
2220

2321
### 1. Basic Usage (`examples/basic/`)
@@ -26,23 +24,22 @@ Simple SDK initialization and protected AI calls.
2624

2725
```bash
2826
cd examples/basic
29-
npx ts-node index.ts
27+
npx tsx index.ts
3028
```
3129

3230
Demonstrates:
3331
- Client initialization
3432
- Protecting AI calls with governance
3533
- Handling blocked requests
3634
- PII detection
37-
- Sandbox mode
3835

3936
### 2. MCP Connectors (`examples/connectors/`)
4037

4138
Working with the MCP connector marketplace.
4239

4340
```bash
4441
cd examples/connectors
45-
npx ts-node index.ts
42+
npx tsx index.ts
4643
```
4744

4845
Demonstrates:
@@ -56,7 +53,7 @@ Complex workflow orchestration with MAP.
5653

5754
```bash
5855
cd examples/planning
59-
npx ts-node index.ts
56+
npx tsx index.ts
6057
```
6158

6259
Demonstrates:
@@ -65,33 +62,46 @@ Demonstrates:
6562
- Checking plan status
6663
- Handling plan results
6764

68-
## Running Examples
65+
### 4. Proxy Mode (`examples/proxy-mode/`)
6966

70-
Each example can be run directly with ts-node:
67+
Routing requests through AxonFlow with `proxyLLMCall`.
7168

7269
```bash
73-
# Install ts-node if not already installed
74-
npm install -g ts-node typescript
70+
cd examples/proxy-mode
71+
npx tsx index.ts
72+
```
7573

76-
# Run any example
77-
cd examples/basic
78-
ts-node index.ts
74+
### 5. WCP retry_context + idempotency_key (`examples/wcp-retry-idempotency/`)
75+
76+
End-to-end exercise of the v7.3.0 WCP retry primitives. Requires an
77+
enterprise stack at `AXONFLOW_BASE_URL`.
78+
79+
```bash
80+
cd examples/wcp-retry-idempotency
81+
npx tsx index.ts
7982
```
8083

81-
Or compile and run:
84+
## Running Examples
85+
86+
Each example uses `tsx` to run TypeScript directly without a separate compile step:
8287

8388
```bash
84-
tsc index.ts
85-
node index.js
89+
# tsx is a zero-config TS runner; either install globally or use npx
90+
npm install -g tsx
91+
92+
# Run any example
93+
cd examples/basic
94+
tsx index.ts
8695
```
8796

8897
## Environment Variables
8998

9099
| Variable | Required | Description |
91100
|----------|----------|-------------|
92-
| `AXONFLOW_API_KEY` | Yes | Your AxonFlow license key (format: AXON-{TIER}-{ORG}-{EXPIRY}-{SIG}) |
93-
| `AXONFLOW_TENANT` | Yes | Your tenant identifier |
94-
| `AXONFLOW_AGENT_URL` | No | Custom endpoint (default: `http://localhost:8080` for local docker-compose) |
101+
| `AXONFLOW_CLIENT_ID` | Yes | Your client/tenant identifier |
102+
| `AXONFLOW_CLIENT_SECRET` | Yes | Your client secret |
103+
| `AXONFLOW_AGENT_URL` | No | Agent endpoint (default: `http://localhost:8080`) |
104+
| `AXONFLOW_TENANT_ID` | No | Tenant ID for connector ops; falls back to `AXONFLOW_CLIENT_ID` |
95105
| `AMADEUS_API_KEY` | No | For connector examples |
96106
| `AMADEUS_API_SECRET` | No | For connector examples |
97107

examples/basic/index.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,6 @@ async function main() {
7272
console.log('✓ Request blocked (PII detected):', err.message);
7373
}
7474

75-
// Test with sandbox client
76-
console.log('\n' + '='.repeat(60));
77-
console.log('Example 3: Sandbox Mode');
78-
console.log('='.repeat(60));
79-
80-
const sandboxClient = AxonFlow.sandbox('demo-key');
81-
82-
try {
83-
const result = await sandboxClient.protect(async () => {
84-
return {
85-
response: 'This is a sandbox test response',
86-
model: 'gpt-4-turbo',
87-
};
88-
});
89-
90-
console.log('✓ Sandbox query succeeded:', result);
91-
} catch (error) {
92-
const err = error as Error;
93-
console.log('⚠ Sandbox query result:', err.message);
94-
}
95-
9675
console.log('\n✅ All examples completed');
9776
}
9877

examples/connectors/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ 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+
// Tenant id is independent of the auth principal in real installations,
16+
// even when a single demo collapses them to the same value.
17+
const tenantId = process.env.AXONFLOW_TENANT_ID || clientId;
1518

1619
const client = new AxonFlow({ clientId, clientSecret, debug: true });
1720

@@ -46,7 +49,7 @@ async function main() {
4649
await client.installConnector({
4750
connector_id: 'amadeus-travel',
4851
name: 'amadeus-prod',
49-
tenant_id: clientId,
52+
tenant_id: tenantId,
5053
options: { environment: 'production' },
5154
credentials: { api_key: amadeusKey, api_secret: amadeusSecret },
5255
});

examples/proxy-mode/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* Proxy Mode routes all requests through AxonFlow, which handles policy checking
66
* and optionally routes to LLM providers.
77
*
8-
* Run: npx ts-node examples/proxy-mode/index.ts
8+
* Run: npx tsx examples/proxy-mode/index.ts
99
*/
1010

11-
import { AxonFlow, PolicyViolationError } from '../../src';
11+
import { AxonFlow, PolicyViolationError } from '@axonflow/sdk';
1212

1313
async function main() {
1414
// Initialize client

examples/wcp-retry-idempotency/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
* Run:
88
* source /tmp/axonflow-e2e-env.sh
99
* export AXONFLOW_BASE_URL=http://localhost:8080
10-
* npx ts-node index.ts
10+
* npx tsx index.ts
1111
*/
1212

1313
import {
1414
AxonFlow,
1515
IdempotencyKeyMismatchError,
1616
type StepGateRequest,
17-
} from '../../src';
17+
} from '@axonflow/sdk';
1818

1919
function mustEnv(k: string): string {
2020
const v = process.env[k];

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"test": "jest",
2828
"test:coverage": "jest --coverage",
2929
"test:contract": "jest tests/contract.test.ts",
30-
"test:integration": "jest tests/integration.test.ts test/integration.test.ts --testTimeout=30000 --testPathIgnorePatterns='/node_modules/'",
30+
"test:integration": "jest tests/integration.test.ts --testTimeout=30000 --testPathIgnorePatterns='/node_modules/'",
3131
"test:e2e": "jest tests/e2e-staging.test.ts --testTimeout=60000 --runInBand --testPathIgnorePatterns='/node_modules/'",
3232
"test:all": "jest --testPathIgnorePatterns='/node_modules/' --testTimeout=60000",
3333
"lint": "eslint 'src/**/*.ts' 'test/**/*.ts' 'tests/**/*.ts'",

0 commit comments

Comments
 (0)