@zeroxyz/cli 0.0.7 → 0.0.9
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/dist/index.js +141 -91
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command as Command8 } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@zeroxyz/cli",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.9",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
12
12
|
zero: "dist/index.js",
|
|
@@ -26,8 +26,9 @@ var package_default = {
|
|
|
26
26
|
dev: "tsx src/index.ts",
|
|
27
27
|
cli: "ZERO_API_URL=http://localhost:1111 tsx src/index.ts",
|
|
28
28
|
"test:integration": "vitest run --project integration",
|
|
29
|
+
"test:online": "vitest run --project online",
|
|
29
30
|
"test:unit": "vitest run --project unit",
|
|
30
|
-
test: "pnpm run test:integration",
|
|
31
|
+
test: "pnpm run test:unit && pnpm run test:integration",
|
|
31
32
|
typecheck: "tsc"
|
|
32
33
|
},
|
|
33
34
|
dependencies: {
|
|
@@ -35,6 +36,8 @@ var package_default = {
|
|
|
35
36
|
"@relayprotocol/relay-sdk": "^5.2.1",
|
|
36
37
|
"@x402/core": "^2.9.0",
|
|
37
38
|
"@x402/evm": "^2.9.0",
|
|
39
|
+
"@x402/extensions": "^2.9.0",
|
|
40
|
+
"@x402/fetch": "^2.9.0",
|
|
38
41
|
commander: "^13.0.0",
|
|
39
42
|
mppx: "^0.5.9",
|
|
40
43
|
open: "^11.0.0",
|
|
@@ -43,7 +46,10 @@ var package_default = {
|
|
|
43
46
|
zod: "^4.3.5"
|
|
44
47
|
},
|
|
45
48
|
devDependencies: {
|
|
49
|
+
"@hono/node-server": "^1.19.13",
|
|
46
50
|
"@types/node": "^25.0.7",
|
|
51
|
+
"@x402/hono": "^2.9.0",
|
|
52
|
+
hono: "^4.12.12",
|
|
47
53
|
tsup: "^8.5.1",
|
|
48
54
|
tsx: "^4.21.0",
|
|
49
55
|
typescript: "^5.9.3",
|
|
@@ -105,7 +111,7 @@ var configCommand = (_appContext) => new Command("config").description("View or
|
|
|
105
111
|
import { Command as Command2 } from "commander";
|
|
106
112
|
var detectPaymentRequirement = (headers, status) => {
|
|
107
113
|
if (status !== 402) return null;
|
|
108
|
-
const x402Header = headers.get("x-payment-required");
|
|
114
|
+
const x402Header = headers.get("payment-required") ?? headers.get("x-payment-required");
|
|
109
115
|
if (x402Header) {
|
|
110
116
|
try {
|
|
111
117
|
const decoded = JSON.parse(
|
|
@@ -218,10 +224,25 @@ var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a c
|
|
|
218
224
|
paymentMode: "charge"
|
|
219
225
|
}
|
|
220
226
|
});
|
|
227
|
+
console.error(`
|
|
228
|
+
Run ID: ${runResult.runId}`);
|
|
221
229
|
console.error(
|
|
222
|
-
`
|
|
223
|
-
|
|
224
|
-
|
|
230
|
+
` Leave a review to help other agents discover great capabilities:`
|
|
231
|
+
);
|
|
232
|
+
console.error(
|
|
233
|
+
` zero review ${runResult.runId} --success --accuracy 5 --value 4 --reliability 5 --content "your feedback"`
|
|
234
|
+
);
|
|
235
|
+
console.error(
|
|
236
|
+
[
|
|
237
|
+
``,
|
|
238
|
+
` Tips for a great review:`,
|
|
239
|
+
` --success / --no-success Did the API return the result you expected?`,
|
|
240
|
+
` --accuracy 1-5 How correct was the response? (1 = wrong, 5 = perfect)`,
|
|
241
|
+
` --value 1-5 Was it worth the price? (1 = overpriced, 5 = great deal)`,
|
|
242
|
+
` --reliability 1-5 Did it respond quickly and without errors? (1 = flaky, 5 = rock solid)`,
|
|
243
|
+
` --content Free-text is optional but helps other agents pick the best capability.`,
|
|
244
|
+
``
|
|
245
|
+
].join("\n")
|
|
225
246
|
);
|
|
226
247
|
} catch {
|
|
227
248
|
}
|
|
@@ -498,7 +519,19 @@ var initCommand = (appContext2) => new Command4("init").description("Initialize
|
|
|
498
519
|
|
|
499
520
|
// src/commands/review-command.ts
|
|
500
521
|
import { Command as Command5 } from "commander";
|
|
501
|
-
var reviewCommand = (appContext2) => new Command5("review").description("Submit a review for a capability run").
|
|
522
|
+
var reviewCommand = (appContext2) => new Command5("review").description("Submit a review for a capability run").addHelpText(
|
|
523
|
+
"after",
|
|
524
|
+
`
|
|
525
|
+
Tips for a great review:
|
|
526
|
+
--success / --no-success Did the API return the result you expected?
|
|
527
|
+
--accuracy 1-5 How correct was the response? (1 = wrong, 5 = perfect)
|
|
528
|
+
--value 1-5 Was it worth the price? (1 = overpriced, 5 = great deal)
|
|
529
|
+
--reliability 1-5 Did it respond quickly and without errors? (1 = flaky, 5 = rock solid)
|
|
530
|
+
--content Free-text is optional but helps other agents pick the best capability.
|
|
531
|
+
|
|
532
|
+
Example:
|
|
533
|
+
zero review run_abc123 --success --accuracy 5 --value 4 --reliability 5 --content "Fast, accurate translation"`
|
|
534
|
+
).argument("<runId>", "Run ID to review").option("--success", "The capability succeeded").option("--no-success", "The capability failed").requiredOption("--accuracy <n>", "Accuracy rating (1-5)", Number.parseInt).requiredOption("--value <n>", "Value rating (1-5)", Number.parseInt).requiredOption(
|
|
502
535
|
"--reliability <n>",
|
|
503
536
|
"Reliability rating (1-5)",
|
|
504
537
|
Number.parseInt
|
|
@@ -617,6 +650,7 @@ ${address}`);
|
|
|
617
650
|
if (url) {
|
|
618
651
|
await open(url);
|
|
619
652
|
console.log("Opened funding page in your browser.");
|
|
653
|
+
console.log(`If it didn't open, visit: ${url}`);
|
|
620
654
|
console.log(`Your wallet address: ${address}`);
|
|
621
655
|
analyticsService.capture("wallet_funded", {
|
|
622
656
|
method: "browser",
|
|
@@ -806,6 +840,7 @@ var capabilityResponseSchema = z2.object({
|
|
|
806
840
|
method: z2.string(),
|
|
807
841
|
headers: z2.record(z2.string(), z2.string()).nullable(),
|
|
808
842
|
bodySchema: z2.record(z2.string(), z2.unknown()).nullable(),
|
|
843
|
+
responseSchema: z2.record(z2.string(), z2.unknown()).nullable(),
|
|
809
844
|
example: z2.object({ request: z2.unknown(), response: z2.unknown() }).nullable(),
|
|
810
845
|
tags: z2.array(z2.string()).nullable(),
|
|
811
846
|
displayCostAmount: z2.string(),
|
|
@@ -923,13 +958,11 @@ import {
|
|
|
923
958
|
MAINNET_RELAY_API
|
|
924
959
|
} from "@relayprotocol/relay-sdk";
|
|
925
960
|
import { x402Client as X402Client } from "@x402/core/client";
|
|
926
|
-
import {
|
|
927
|
-
decodePaymentRequiredHeader,
|
|
928
|
-
decodePaymentResponseHeader,
|
|
929
|
-
encodePaymentSignatureHeader
|
|
930
|
-
} from "@x402/core/http";
|
|
961
|
+
import { decodePaymentResponseHeader, x402HTTPClient } from "@x402/core/http";
|
|
931
962
|
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
932
|
-
import {
|
|
963
|
+
import { createSIWxClientHook } from "@x402/extensions/sign-in-with-x";
|
|
964
|
+
import { wrapFetchWithPayment } from "@x402/fetch";
|
|
965
|
+
import { Receipt } from "mppx";
|
|
933
966
|
import { Mppx, tempo } from "mppx/client";
|
|
934
967
|
import {
|
|
935
968
|
createPublicClient,
|
|
@@ -937,11 +970,14 @@ import {
|
|
|
937
970
|
formatUnits,
|
|
938
971
|
http
|
|
939
972
|
} from "viem";
|
|
940
|
-
import { base } from "viem/chains";
|
|
973
|
+
import { base, baseSepolia } from "viem/chains";
|
|
941
974
|
var USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
975
|
+
var USDC_BASE_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
942
976
|
var USDC_TEMPO = "0x20c000000000000000000000b9537d11c60e8b50";
|
|
977
|
+
var PATHUSD_TEMPO = "0x20c0000000000000000000000000000000000000";
|
|
943
978
|
var BASE_CHAIN_ID = 8453;
|
|
944
979
|
var TEMPO_CHAIN_ID = 4217;
|
|
980
|
+
var TEMPO_TESTNET_CHAIN_ID = 42431;
|
|
945
981
|
var calculateBuffer = (baseBalance) => {
|
|
946
982
|
const twentyFivePercent = baseBalance / 4n;
|
|
947
983
|
const twoDollars = 2000000n;
|
|
@@ -955,6 +991,14 @@ var tempoChain = {
|
|
|
955
991
|
default: { http: ["https://rpc.tempo.xyz"] }
|
|
956
992
|
}
|
|
957
993
|
};
|
|
994
|
+
var tempoTestnetChain = {
|
|
995
|
+
id: TEMPO_TESTNET_CHAIN_ID,
|
|
996
|
+
name: "Tempo Testnet",
|
|
997
|
+
nativeCurrency: { name: "USD", symbol: "USD", decimals: 18 },
|
|
998
|
+
rpcUrls: {
|
|
999
|
+
default: { http: ["https://rpc.moderato.tempo.xyz"] }
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
958
1002
|
var ERC20_BALANCE_ABI = [
|
|
959
1003
|
{
|
|
960
1004
|
inputs: [{ name: "account", type: "address" }],
|
|
@@ -969,29 +1013,7 @@ var PaymentService = class {
|
|
|
969
1013
|
this.account = account;
|
|
970
1014
|
this.config = config;
|
|
971
1015
|
}
|
|
972
|
-
x402Client = null;
|
|
973
|
-
mppxClient = null;
|
|
974
1016
|
relayInitialized = false;
|
|
975
|
-
getX402Client = () => {
|
|
976
|
-
if (!this.x402Client) {
|
|
977
|
-
if (!this.account) throw new Error("No wallet configured");
|
|
978
|
-
this.x402Client = new X402Client().register(
|
|
979
|
-
"eip155:*",
|
|
980
|
-
new ExactEvmScheme(this.account)
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
return this.x402Client;
|
|
984
|
-
};
|
|
985
|
-
getMppxClient = () => {
|
|
986
|
-
if (!this.mppxClient) {
|
|
987
|
-
if (!this.account) throw new Error("No wallet configured");
|
|
988
|
-
this.mppxClient = Mppx.create({
|
|
989
|
-
polyfill: false,
|
|
990
|
-
methods: [tempo({ account: this.account })]
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
return this.mppxClient;
|
|
994
|
-
};
|
|
995
1017
|
ensureRelayClient = () => {
|
|
996
1018
|
if (!this.relayInitialized) {
|
|
997
1019
|
createRelayClient({
|
|
@@ -1053,32 +1075,35 @@ var PaymentService = class {
|
|
|
1053
1075
|
}
|
|
1054
1076
|
throw new Error("Unrecognized 402 payment protocol");
|
|
1055
1077
|
};
|
|
1056
|
-
payX402 = async (url, request,
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
)
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1078
|
+
payX402 = async (url, request, _raw, maxPay) => {
|
|
1079
|
+
if (!this.account) throw new Error("No wallet configured");
|
|
1080
|
+
let capturedAmount = "0";
|
|
1081
|
+
const client = new X402Client().register(
|
|
1082
|
+
"eip155:*",
|
|
1083
|
+
new ExactEvmScheme(this.account)
|
|
1084
|
+
);
|
|
1085
|
+
client.onBeforePaymentCreation(async (context) => {
|
|
1086
|
+
const requirement = context.paymentRequired.accepts[0];
|
|
1087
|
+
if (!requirement) return;
|
|
1088
|
+
capturedAmount = formatUnits(BigInt(requirement.amount), 6);
|
|
1089
|
+
if (maxPay && Number.parseFloat(capturedAmount) > Number.parseFloat(maxPay)) {
|
|
1090
|
+
return {
|
|
1091
|
+
abort: true,
|
|
1092
|
+
reason: `Payment of ${capturedAmount} USDC exceeds --max-pay ${maxPay}`
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
const httpClient = new x402HTTPClient(client).onPaymentRequired(
|
|
1097
|
+
createSIWxClientHook(this.account)
|
|
1098
|
+
);
|
|
1099
|
+
const wrappedFetch = wrapFetchWithPayment(fetch, httpClient);
|
|
1100
|
+
const response = await wrappedFetch(url, {
|
|
1073
1101
|
method: request.method,
|
|
1074
|
-
headers:
|
|
1075
|
-
...request.headers,
|
|
1076
|
-
"PAYMENT-SIGNATURE": signatureHeader
|
|
1077
|
-
},
|
|
1102
|
+
headers: request.headers,
|
|
1078
1103
|
body: request.body
|
|
1079
1104
|
});
|
|
1080
1105
|
let txHash = null;
|
|
1081
|
-
const paymentResponseHeader =
|
|
1106
|
+
const paymentResponseHeader = response.headers.get("payment-response") ?? response.headers.get("x-payment-response");
|
|
1082
1107
|
if (paymentResponseHeader) {
|
|
1083
1108
|
try {
|
|
1084
1109
|
const settlement = decodePaymentResponseHeader(paymentResponseHeader);
|
|
@@ -1087,61 +1112,86 @@ var PaymentService = class {
|
|
|
1087
1112
|
}
|
|
1088
1113
|
}
|
|
1089
1114
|
return {
|
|
1090
|
-
response
|
|
1115
|
+
response,
|
|
1091
1116
|
protocol: "x402",
|
|
1092
1117
|
chain: "base",
|
|
1093
1118
|
txHash,
|
|
1094
|
-
amount:
|
|
1119
|
+
amount: capturedAmount,
|
|
1095
1120
|
asset: "USDC"
|
|
1096
1121
|
};
|
|
1097
1122
|
};
|
|
1098
|
-
payMpp = async (url, request,
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1123
|
+
payMpp = async (url, request, _raw, maxPay) => {
|
|
1124
|
+
if (!this.account) throw new Error("No wallet configured");
|
|
1125
|
+
let capturedAmount = "0";
|
|
1126
|
+
const mppx = Mppx.create({
|
|
1127
|
+
polyfill: false,
|
|
1128
|
+
methods: [tempo({ account: this.account })],
|
|
1129
|
+
onChallenge: async (challenge) => {
|
|
1130
|
+
const challengeRequest = challenge.request;
|
|
1131
|
+
const amountRaw = challengeRequest.amount;
|
|
1132
|
+
capturedAmount = formatUnits(BigInt(amountRaw), 6);
|
|
1133
|
+
if (maxPay && Number.parseFloat(capturedAmount) > Number.parseFloat(maxPay)) {
|
|
1134
|
+
throw new Error(
|
|
1135
|
+
`Payment of ${capturedAmount} USDC exceeds --max-pay ${maxPay}`
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
const methodDetails = challengeRequest.methodDetails;
|
|
1139
|
+
const challengeChainId = challengeRequest.chainId ?? methodDetails?.chainId;
|
|
1140
|
+
const isTestnet = challengeChainId === TEMPO_TESTNET_CHAIN_ID;
|
|
1141
|
+
const balanceChain = isTestnet ? "tempo-testnet" : "tempo";
|
|
1142
|
+
const tempoBalance = await this.getBalanceRaw(balanceChain);
|
|
1143
|
+
if (tempoBalance < BigInt(amountRaw)) {
|
|
1144
|
+
if (isTestnet) {
|
|
1145
|
+
throw new Error(
|
|
1146
|
+
`Insufficient pathUSD on Tempo testnet: have ${formatUnits(tempoBalance, 6)}, need ${capturedAmount}. Fund your wallet with the Tempo testnet faucet: https://docs.tempo.xyz/quickstart/faucet`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
await this.bridgeToTempo(BigInt(amountRaw));
|
|
1150
|
+
}
|
|
1151
|
+
return void 0;
|
|
1152
|
+
}
|
|
1103
1153
|
});
|
|
1104
|
-
const
|
|
1105
|
-
const amountRaw = challenge.request.amount;
|
|
1106
|
-
const amountUsdc = formatUnits(BigInt(amountRaw), 6);
|
|
1107
|
-
if (maxPay && Number.parseFloat(amountUsdc) > Number.parseFloat(maxPay)) {
|
|
1108
|
-
throw new Error(
|
|
1109
|
-
`Payment of ${amountUsdc} USDC exceeds --max-pay ${maxPay}`
|
|
1110
|
-
);
|
|
1111
|
-
}
|
|
1112
|
-
const tempoBalance = await this.getBalanceRaw("tempo");
|
|
1113
|
-
if (tempoBalance < BigInt(amountRaw)) {
|
|
1114
|
-
await this.bridgeToTempo(BigInt(amountRaw));
|
|
1115
|
-
}
|
|
1116
|
-
const mppx = this.getMppxClient();
|
|
1117
|
-
const credential = await mppx.createCredential(challengeResponse);
|
|
1118
|
-
const retryResponse = await fetch(url, {
|
|
1154
|
+
const response = await mppx.fetch(url, {
|
|
1119
1155
|
method: request.method,
|
|
1120
|
-
headers:
|
|
1121
|
-
...request.headers,
|
|
1122
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header name
|
|
1123
|
-
Authorization: credential
|
|
1124
|
-
},
|
|
1156
|
+
headers: request.headers,
|
|
1125
1157
|
body: request.body
|
|
1126
1158
|
});
|
|
1159
|
+
let txHash = null;
|
|
1160
|
+
try {
|
|
1161
|
+
const receipt = Receipt.fromResponse(response);
|
|
1162
|
+
txHash = receipt.reference ?? null;
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1127
1165
|
return {
|
|
1128
|
-
response
|
|
1166
|
+
response,
|
|
1129
1167
|
protocol: "mpp",
|
|
1130
1168
|
chain: "tempo",
|
|
1131
|
-
txHash
|
|
1132
|
-
|
|
1133
|
-
amount: amountUsdc,
|
|
1169
|
+
txHash,
|
|
1170
|
+
amount: capturedAmount,
|
|
1134
1171
|
asset: "USDC"
|
|
1135
1172
|
};
|
|
1136
1173
|
};
|
|
1174
|
+
resolveChainConfig = (chain) => {
|
|
1175
|
+
switch (chain) {
|
|
1176
|
+
case "base":
|
|
1177
|
+
return { viemChain: base, token: USDC_BASE };
|
|
1178
|
+
case "base-sepolia":
|
|
1179
|
+
return { viemChain: baseSepolia, token: USDC_BASE_SEPOLIA };
|
|
1180
|
+
case "tempo":
|
|
1181
|
+
return { viemChain: tempoChain, token: USDC_TEMPO };
|
|
1182
|
+
case "tempo-testnet":
|
|
1183
|
+
return { viemChain: tempoTestnetChain, token: PATHUSD_TEMPO };
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1137
1186
|
getBalanceRaw = async (chain) => {
|
|
1138
1187
|
if (!this.account) return 0n;
|
|
1188
|
+
const { viemChain, token } = this.resolveChainConfig(chain);
|
|
1139
1189
|
const client = createPublicClient({
|
|
1140
|
-
chain:
|
|
1190
|
+
chain: viemChain,
|
|
1141
1191
|
transport: http()
|
|
1142
1192
|
});
|
|
1143
1193
|
const balance = await client.readContract({
|
|
1144
|
-
address:
|
|
1194
|
+
address: token,
|
|
1145
1195
|
abi: ERC20_BALANCE_ABI,
|
|
1146
1196
|
functionName: "balanceOf",
|
|
1147
1197
|
args: [this.account.address]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroxyz/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"zero": "dist/index.js",
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
"dev": "tsx src/index.ts",
|
|
21
21
|
"cli": "ZERO_API_URL=http://localhost:1111 tsx src/index.ts",
|
|
22
22
|
"test:integration": "vitest run --project integration",
|
|
23
|
+
"test:online": "vitest run --project online",
|
|
23
24
|
"test:unit": "vitest run --project unit",
|
|
24
|
-
"test": "pnpm run test:integration",
|
|
25
|
+
"test": "pnpm run test:unit && pnpm run test:integration",
|
|
25
26
|
"typecheck": "tsc"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
@@ -29,6 +30,8 @@
|
|
|
29
30
|
"@relayprotocol/relay-sdk": "^5.2.1",
|
|
30
31
|
"@x402/core": "^2.9.0",
|
|
31
32
|
"@x402/evm": "^2.9.0",
|
|
33
|
+
"@x402/extensions": "^2.9.0",
|
|
34
|
+
"@x402/fetch": "^2.9.0",
|
|
32
35
|
"commander": "^13.0.0",
|
|
33
36
|
"mppx": "^0.5.9",
|
|
34
37
|
"open": "^11.0.0",
|
|
@@ -37,7 +40,10 @@
|
|
|
37
40
|
"zod": "^4.3.5"
|
|
38
41
|
},
|
|
39
42
|
"devDependencies": {
|
|
43
|
+
"@hono/node-server": "^1.19.13",
|
|
40
44
|
"@types/node": "^25.0.7",
|
|
45
|
+
"@x402/hono": "^2.9.0",
|
|
46
|
+
"hono": "^4.12.12",
|
|
41
47
|
"tsup": "^8.5.1",
|
|
42
48
|
"tsx": "^4.21.0",
|
|
43
49
|
"typescript": "^5.9.3",
|