Skip to content

Commit beeffa0

Browse files
committed
fix(cli): unify MPP streaming status and improve response output
- Unify MPP streaming payment label from "MPP session:" to "Payment: ... MPP" matching the shared non-streaming format - Add newline before MPP streaming status to separate from body - Auto pretty-print JSON responses (2-space indent) on TTY for non-streaming application/json responses - Honor --json flag to force pretty-printing even when piped - Color-code HTTP status lines (green 2xx, yellow 3xx, red 4xx/5xx)
1 parent 17e6279 commit beeffa0

3 files changed

Lines changed: 48 additions & 18 deletions

File tree

packages/x402-proxy/CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.8.3] - 2026-03-24
11+
12+
### Added
13+
- JSON response pretty-printing: non-streaming `application/json` responses auto-formatted with 2-space indent on TTY
14+
- `--json` flag now works: forces JSON pretty-printing even when piped (non-TTY)
15+
- Color-coded HTTP status lines: green for 2xx, yellow for 3xx, red for 4xx/5xx
16+
17+
### Fixed
18+
- MPP streaming payment label unified from `MPP session:` to `Payment: ... MPP` to match non-streaming format
19+
- MPP streaming status line now starts on a new line instead of appending to last JSON chunk
20+
1021
## [0.8.2] - 2026-03-24
1122

1223
### Fixed
@@ -219,7 +230,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
219230
- `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
220231
- Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
221232

222-
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.2...HEAD
233+
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.3...HEAD
234+
[0.8.3]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.2...v0.8.3
223235
[0.8.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.1...v0.8.2
224236
[0.8.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.0...v0.8.1
225237
[0.8.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.1...v0.8.0

packages/x402-proxy/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "x402-proxy",
3-
"version": "0.8.2",
3+
"version": "0.8.3",
44
"description": "curl for x402 paid APIs. Auto-pays any endpoint on Base, Solana, and Tempo. Also works as an OpenClaw plugin.",
55
"type": "module",
66
"sideEffects": false,

packages/x402-proxy/src/commands/fetch.ts

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import { getHistoryPath, isConfigured, loadConfig } from "../lib/config.js";
2121
import { dim, error, info, isTTY } from "../lib/output.js";
2222
import { buildX402Client, resolveWallet } from "../lib/resolve-wallet.js";
2323

24+
function isStreamingResponse(res: Response): boolean {
25+
return (res.headers.get("content-type") ?? "").includes("text/event-stream");
26+
}
27+
2428
type FetchFlags = {
2529
method: string;
2630
body: string | undefined;
@@ -317,9 +321,10 @@ Examples:
317321

318322
const elapsedMs = Date.now() - startMs;
319323
const spentAmount = mppPayment.amount ? Number(mppPayment.amount) : undefined;
324+
if (isTTY()) process.stderr.write("\n");
320325
if (mppPayment && isTTY()) {
321-
const spentStr = spentAmount != null ? `${formatAmount(spentAmount, "USDC")} ` : "";
322-
info(` MPP session: ${spentStr}(${displayNetwork(mppPayment.network)})`);
326+
const mppAmount = spentAmount != null ? ` ${formatAmount(spentAmount, "USDC")}` : "";
327+
info(` Payment:${mppAmount} MPP (${displayNetwork(mppPayment.network)})`);
323328
}
324329
if (isTTY()) {
325330
dim(` Streamed (${elapsedMs}ms)`);
@@ -570,7 +575,9 @@ Examples:
570575
}
571576

572577
if (isTTY()) {
573-
dim(` ${response.status} ${response.statusText} (${elapsedMs}ms)`);
578+
const statusText = ` ${response.status} ${response.statusText} (${elapsedMs}ms)`;
579+
const colorFn = response.status < 300 ? pc.green : response.status < 400 ? pc.yellow : pc.red;
580+
process.stderr.write(`${colorFn(statusText)}\n`);
574581
}
575582

576583
// Record payment in history
@@ -606,22 +613,33 @@ Examples:
606613
}
607614

608615
// Stream response body to stdout
616+
const isJsonResponse = (response.headers.get("content-type") ?? "").includes(
617+
"application/json",
618+
);
619+
const shouldPrettyPrint =
620+
isJsonResponse && (flags.json || isTTY()) && !isStreamingResponse(response);
609621
if (response.body) {
610-
const reader = response.body.getReader();
611-
try {
612-
while (true) {
613-
const { done, value } = await reader.read();
614-
if (done) break;
615-
process.stdout.write(value);
622+
if (shouldPrettyPrint) {
623+
const text = await response.text();
624+
try {
625+
process.stdout.write(`${JSON.stringify(JSON.parse(text), null, 2)}\n`);
626+
} catch {
627+
process.stdout.write(text);
628+
if (isTTY()) process.stdout.write("\n");
629+
}
630+
} else {
631+
const reader = response.body.getReader();
632+
try {
633+
while (true) {
634+
const { done, value } = await reader.read();
635+
if (done) break;
636+
process.stdout.write(value);
637+
}
638+
} finally {
639+
reader.releaseLock();
616640
}
617-
} finally {
618-
reader.releaseLock();
641+
if (isTTY()) process.stdout.write("\n");
619642
}
620643
}
621-
622-
// Trailing newline for TTY
623-
if (isTTY() && response.body) {
624-
process.stdout.write("\n");
625-
}
626644
},
627645
});

0 commit comments

Comments
 (0)