x402-proxy 0.10.5 → 0.10.7
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/CHANGELOG.md +22 -1
- package/dist/bin/cli.js +146 -46
- package/dist/index.js +12 -6
- package/dist/openclaw/plugin.js +120 -35
- package/dist/status-DZlJ4pS7.js +3 -0
- package/dist/{status-Dc1dB0ZG.js → status-DihAcUSC.js} +1 -1
- package/dist/{wallet-LUl2l8WM.js → wallet-B0S-rma9.js} +12 -6
- package/dist/wallet-DBrVZJqe.js +3 -0
- package/package.json +1 -1
- package/dist/status-wknbavnl.js +0 -3
- package/dist/wallet-C9UlV7qi.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.7] - 2026-04-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `claude` and `claude --model` shown in default `npx x402-proxy` commands output
|
|
14
|
+
- `claude --help` lists available models and usage examples
|
|
15
|
+
|
|
16
|
+
## [0.10.6] - 2026-04-01
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Client disconnect now cancels upstream requests via abort signal propagation (prevents resource leaks on dropped connections)
|
|
20
|
+
- Anthropic SSE streaming stops reading after `message_stop` event instead of waiting for connection close
|
|
21
|
+
- Non-LLM endpoint requests (tool discovery, resource listing) no longer write empty records to inference history
|
|
22
|
+
- Empty inference history records (no amount, model, tokens, or tx) filtered out when reading history
|
|
23
|
+
- `formatUsdcValue()` uses `Intl.NumberFormat` for full precision up to 12 decimals instead of truncating to fixed tiers
|
|
24
|
+
- Stream responses guarded against double `res.end()` calls
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Default model for `claude` command changed from `minimax/minimax-m2.7` to `stepfun/step-3.5-flash`
|
|
28
|
+
|
|
10
29
|
## [0.10.5] - 2026-04-01
|
|
11
30
|
|
|
12
31
|
### Fixed
|
|
@@ -374,7 +393,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
374
393
|
- `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
|
|
375
394
|
- Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
|
|
376
395
|
|
|
377
|
-
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.
|
|
396
|
+
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.7...HEAD
|
|
397
|
+
[0.10.7]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.6...v0.10.7
|
|
398
|
+
[0.10.6]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.5...v0.10.6
|
|
378
399
|
[0.10.5]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.4...v0.10.5
|
|
379
400
|
[0.10.4]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.3...v0.10.4
|
|
380
401
|
[0.10.3]: https://github.com/cascade-protocol/x402-proxy/compare/v0.10.2...v0.10.3
|
package/dist/bin/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { _ as error, b as success, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, n as fetchAllBalances, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, x as warn, y as isTTY } from "../wallet-
|
|
2
|
+
import { _ as error, b as success, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, n as fetchAllBalances, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, x as warn, y as isTTY } from "../wallet-B0S-rma9.js";
|
|
3
3
|
import { a as getHistoryPath, c as isConfigured, d as saveConfig, i as getConfigDirShort, l as loadConfig, u as loadWalletFile } from "../config-D9wIR3xc.js";
|
|
4
4
|
import { n as setupCommand, t as runSetup } from "../setup-CNyMLnM-.js";
|
|
5
|
-
import { n as statusCommand } from "../status-
|
|
5
|
+
import { n as statusCommand } from "../status-DihAcUSC.js";
|
|
6
6
|
import { dirname, join, normalize, resolve } from "node:path";
|
|
7
7
|
import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
@@ -328,10 +328,28 @@ function addressForNetwork(evmAddress, solanaAddress, network) {
|
|
|
328
328
|
function dbg(msg) {
|
|
329
329
|
if (process.env.X402_PROXY_DEBUG === "1") process.stderr.write(`[x402-proxy] ${msg}\n`);
|
|
330
330
|
}
|
|
331
|
+
function createDownstreamAbort(req, res) {
|
|
332
|
+
const controller = new AbortController();
|
|
333
|
+
const abort = () => {
|
|
334
|
+
if (!controller.signal.aborted) controller.abort();
|
|
335
|
+
};
|
|
336
|
+
const onReqAborted = () => abort();
|
|
337
|
+
const onResClose = () => abort();
|
|
338
|
+
req.once("aborted", onReqAborted);
|
|
339
|
+
res.once("close", onResClose);
|
|
340
|
+
return {
|
|
341
|
+
signal: controller.signal,
|
|
342
|
+
cleanup() {
|
|
343
|
+
req.off("aborted", onReqAborted);
|
|
344
|
+
res.off("close", onResClose);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
331
348
|
function createInferenceProxyRouteHandler(opts) {
|
|
332
349
|
const { providers, getX402Proxy, getMppHandler, getWalletAddress, getWalletAddressForNetwork, historyPath, allModels, logger } = opts;
|
|
333
350
|
const sortedProviders = providers.slice().sort((left, right) => right.baseUrl.length - left.baseUrl.length);
|
|
334
351
|
return async (req, res) => {
|
|
352
|
+
const downstream = createDownstreamAbort(req, res);
|
|
335
353
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
336
354
|
const provider = sortedProviders.find((entry) => entry.baseUrl === "/" || url.pathname.startsWith(entry.baseUrl));
|
|
337
355
|
if (!provider) {
|
|
@@ -395,7 +413,8 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
395
413
|
const requestInit = {
|
|
396
414
|
method,
|
|
397
415
|
headers,
|
|
398
|
-
body: ["GET", "HEAD"].includes(method) ? void 0 : body
|
|
416
|
+
body: ["GET", "HEAD"].includes(method) ? void 0 : body,
|
|
417
|
+
signal: downstream.signal
|
|
399
418
|
};
|
|
400
419
|
const useMpp = provider.protocol === "mpp" || provider.protocol === "auto";
|
|
401
420
|
const wantsStreaming = isLlmEndpoint && /"stream"\s*:\s*true/.test(body);
|
|
@@ -409,11 +428,14 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
409
428
|
} }));
|
|
410
429
|
return true;
|
|
411
430
|
}
|
|
431
|
+
const mppWalletAddress = getWalletAddressForNetwork?.("eip155:4217") ?? walletAddress;
|
|
412
432
|
return await handleMppRequest({
|
|
413
433
|
res,
|
|
414
434
|
upstreamUrl,
|
|
415
435
|
requestInit,
|
|
416
|
-
|
|
436
|
+
abortSignal: downstream.signal,
|
|
437
|
+
isLlmEndpoint,
|
|
438
|
+
walletAddress: mppWalletAddress,
|
|
417
439
|
historyPath,
|
|
418
440
|
logger,
|
|
419
441
|
allModels,
|
|
@@ -441,7 +463,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
441
463
|
const amount = paymentAmount(payment);
|
|
442
464
|
const paymentFrom = (payment?.network && getWalletAddressForNetwork?.(payment.network)) ?? walletAddress;
|
|
443
465
|
const paymentNetwork = payment?.network ?? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
444
|
-
appendHistory(historyPath, {
|
|
466
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
445
467
|
t: Date.now(),
|
|
446
468
|
ok: false,
|
|
447
469
|
kind: "x402_inference",
|
|
@@ -493,7 +515,10 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
493
515
|
res.writeHead(response.status, resHeaders);
|
|
494
516
|
if (!response.body) {
|
|
495
517
|
res.end();
|
|
496
|
-
|
|
518
|
+
if (shouldAppendInferenceHistory({
|
|
519
|
+
isLlmEndpoint,
|
|
520
|
+
amount
|
|
521
|
+
})) appendHistory(historyPath, {
|
|
497
522
|
t: Date.now(),
|
|
498
523
|
ok: true,
|
|
499
524
|
kind: "x402_inference",
|
|
@@ -513,16 +538,28 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
513
538
|
const decoder = new TextDecoder();
|
|
514
539
|
try {
|
|
515
540
|
while (true) {
|
|
541
|
+
if (downstream.signal.aborted) {
|
|
542
|
+
await reader.cancel().catch(() => {});
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
516
545
|
const { done, value } = await reader.read();
|
|
517
546
|
if (done) break;
|
|
518
547
|
res.write(value);
|
|
519
548
|
sse?.push(decoder.decode(value, { stream: true }));
|
|
549
|
+
if (isMessagesApi && sse?.sawAnthropicMessageStop) {
|
|
550
|
+
await reader.cancel().catch(() => {});
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
520
553
|
}
|
|
521
554
|
} finally {
|
|
522
555
|
reader.releaseLock();
|
|
523
556
|
}
|
|
524
|
-
res.end();
|
|
525
|
-
|
|
557
|
+
if (!res.writableEnded) res.end();
|
|
558
|
+
if (shouldAppendInferenceHistory({
|
|
559
|
+
isLlmEndpoint,
|
|
560
|
+
amount,
|
|
561
|
+
usage: sse?.result
|
|
562
|
+
})) appendInferenceHistory({
|
|
526
563
|
historyPath,
|
|
527
564
|
allModels,
|
|
528
565
|
walletAddress: paymentFrom,
|
|
@@ -539,7 +576,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
539
576
|
const msg = String(err);
|
|
540
577
|
logger.error(`x402: fetch threw: ${msg}`);
|
|
541
578
|
getX402Proxy()?.shiftPayment();
|
|
542
|
-
appendHistory(historyPath, {
|
|
579
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
543
580
|
t: Date.now(),
|
|
544
581
|
ok: false,
|
|
545
582
|
kind: "x402_inference",
|
|
@@ -554,6 +591,8 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
554
591
|
else userMessage = `x402 request failed: ${msg}`;
|
|
555
592
|
if (!res.headersSent) writeErrorResponse(res, 402, userMessage, "x402_payment_error", "payment_failed", isMessagesApi);
|
|
556
593
|
return true;
|
|
594
|
+
} finally {
|
|
595
|
+
downstream.cleanup();
|
|
557
596
|
}
|
|
558
597
|
};
|
|
559
598
|
}
|
|
@@ -572,6 +611,10 @@ function writeErrorResponse(res, status, message, type, code, isAnthropicFormat)
|
|
|
572
611
|
code
|
|
573
612
|
} }));
|
|
574
613
|
}
|
|
614
|
+
function shouldAppendInferenceHistory(opts) {
|
|
615
|
+
if (!opts.isLlmEndpoint) return false;
|
|
616
|
+
return opts.amount != null || opts.usage != null;
|
|
617
|
+
}
|
|
575
618
|
function createSseTracker() {
|
|
576
619
|
let residual = "";
|
|
577
620
|
let anthropicModel = "";
|
|
@@ -579,6 +622,7 @@ function createSseTracker() {
|
|
|
579
622
|
let anthropicOutputTokens = 0;
|
|
580
623
|
let anthropicCacheRead;
|
|
581
624
|
let anthropicCacheWrite;
|
|
625
|
+
let anthropicMessageStop = false;
|
|
582
626
|
let lastOpenAiData = "";
|
|
583
627
|
let isAnthropic = false;
|
|
584
628
|
function processJson(json) {
|
|
@@ -607,6 +651,11 @@ function createSseTracker() {
|
|
|
607
651
|
if (u?.output_tokens != null) anthropicOutputTokens = u.output_tokens;
|
|
608
652
|
return;
|
|
609
653
|
}
|
|
654
|
+
if (type === "message_stop") {
|
|
655
|
+
isAnthropic = true;
|
|
656
|
+
anthropicMessageStop = true;
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
610
659
|
if (type === "message") {
|
|
611
660
|
isAnthropic = true;
|
|
612
661
|
anthropicModel = parsed.model ?? "";
|
|
@@ -630,6 +679,9 @@ function createSseTracker() {
|
|
|
630
679
|
pushJson(text) {
|
|
631
680
|
processJson(text);
|
|
632
681
|
},
|
|
682
|
+
get sawAnthropicMessageStop() {
|
|
683
|
+
return anthropicMessageStop;
|
|
684
|
+
},
|
|
633
685
|
get result() {
|
|
634
686
|
if (isAnthropic) {
|
|
635
687
|
if (!anthropicModel && !anthropicInputTokens && !anthropicOutputTokens) return void 0;
|
|
@@ -659,7 +711,7 @@ function createSseTracker() {
|
|
|
659
711
|
};
|
|
660
712
|
}
|
|
661
713
|
async function handleMppRequest(opts) {
|
|
662
|
-
const { res, upstreamUrl, requestInit, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
714
|
+
const { res, upstreamUrl, requestInit, abortSignal, isLlmEndpoint, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
663
715
|
try {
|
|
664
716
|
if (wantsStreaming) {
|
|
665
717
|
res.writeHead(200, {
|
|
@@ -671,27 +723,49 @@ async function handleMppRequest(opts) {
|
|
|
671
723
|
dbg(`mpp.sse() calling ${upstreamUrl}`);
|
|
672
724
|
const stream = await mpp.sse(upstreamUrl, requestInit);
|
|
673
725
|
dbg("mpp.sse() resolved, iterating stream");
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
res.write(`
|
|
726
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
727
|
+
let semanticComplete = false;
|
|
728
|
+
try {
|
|
729
|
+
if (isMessagesApi) while (true) {
|
|
730
|
+
if (abortSignal.aborted) break;
|
|
731
|
+
const { done, value } = await iterator.next();
|
|
732
|
+
if (done) break;
|
|
733
|
+
const text = String(value);
|
|
734
|
+
let eventType = "unknown";
|
|
735
|
+
try {
|
|
736
|
+
eventType = JSON.parse(text).type ?? "unknown";
|
|
737
|
+
} catch {}
|
|
738
|
+
res.write(`event: ${eventType}\ndata: ${text}\n\n`);
|
|
687
739
|
sse.pushJson(text);
|
|
740
|
+
if (sse.sawAnthropicMessageStop) {
|
|
741
|
+
semanticComplete = true;
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
688
744
|
}
|
|
689
|
-
|
|
745
|
+
else {
|
|
746
|
+
while (true) {
|
|
747
|
+
if (abortSignal.aborted) break;
|
|
748
|
+
const { done, value } = await iterator.next();
|
|
749
|
+
if (done) break;
|
|
750
|
+
const text = String(value);
|
|
751
|
+
res.write(`data: ${text}\n\n`);
|
|
752
|
+
sse.pushJson(text);
|
|
753
|
+
}
|
|
754
|
+
if (!abortSignal.aborted) res.write("data: [DONE]\n\n");
|
|
755
|
+
}
|
|
756
|
+
} catch (err) {
|
|
757
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
758
|
+
if (!(abortSignal.aborted || semanticComplete) || !msg.includes("terminated")) throw err;
|
|
759
|
+
} finally {
|
|
760
|
+
await iterator.return?.().catch(() => {});
|
|
690
761
|
}
|
|
691
762
|
dbg(`stream done, ${sse.result ? `${sse.result.model} ${sse.result.inputTokens}+${sse.result.outputTokens}t` : "no usage"}`);
|
|
692
|
-
res.end();
|
|
763
|
+
if (!res.writableEnded) res.end();
|
|
693
764
|
mpp.shiftPayment();
|
|
694
|
-
|
|
765
|
+
if (shouldAppendInferenceHistory({
|
|
766
|
+
isLlmEndpoint,
|
|
767
|
+
usage: sse.result
|
|
768
|
+
})) appendInferenceHistory({
|
|
695
769
|
historyPath,
|
|
696
770
|
allModels,
|
|
697
771
|
walletAddress,
|
|
@@ -708,7 +782,7 @@ async function handleMppRequest(opts) {
|
|
|
708
782
|
if (response.status === 402) {
|
|
709
783
|
const responseBody = await response.text();
|
|
710
784
|
logger.error(`mpp: payment failed, raw response: ${responseBody}`);
|
|
711
|
-
appendHistory(historyPath, {
|
|
785
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
712
786
|
t: Date.now(),
|
|
713
787
|
ok: false,
|
|
714
788
|
kind: "x402_inference",
|
|
@@ -728,14 +802,19 @@ async function handleMppRequest(opts) {
|
|
|
728
802
|
const usageTracker = createSseTracker();
|
|
729
803
|
usageTracker.pushJson(responseBody);
|
|
730
804
|
const payment = mpp.shiftPayment();
|
|
731
|
-
|
|
805
|
+
const amount = parseMppAmount(payment?.amount);
|
|
806
|
+
if (shouldAppendInferenceHistory({
|
|
807
|
+
isLlmEndpoint,
|
|
808
|
+
amount,
|
|
809
|
+
usage: usageTracker.result
|
|
810
|
+
})) appendInferenceHistory({
|
|
732
811
|
historyPath,
|
|
733
812
|
allModels,
|
|
734
813
|
walletAddress,
|
|
735
814
|
paymentNetwork: payment?.network ?? "eip155:4217",
|
|
736
815
|
paymentTo: void 0,
|
|
737
816
|
tx: tx ?? payment?.receipt?.reference,
|
|
738
|
-
amount
|
|
817
|
+
amount,
|
|
739
818
|
thinkingMode,
|
|
740
819
|
usage: usageTracker.result,
|
|
741
820
|
durationMs: Date.now() - startMs
|
|
@@ -744,7 +823,7 @@ async function handleMppRequest(opts) {
|
|
|
744
823
|
} catch (err) {
|
|
745
824
|
dbg(`mpp error: ${String(err)}`);
|
|
746
825
|
logger.error(`mpp: fetch threw: ${String(err)}`);
|
|
747
|
-
appendHistory(historyPath, {
|
|
826
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
748
827
|
t: Date.now(),
|
|
749
828
|
ok: false,
|
|
750
829
|
kind: "x402_inference",
|
|
@@ -1003,25 +1082,43 @@ Examples:
|
|
|
1003
1082
|
});
|
|
1004
1083
|
//#endregion
|
|
1005
1084
|
//#region src/commands/claude.ts
|
|
1006
|
-
const DEFAULT_MODEL = "
|
|
1085
|
+
const DEFAULT_MODEL = "stepfun/step-3.5-flash";
|
|
1086
|
+
const modelList = [
|
|
1087
|
+
"stepfun/step-3.5-flash",
|
|
1088
|
+
"minimax/minimax-m2.5",
|
|
1089
|
+
"minimax/minimax-m2.7",
|
|
1090
|
+
"z-ai/glm-5",
|
|
1091
|
+
"z-ai/glm-5-turbo",
|
|
1092
|
+
"moonshotai/kimi-k2.5"
|
|
1093
|
+
].map((id) => ` ${id}`).join("\n");
|
|
1007
1094
|
function normalizeClaudeArgs(args) {
|
|
1008
1095
|
return args[0] === "--" ? args.slice(1) : args;
|
|
1009
1096
|
}
|
|
1010
1097
|
const claudeCommand = buildCommand({
|
|
1011
1098
|
docs: {
|
|
1012
1099
|
brief: "Run Claude Code through a paid local proxy",
|
|
1013
|
-
fullDescription: `
|
|
1100
|
+
fullDescription: `Starts a local x402-proxy server and launches Claude Code with
|
|
1101
|
+
ANTHROPIC_BASE_URL pointed at it. All inference requests go through
|
|
1102
|
+
the proxy, which handles payments automatically via MPP.
|
|
1014
1103
|
|
|
1015
|
-
|
|
1016
|
-
$ x402-proxy claude
|
|
1017
|
-
$ x402-proxy claude --model z-ai/glm-5
|
|
1018
|
-
$ x402-proxy claude -- --print "explain this
|
|
1104
|
+
Usage:
|
|
1105
|
+
$ x402-proxy claude Start with default model
|
|
1106
|
+
$ x402-proxy claude --model z-ai/glm-5 Use a specific model
|
|
1107
|
+
$ x402-proxy claude -- --print "explain this" Pass args to Claude Code
|
|
1108
|
+
$ x402-proxy claude -- -p "summarize *.ts" Print mode (non-interactive)
|
|
1109
|
+
|
|
1110
|
+
Available models (via surf.cascade.fyi):
|
|
1111
|
+
${modelList}
|
|
1112
|
+
|
|
1113
|
+
The --model value is passed as ANTHROPIC_MODEL and ANTHROPIC_CUSTOM_MODEL_OPTION
|
|
1114
|
+
to Claude Code. Any model supported by the upstream endpoint will work, even if
|
|
1115
|
+
not listed above.`
|
|
1019
1116
|
},
|
|
1020
1117
|
parameters: {
|
|
1021
1118
|
flags: {
|
|
1022
1119
|
model: {
|
|
1023
1120
|
kind: "parsed",
|
|
1024
|
-
brief: "Model to use (default:
|
|
1121
|
+
brief: "Model to use (default: stepfun/step-3.5-flash)",
|
|
1025
1122
|
parse: String,
|
|
1026
1123
|
default: DEFAULT_MODEL
|
|
1027
1124
|
},
|
|
@@ -1368,12 +1465,14 @@ Examples:
|
|
|
1368
1465
|
};
|
|
1369
1466
|
if (!url) {
|
|
1370
1467
|
if (isConfigured()) {
|
|
1371
|
-
const { displayStatus } = await import("../status-
|
|
1468
|
+
const { displayStatus } = await import("../status-DZlJ4pS7.js");
|
|
1372
1469
|
await displayStatus();
|
|
1373
1470
|
console.log();
|
|
1374
1471
|
console.log(pc.dim(" Commands:"));
|
|
1375
1472
|
console.log(` ${pc.cyan("$ npx x402-proxy <url>")} Fetch a paid API`);
|
|
1376
1473
|
console.log(` ${pc.cyan("$ npx x402-proxy mcp <url>")} MCP proxy for AI agents`);
|
|
1474
|
+
console.log(` ${pc.cyan("$ npx x402-proxy claude")} Run Claude Code via paid proxy`);
|
|
1475
|
+
console.log(` ${pc.cyan("$ npx x402-proxy claude --model <id>")} Use a specific model`);
|
|
1377
1476
|
console.log(` ${pc.cyan("$ npx x402-proxy setup")} Reconfigure wallet`);
|
|
1378
1477
|
console.log(` ${pc.cyan("$ npx x402-proxy wallet")} Addresses and balances`);
|
|
1379
1478
|
console.log(` ${pc.cyan("$ npx x402-proxy wallet history")} Full payment history`);
|
|
@@ -1391,6 +1490,7 @@ Examples:
|
|
|
1391
1490
|
console.log(` ${pc.cyan("$ npx x402-proxy setup")} Create a wallet`);
|
|
1392
1491
|
console.log(` ${pc.cyan("$ npx x402-proxy <url>")} Fetch a paid API`);
|
|
1393
1492
|
console.log(` ${pc.cyan("$ npx x402-proxy mcp <url>")} MCP proxy for AI agents`);
|
|
1493
|
+
console.log(` ${pc.cyan("$ npx x402-proxy claude")} Run Claude Code via paid proxy`);
|
|
1394
1494
|
console.log(` ${pc.cyan("$ npx x402-proxy wallet")} Addresses and balances`);
|
|
1395
1495
|
console.log(` ${pc.cyan("$ npx x402-proxy wallet history")} Payment history`);
|
|
1396
1496
|
console.log(` ${pc.cyan("$ npx x402-proxy --help")} All options`);
|
|
@@ -1433,7 +1533,7 @@ Examples:
|
|
|
1433
1533
|
verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
|
|
1434
1534
|
let preferredNetwork = config?.defaultNetwork;
|
|
1435
1535
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1436
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
1536
|
+
const { fetchAllBalances } = await import("../wallet-DBrVZJqe.js");
|
|
1437
1537
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1438
1538
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1439
1539
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1600,7 +1700,7 @@ Examples:
|
|
|
1600
1700
|
const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
|
|
1601
1701
|
const hasMpp = detected.mpp;
|
|
1602
1702
|
const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
|
|
1603
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
1703
|
+
const { fetchAllBalances } = await import("../wallet-DBrVZJqe.js");
|
|
1604
1704
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1605
1705
|
const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1606
1706
|
const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1820,7 +1920,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1820
1920
|
async function startX402Proxy() {
|
|
1821
1921
|
let preferredNetwork = config?.defaultNetwork;
|
|
1822
1922
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1823
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
1923
|
+
const { fetchAllBalances } = await import("../wallet-DBrVZJqe.js");
|
|
1824
1924
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1825
1925
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1826
1926
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1840,7 +1940,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1840
1940
|
}
|
|
1841
1941
|
const remoteClient = new Client({
|
|
1842
1942
|
name: "x402-proxy",
|
|
1843
|
-
version: "0.10.
|
|
1943
|
+
version: "0.10.7"
|
|
1844
1944
|
});
|
|
1845
1945
|
const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
|
|
1846
1946
|
autoPayment: true,
|
|
@@ -1878,7 +1978,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1878
1978
|
}
|
|
1879
1979
|
const localServer = new Server({
|
|
1880
1980
|
name: "x402-proxy",
|
|
1881
|
-
version: "0.10.
|
|
1981
|
+
version: "0.10.7"
|
|
1882
1982
|
}, { capabilities: {
|
|
1883
1983
|
tools: tools.length > 0 ? {} : void 0,
|
|
1884
1984
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
@@ -1973,7 +2073,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1973
2073
|
}));
|
|
1974
2074
|
const remoteClient = new Client({
|
|
1975
2075
|
name: "x402-proxy",
|
|
1976
|
-
version: "0.10.
|
|
2076
|
+
version: "0.10.7"
|
|
1977
2077
|
});
|
|
1978
2078
|
await connectTransport(remoteClient);
|
|
1979
2079
|
const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
|
|
@@ -1988,7 +2088,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1988
2088
|
}
|
|
1989
2089
|
const localServer = new Server({
|
|
1990
2090
|
name: "x402-proxy",
|
|
1991
|
-
version: "0.10.
|
|
2091
|
+
version: "0.10.7"
|
|
1992
2092
|
}, { capabilities: {
|
|
1993
2093
|
tools: tools.length > 0 ? {} : void 0,
|
|
1994
2094
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
@@ -2387,7 +2487,7 @@ const app = buildApplication(buildRouteMap({
|
|
|
2387
2487
|
docs: { brief: "curl for x402 paid APIs" }
|
|
2388
2488
|
}), {
|
|
2389
2489
|
name: "x402-proxy",
|
|
2390
|
-
versionInfo: { currentVersion: "0.10.
|
|
2490
|
+
versionInfo: { currentVersion: "0.10.7" },
|
|
2391
2491
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
2392
2492
|
});
|
|
2393
2493
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -174,6 +174,11 @@ async function createMppProxyHandler(opts) {
|
|
|
174
174
|
//#region src/history.ts
|
|
175
175
|
const HISTORY_MAX_LINES = 1e3;
|
|
176
176
|
const HISTORY_KEEP_LINES = 500;
|
|
177
|
+
function isMeaningfulInferenceRecord(record) {
|
|
178
|
+
if (record.kind !== "x402_inference") return true;
|
|
179
|
+
if (!record.ok) return true;
|
|
180
|
+
return record.amount != null || record.model != null || record.inputTokens != null || record.outputTokens != null || record.tx != null;
|
|
181
|
+
}
|
|
177
182
|
function appendHistory(historyPath, record) {
|
|
178
183
|
try {
|
|
179
184
|
mkdirSync(dirname(historyPath), { recursive: true });
|
|
@@ -195,7 +200,8 @@ function readHistory(historyPath) {
|
|
|
195
200
|
try {
|
|
196
201
|
const parsed = JSON.parse(line);
|
|
197
202
|
if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
|
|
198
|
-
|
|
203
|
+
const record = parsed;
|
|
204
|
+
return isMeaningfulInferenceRecord(record) ? [record] : [];
|
|
199
205
|
} catch {
|
|
200
206
|
return [];
|
|
201
207
|
}
|
|
@@ -224,12 +230,12 @@ function calcSpend(records) {
|
|
|
224
230
|
count
|
|
225
231
|
};
|
|
226
232
|
}
|
|
227
|
-
/** Format a USDC value with adaptive precision (no token suffix). */
|
|
228
233
|
function formatUsdcValue(amount) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
return new Intl.NumberFormat("en-US", {
|
|
235
|
+
useGrouping: false,
|
|
236
|
+
minimumFractionDigits: 0,
|
|
237
|
+
maximumFractionDigits: 12
|
|
238
|
+
}).format(amount);
|
|
233
239
|
}
|
|
234
240
|
function formatAmount(amount, token) {
|
|
235
241
|
if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
|
package/dist/openclaw/plugin.js
CHANGED
|
@@ -258,6 +258,11 @@ var OptimizedSvmScheme = class {
|
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
260
|
};
|
|
261
|
+
function isMeaningfulInferenceRecord(record) {
|
|
262
|
+
if (record.kind !== "x402_inference") return true;
|
|
263
|
+
if (!record.ok) return true;
|
|
264
|
+
return record.amount != null || record.model != null || record.inputTokens != null || record.outputTokens != null || record.tx != null;
|
|
265
|
+
}
|
|
261
266
|
function appendHistory(historyPath, record) {
|
|
262
267
|
try {
|
|
263
268
|
mkdirSync(dirname(historyPath), { recursive: true });
|
|
@@ -279,7 +284,8 @@ function readHistory(historyPath) {
|
|
|
279
284
|
try {
|
|
280
285
|
const parsed = JSON.parse(line);
|
|
281
286
|
if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
|
|
282
|
-
|
|
287
|
+
const record = parsed;
|
|
288
|
+
return isMeaningfulInferenceRecord(record) ? [record] : [];
|
|
283
289
|
} catch {
|
|
284
290
|
return [];
|
|
285
291
|
}
|
|
@@ -308,12 +314,12 @@ function calcSpend(records) {
|
|
|
308
314
|
count
|
|
309
315
|
};
|
|
310
316
|
}
|
|
311
|
-
/** Format a USDC value with adaptive precision (no token suffix). */
|
|
312
317
|
function formatUsdcValue(amount) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
return new Intl.NumberFormat("en-US", {
|
|
319
|
+
useGrouping: false,
|
|
320
|
+
minimumFractionDigits: 0,
|
|
321
|
+
maximumFractionDigits: 12
|
|
322
|
+
}).format(amount);
|
|
317
323
|
}
|
|
318
324
|
function formatAmount(amount, token) {
|
|
319
325
|
if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
|
|
@@ -1153,7 +1159,7 @@ function createWalletCommand(ctx) {
|
|
|
1153
1159
|
if (parts[0]?.toLowerCase() === "send") return { text: "Use `/x_send <amount|all> <address>` for transfers." };
|
|
1154
1160
|
try {
|
|
1155
1161
|
const snap = await getWalletSnapshot(ctx.rpcUrl, solanaWallet, evmWallet, ctx.historyPath);
|
|
1156
|
-
const lines = [`x402-proxy v0.10.
|
|
1162
|
+
const lines = [`x402-proxy v0.10.7`];
|
|
1157
1163
|
const defaultModel = ctx.allModels[0];
|
|
1158
1164
|
if (defaultModel) lines.push("", `**Model** - ${defaultModel.name} (${defaultModel.provider})`);
|
|
1159
1165
|
lines.push("", `**Protocol** - ${ctx.getDefaultRequestProtocol()}`);
|
|
@@ -1372,10 +1378,28 @@ function resolveMppSessionBudget(value, fallback = "0.5") {
|
|
|
1372
1378
|
function dbg(msg) {
|
|
1373
1379
|
if (process.env.X402_PROXY_DEBUG === "1") process.stderr.write(`[x402-proxy] ${msg}\n`);
|
|
1374
1380
|
}
|
|
1381
|
+
function createDownstreamAbort(req, res) {
|
|
1382
|
+
const controller = new AbortController();
|
|
1383
|
+
const abort = () => {
|
|
1384
|
+
if (!controller.signal.aborted) controller.abort();
|
|
1385
|
+
};
|
|
1386
|
+
const onReqAborted = () => abort();
|
|
1387
|
+
const onResClose = () => abort();
|
|
1388
|
+
req.once("aborted", onReqAborted);
|
|
1389
|
+
res.once("close", onResClose);
|
|
1390
|
+
return {
|
|
1391
|
+
signal: controller.signal,
|
|
1392
|
+
cleanup() {
|
|
1393
|
+
req.off("aborted", onReqAborted);
|
|
1394
|
+
res.off("close", onResClose);
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1375
1398
|
function createInferenceProxyRouteHandler(opts) {
|
|
1376
1399
|
const { providers, getX402Proxy, getMppHandler, getWalletAddress, getWalletAddressForNetwork, historyPath, allModels, logger } = opts;
|
|
1377
1400
|
const sortedProviders = providers.slice().sort((left, right) => right.baseUrl.length - left.baseUrl.length);
|
|
1378
1401
|
return async (req, res) => {
|
|
1402
|
+
const downstream = createDownstreamAbort(req, res);
|
|
1379
1403
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
1380
1404
|
const provider = sortedProviders.find((entry) => entry.baseUrl === "/" || url.pathname.startsWith(entry.baseUrl));
|
|
1381
1405
|
if (!provider) {
|
|
@@ -1439,7 +1463,8 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1439
1463
|
const requestInit = {
|
|
1440
1464
|
method,
|
|
1441
1465
|
headers,
|
|
1442
|
-
body: ["GET", "HEAD"].includes(method) ? void 0 : body
|
|
1466
|
+
body: ["GET", "HEAD"].includes(method) ? void 0 : body,
|
|
1467
|
+
signal: downstream.signal
|
|
1443
1468
|
};
|
|
1444
1469
|
const useMpp = provider.protocol === "mpp" || provider.protocol === "auto";
|
|
1445
1470
|
const wantsStreaming = isLlmEndpoint && /"stream"\s*:\s*true/.test(body);
|
|
@@ -1453,11 +1478,14 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1453
1478
|
} }));
|
|
1454
1479
|
return true;
|
|
1455
1480
|
}
|
|
1481
|
+
const mppWalletAddress = getWalletAddressForNetwork?.("eip155:4217") ?? walletAddress;
|
|
1456
1482
|
return await handleMppRequest({
|
|
1457
1483
|
res,
|
|
1458
1484
|
upstreamUrl,
|
|
1459
1485
|
requestInit,
|
|
1460
|
-
|
|
1486
|
+
abortSignal: downstream.signal,
|
|
1487
|
+
isLlmEndpoint,
|
|
1488
|
+
walletAddress: mppWalletAddress,
|
|
1461
1489
|
historyPath,
|
|
1462
1490
|
logger,
|
|
1463
1491
|
allModels,
|
|
@@ -1485,7 +1513,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1485
1513
|
const amount = paymentAmount(payment);
|
|
1486
1514
|
const paymentFrom = (payment?.network && getWalletAddressForNetwork?.(payment.network)) ?? walletAddress;
|
|
1487
1515
|
const paymentNetwork = payment?.network ?? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
1488
|
-
appendHistory(historyPath, {
|
|
1516
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
1489
1517
|
t: Date.now(),
|
|
1490
1518
|
ok: false,
|
|
1491
1519
|
kind: "x402_inference",
|
|
@@ -1537,7 +1565,10 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1537
1565
|
res.writeHead(response.status, resHeaders);
|
|
1538
1566
|
if (!response.body) {
|
|
1539
1567
|
res.end();
|
|
1540
|
-
|
|
1568
|
+
if (shouldAppendInferenceHistory({
|
|
1569
|
+
isLlmEndpoint,
|
|
1570
|
+
amount
|
|
1571
|
+
})) appendHistory(historyPath, {
|
|
1541
1572
|
t: Date.now(),
|
|
1542
1573
|
ok: true,
|
|
1543
1574
|
kind: "x402_inference",
|
|
@@ -1557,16 +1588,28 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1557
1588
|
const decoder = new TextDecoder();
|
|
1558
1589
|
try {
|
|
1559
1590
|
while (true) {
|
|
1591
|
+
if (downstream.signal.aborted) {
|
|
1592
|
+
await reader.cancel().catch(() => {});
|
|
1593
|
+
break;
|
|
1594
|
+
}
|
|
1560
1595
|
const { done, value } = await reader.read();
|
|
1561
1596
|
if (done) break;
|
|
1562
1597
|
res.write(value);
|
|
1563
1598
|
sse?.push(decoder.decode(value, { stream: true }));
|
|
1599
|
+
if (isMessagesApi && sse?.sawAnthropicMessageStop) {
|
|
1600
|
+
await reader.cancel().catch(() => {});
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1564
1603
|
}
|
|
1565
1604
|
} finally {
|
|
1566
1605
|
reader.releaseLock();
|
|
1567
1606
|
}
|
|
1568
|
-
res.end();
|
|
1569
|
-
|
|
1607
|
+
if (!res.writableEnded) res.end();
|
|
1608
|
+
if (shouldAppendInferenceHistory({
|
|
1609
|
+
isLlmEndpoint,
|
|
1610
|
+
amount,
|
|
1611
|
+
usage: sse?.result
|
|
1612
|
+
})) appendInferenceHistory({
|
|
1570
1613
|
historyPath,
|
|
1571
1614
|
allModels,
|
|
1572
1615
|
walletAddress: paymentFrom,
|
|
@@ -1583,7 +1626,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1583
1626
|
const msg = String(err);
|
|
1584
1627
|
logger.error(`x402: fetch threw: ${msg}`);
|
|
1585
1628
|
getX402Proxy()?.shiftPayment();
|
|
1586
|
-
appendHistory(historyPath, {
|
|
1629
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
1587
1630
|
t: Date.now(),
|
|
1588
1631
|
ok: false,
|
|
1589
1632
|
kind: "x402_inference",
|
|
@@ -1598,6 +1641,8 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
1598
1641
|
else userMessage = `x402 request failed: ${msg}`;
|
|
1599
1642
|
if (!res.headersSent) writeErrorResponse(res, 402, userMessage, "x402_payment_error", "payment_failed", isMessagesApi);
|
|
1600
1643
|
return true;
|
|
1644
|
+
} finally {
|
|
1645
|
+
downstream.cleanup();
|
|
1601
1646
|
}
|
|
1602
1647
|
};
|
|
1603
1648
|
}
|
|
@@ -1616,6 +1661,10 @@ function writeErrorResponse(res, status, message, type, code, isAnthropicFormat)
|
|
|
1616
1661
|
code
|
|
1617
1662
|
} }));
|
|
1618
1663
|
}
|
|
1664
|
+
function shouldAppendInferenceHistory(opts) {
|
|
1665
|
+
if (!opts.isLlmEndpoint) return false;
|
|
1666
|
+
return opts.amount != null || opts.usage != null;
|
|
1667
|
+
}
|
|
1619
1668
|
function createSseTracker() {
|
|
1620
1669
|
let residual = "";
|
|
1621
1670
|
let anthropicModel = "";
|
|
@@ -1623,6 +1672,7 @@ function createSseTracker() {
|
|
|
1623
1672
|
let anthropicOutputTokens = 0;
|
|
1624
1673
|
let anthropicCacheRead;
|
|
1625
1674
|
let anthropicCacheWrite;
|
|
1675
|
+
let anthropicMessageStop = false;
|
|
1626
1676
|
let lastOpenAiData = "";
|
|
1627
1677
|
let isAnthropic = false;
|
|
1628
1678
|
function processJson(json) {
|
|
@@ -1651,6 +1701,11 @@ function createSseTracker() {
|
|
|
1651
1701
|
if (u?.output_tokens != null) anthropicOutputTokens = u.output_tokens;
|
|
1652
1702
|
return;
|
|
1653
1703
|
}
|
|
1704
|
+
if (type === "message_stop") {
|
|
1705
|
+
isAnthropic = true;
|
|
1706
|
+
anthropicMessageStop = true;
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1654
1709
|
if (type === "message") {
|
|
1655
1710
|
isAnthropic = true;
|
|
1656
1711
|
anthropicModel = parsed.model ?? "";
|
|
@@ -1674,6 +1729,9 @@ function createSseTracker() {
|
|
|
1674
1729
|
pushJson(text) {
|
|
1675
1730
|
processJson(text);
|
|
1676
1731
|
},
|
|
1732
|
+
get sawAnthropicMessageStop() {
|
|
1733
|
+
return anthropicMessageStop;
|
|
1734
|
+
},
|
|
1677
1735
|
get result() {
|
|
1678
1736
|
if (isAnthropic) {
|
|
1679
1737
|
if (!anthropicModel && !anthropicInputTokens && !anthropicOutputTokens) return void 0;
|
|
@@ -1703,7 +1761,7 @@ function createSseTracker() {
|
|
|
1703
1761
|
};
|
|
1704
1762
|
}
|
|
1705
1763
|
async function handleMppRequest(opts) {
|
|
1706
|
-
const { res, upstreamUrl, requestInit, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
1764
|
+
const { res, upstreamUrl, requestInit, abortSignal, isLlmEndpoint, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
1707
1765
|
try {
|
|
1708
1766
|
if (wantsStreaming) {
|
|
1709
1767
|
res.writeHead(200, {
|
|
@@ -1715,27 +1773,49 @@ async function handleMppRequest(opts) {
|
|
|
1715
1773
|
dbg(`mpp.sse() calling ${upstreamUrl}`);
|
|
1716
1774
|
const stream = await mpp.sse(upstreamUrl, requestInit);
|
|
1717
1775
|
dbg("mpp.sse() resolved, iterating stream");
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
res.write(`
|
|
1776
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
1777
|
+
let semanticComplete = false;
|
|
1778
|
+
try {
|
|
1779
|
+
if (isMessagesApi) while (true) {
|
|
1780
|
+
if (abortSignal.aborted) break;
|
|
1781
|
+
const { done, value } = await iterator.next();
|
|
1782
|
+
if (done) break;
|
|
1783
|
+
const text = String(value);
|
|
1784
|
+
let eventType = "unknown";
|
|
1785
|
+
try {
|
|
1786
|
+
eventType = JSON.parse(text).type ?? "unknown";
|
|
1787
|
+
} catch {}
|
|
1788
|
+
res.write(`event: ${eventType}\ndata: ${text}\n\n`);
|
|
1731
1789
|
sse.pushJson(text);
|
|
1790
|
+
if (sse.sawAnthropicMessageStop) {
|
|
1791
|
+
semanticComplete = true;
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
while (true) {
|
|
1797
|
+
if (abortSignal.aborted) break;
|
|
1798
|
+
const { done, value } = await iterator.next();
|
|
1799
|
+
if (done) break;
|
|
1800
|
+
const text = String(value);
|
|
1801
|
+
res.write(`data: ${text}\n\n`);
|
|
1802
|
+
sse.pushJson(text);
|
|
1803
|
+
}
|
|
1804
|
+
if (!abortSignal.aborted) res.write("data: [DONE]\n\n");
|
|
1732
1805
|
}
|
|
1733
|
-
|
|
1806
|
+
} catch (err) {
|
|
1807
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1808
|
+
if (!(abortSignal.aborted || semanticComplete) || !msg.includes("terminated")) throw err;
|
|
1809
|
+
} finally {
|
|
1810
|
+
await iterator.return?.().catch(() => {});
|
|
1734
1811
|
}
|
|
1735
1812
|
dbg(`stream done, ${sse.result ? `${sse.result.model} ${sse.result.inputTokens}+${sse.result.outputTokens}t` : "no usage"}`);
|
|
1736
|
-
res.end();
|
|
1813
|
+
if (!res.writableEnded) res.end();
|
|
1737
1814
|
mpp.shiftPayment();
|
|
1738
|
-
|
|
1815
|
+
if (shouldAppendInferenceHistory({
|
|
1816
|
+
isLlmEndpoint,
|
|
1817
|
+
usage: sse.result
|
|
1818
|
+
})) appendInferenceHistory({
|
|
1739
1819
|
historyPath,
|
|
1740
1820
|
allModels,
|
|
1741
1821
|
walletAddress,
|
|
@@ -1752,7 +1832,7 @@ async function handleMppRequest(opts) {
|
|
|
1752
1832
|
if (response.status === 402) {
|
|
1753
1833
|
const responseBody = await response.text();
|
|
1754
1834
|
logger.error(`mpp: payment failed, raw response: ${responseBody}`);
|
|
1755
|
-
appendHistory(historyPath, {
|
|
1835
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
1756
1836
|
t: Date.now(),
|
|
1757
1837
|
ok: false,
|
|
1758
1838
|
kind: "x402_inference",
|
|
@@ -1772,14 +1852,19 @@ async function handleMppRequest(opts) {
|
|
|
1772
1852
|
const usageTracker = createSseTracker();
|
|
1773
1853
|
usageTracker.pushJson(responseBody);
|
|
1774
1854
|
const payment = mpp.shiftPayment();
|
|
1775
|
-
|
|
1855
|
+
const amount = parseMppAmount(payment?.amount);
|
|
1856
|
+
if (shouldAppendInferenceHistory({
|
|
1857
|
+
isLlmEndpoint,
|
|
1858
|
+
amount,
|
|
1859
|
+
usage: usageTracker.result
|
|
1860
|
+
})) appendInferenceHistory({
|
|
1776
1861
|
historyPath,
|
|
1777
1862
|
allModels,
|
|
1778
1863
|
walletAddress,
|
|
1779
1864
|
paymentNetwork: payment?.network ?? "eip155:4217",
|
|
1780
1865
|
paymentTo: void 0,
|
|
1781
1866
|
tx: tx ?? payment?.receipt?.reference,
|
|
1782
|
-
amount
|
|
1867
|
+
amount,
|
|
1783
1868
|
thinkingMode,
|
|
1784
1869
|
usage: usageTracker.result,
|
|
1785
1870
|
durationMs: Date.now() - startMs
|
|
@@ -1788,7 +1873,7 @@ async function handleMppRequest(opts) {
|
|
|
1788
1873
|
} catch (err) {
|
|
1789
1874
|
dbg(`mpp error: ${String(err)}`);
|
|
1790
1875
|
logger.error(`mpp: fetch threw: ${String(err)}`);
|
|
1791
|
-
appendHistory(historyPath, {
|
|
1876
|
+
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
1792
1877
|
t: Date.now(),
|
|
1793
1878
|
ok: false,
|
|
1794
1879
|
kind: "x402_inference",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-
|
|
2
|
+
import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-B0S-rma9.js";
|
|
3
3
|
import { a as getHistoryPath, i as getConfigDirShort, l as loadConfig } from "./config-D9wIR3xc.js";
|
|
4
4
|
import { buildCommand } from "@stricli/core";
|
|
5
5
|
import pc from "picocolors";
|
|
@@ -34,6 +34,11 @@ function dim(msg) {
|
|
|
34
34
|
function success(msg) {
|
|
35
35
|
process.stderr.write(`${isTTY() ? pc.green(`✓ ${msg}`) : `✓ ${msg}`}\n`);
|
|
36
36
|
}
|
|
37
|
+
function isMeaningfulInferenceRecord(record) {
|
|
38
|
+
if (record.kind !== "x402_inference") return true;
|
|
39
|
+
if (!record.ok) return true;
|
|
40
|
+
return record.amount != null || record.model != null || record.inputTokens != null || record.outputTokens != null || record.tx != null;
|
|
41
|
+
}
|
|
37
42
|
function appendHistory(historyPath, record) {
|
|
38
43
|
try {
|
|
39
44
|
mkdirSync(dirname(historyPath), { recursive: true });
|
|
@@ -55,7 +60,8 @@ function readHistory(historyPath) {
|
|
|
55
60
|
try {
|
|
56
61
|
const parsed = JSON.parse(line);
|
|
57
62
|
if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
|
|
58
|
-
|
|
63
|
+
const record = parsed;
|
|
64
|
+
return isMeaningfulInferenceRecord(record) ? [record] : [];
|
|
59
65
|
} catch {
|
|
60
66
|
return [];
|
|
61
67
|
}
|
|
@@ -84,12 +90,12 @@ function calcSpend(records) {
|
|
|
84
90
|
count
|
|
85
91
|
};
|
|
86
92
|
}
|
|
87
|
-
/** Format a USDC value with adaptive precision (no token suffix). */
|
|
88
93
|
function formatUsdcValue(amount) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
return new Intl.NumberFormat("en-US", {
|
|
95
|
+
useGrouping: false,
|
|
96
|
+
minimumFractionDigits: 0,
|
|
97
|
+
maximumFractionDigits: 12
|
|
98
|
+
}).format(amount);
|
|
93
99
|
}
|
|
94
100
|
function formatAmount(amount, token) {
|
|
95
101
|
if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
|
package/package.json
CHANGED
package/dist/status-wknbavnl.js
DELETED
package/dist/wallet-C9UlV7qi.js
DELETED