@zeroxyz/cli 0.0.8 → 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 +113 -145
- package/package.json +3 -1
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",
|
|
@@ -36,6 +36,8 @@ var package_default = {
|
|
|
36
36
|
"@relayprotocol/relay-sdk": "^5.2.1",
|
|
37
37
|
"@x402/core": "^2.9.0",
|
|
38
38
|
"@x402/evm": "^2.9.0",
|
|
39
|
+
"@x402/extensions": "^2.9.0",
|
|
40
|
+
"@x402/fetch": "^2.9.0",
|
|
39
41
|
commander: "^13.0.0",
|
|
40
42
|
mppx: "^0.5.9",
|
|
41
43
|
open: "^11.0.0",
|
|
@@ -222,10 +224,25 @@ var fetchCommand = (appContext2) => new Command2("fetch").description("Fetch a c
|
|
|
222
224
|
paymentMode: "charge"
|
|
223
225
|
}
|
|
224
226
|
});
|
|
227
|
+
console.error(`
|
|
228
|
+
Run ID: ${runResult.runId}`);
|
|
225
229
|
console.error(
|
|
226
|
-
`
|
|
227
|
-
|
|
228
|
-
|
|
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")
|
|
229
246
|
);
|
|
230
247
|
} catch {
|
|
231
248
|
}
|
|
@@ -502,7 +519,19 @@ var initCommand = (appContext2) => new Command4("init").description("Initialize
|
|
|
502
519
|
|
|
503
520
|
// src/commands/review-command.ts
|
|
504
521
|
import { Command as Command5 } from "commander";
|
|
505
|
-
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(
|
|
506
535
|
"--reliability <n>",
|
|
507
536
|
"Reliability rating (1-5)",
|
|
508
537
|
Number.parseInt
|
|
@@ -621,6 +650,7 @@ ${address}`);
|
|
|
621
650
|
if (url) {
|
|
622
651
|
await open(url);
|
|
623
652
|
console.log("Opened funding page in your browser.");
|
|
653
|
+
console.log(`If it didn't open, visit: ${url}`);
|
|
624
654
|
console.log(`Your wallet address: ${address}`);
|
|
625
655
|
analyticsService.capture("wallet_funded", {
|
|
626
656
|
method: "browser",
|
|
@@ -810,6 +840,7 @@ var capabilityResponseSchema = z2.object({
|
|
|
810
840
|
method: z2.string(),
|
|
811
841
|
headers: z2.record(z2.string(), z2.string()).nullable(),
|
|
812
842
|
bodySchema: z2.record(z2.string(), z2.unknown()).nullable(),
|
|
843
|
+
responseSchema: z2.record(z2.string(), z2.unknown()).nullable(),
|
|
813
844
|
example: z2.object({ request: z2.unknown(), response: z2.unknown() }).nullable(),
|
|
814
845
|
tags: z2.array(z2.string()).nullable(),
|
|
815
846
|
displayCostAmount: z2.string(),
|
|
@@ -927,13 +958,11 @@ import {
|
|
|
927
958
|
MAINNET_RELAY_API
|
|
928
959
|
} from "@relayprotocol/relay-sdk";
|
|
929
960
|
import { x402Client as X402Client } from "@x402/core/client";
|
|
930
|
-
import {
|
|
931
|
-
decodePaymentRequiredHeader,
|
|
932
|
-
decodePaymentResponseHeader,
|
|
933
|
-
encodePaymentSignatureHeader
|
|
934
|
-
} from "@x402/core/http";
|
|
961
|
+
import { decodePaymentResponseHeader, x402HTTPClient } from "@x402/core/http";
|
|
935
962
|
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
936
|
-
import {
|
|
963
|
+
import { createSIWxClientHook } from "@x402/extensions/sign-in-with-x";
|
|
964
|
+
import { wrapFetchWithPayment } from "@x402/fetch";
|
|
965
|
+
import { Receipt } from "mppx";
|
|
937
966
|
import { Mppx, tempo } from "mppx/client";
|
|
938
967
|
import {
|
|
939
968
|
createPublicClient,
|
|
@@ -949,8 +978,6 @@ var PATHUSD_TEMPO = "0x20c0000000000000000000000000000000000000";
|
|
|
949
978
|
var BASE_CHAIN_ID = 8453;
|
|
950
979
|
var TEMPO_CHAIN_ID = 4217;
|
|
951
980
|
var TEMPO_TESTNET_CHAIN_ID = 42431;
|
|
952
|
-
var X402_PAYMENT_MAX_ATTEMPTS = 6;
|
|
953
|
-
var X402_PAYMENT_RETRY_DELAY_MS = 300;
|
|
954
981
|
var calculateBuffer = (baseBalance) => {
|
|
955
982
|
const twentyFivePercent = baseBalance / 4n;
|
|
956
983
|
const twoDollars = 2000000n;
|
|
@@ -986,29 +1013,7 @@ var PaymentService = class {
|
|
|
986
1013
|
this.account = account;
|
|
987
1014
|
this.config = config;
|
|
988
1015
|
}
|
|
989
|
-
x402Client = null;
|
|
990
|
-
mppxClient = null;
|
|
991
1016
|
relayInitialized = false;
|
|
992
|
-
getX402Client = () => {
|
|
993
|
-
if (!this.x402Client) {
|
|
994
|
-
if (!this.account) throw new Error("No wallet configured");
|
|
995
|
-
this.x402Client = new X402Client().register(
|
|
996
|
-
"eip155:*",
|
|
997
|
-
new ExactEvmScheme(this.account)
|
|
998
|
-
);
|
|
999
|
-
}
|
|
1000
|
-
return this.x402Client;
|
|
1001
|
-
};
|
|
1002
|
-
getMppxClient = () => {
|
|
1003
|
-
if (!this.mppxClient) {
|
|
1004
|
-
if (!this.account) throw new Error("No wallet configured");
|
|
1005
|
-
this.mppxClient = Mppx.create({
|
|
1006
|
-
polyfill: false,
|
|
1007
|
-
methods: [tempo({ account: this.account })]
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
return this.mppxClient;
|
|
1011
|
-
};
|
|
1012
1017
|
ensureRelayClient = () => {
|
|
1013
1018
|
if (!this.relayInitialized) {
|
|
1014
1019
|
createRelayClient({
|
|
@@ -1070,136 +1075,99 @@ var PaymentService = class {
|
|
|
1070
1075
|
}
|
|
1071
1076
|
throw new Error("Unrecognized 402 payment protocol");
|
|
1072
1077
|
};
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
let currentRaw = raw;
|
|
1090
|
-
let amountUsdc = "0";
|
|
1091
|
-
for (let attempt = 1; attempt <= X402_PAYMENT_MAX_ATTEMPTS; attempt++) {
|
|
1092
|
-
const headerValue = this.toX402HeaderValue(currentRaw);
|
|
1093
|
-
const paymentRequired = decodePaymentRequiredHeader(headerValue);
|
|
1094
|
-
const requirement = paymentRequired.accepts[0];
|
|
1095
|
-
if (!requirement) {
|
|
1096
|
-
throw new Error("No accepted payment methods in x402 challenge");
|
|
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
|
+
};
|
|
1097
1094
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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, {
|
|
1101
|
+
method: request.method,
|
|
1102
|
+
headers: request.headers,
|
|
1103
|
+
body: request.body
|
|
1104
|
+
});
|
|
1105
|
+
let txHash = null;
|
|
1106
|
+
const paymentResponseHeader = response.headers.get("payment-response") ?? response.headers.get("x-payment-response");
|
|
1107
|
+
if (paymentResponseHeader) {
|
|
1108
|
+
try {
|
|
1109
|
+
const settlement = decodePaymentResponseHeader(paymentResponseHeader);
|
|
1110
|
+
txHash = settlement.transaction ?? null;
|
|
1111
|
+
} catch {
|
|
1103
1112
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1113
|
+
}
|
|
1114
|
+
return {
|
|
1115
|
+
response,
|
|
1116
|
+
protocol: "x402",
|
|
1117
|
+
chain: "base",
|
|
1118
|
+
txHash,
|
|
1119
|
+
amount: capturedAmount,
|
|
1120
|
+
asset: "USDC"
|
|
1121
|
+
};
|
|
1122
|
+
};
|
|
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)) {
|
|
1122
1134
|
throw new Error(
|
|
1123
|
-
|
|
1135
|
+
`Payment of ${capturedAmount} USDC exceeds --max-pay ${maxPay}`
|
|
1124
1136
|
);
|
|
1125
1137
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
} catch {
|
|
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));
|
|
1139
1150
|
}
|
|
1151
|
+
return void 0;
|
|
1140
1152
|
}
|
|
1141
|
-
return {
|
|
1142
|
-
response: retryResponse,
|
|
1143
|
-
protocol: "x402",
|
|
1144
|
-
chain: "base",
|
|
1145
|
-
txHash,
|
|
1146
|
-
amount: amountUsdc,
|
|
1147
|
-
asset: "USDC"
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
throw new Error("x402 payment failed");
|
|
1151
|
-
};
|
|
1152
|
-
payMpp = async (url, request, raw, maxPay) => {
|
|
1153
|
-
const wwwAuth = raw?.["www-authenticate"] ?? "";
|
|
1154
|
-
const challengeResponse = new Response("Payment Required", {
|
|
1155
|
-
status: 402,
|
|
1156
|
-
headers: { "www-authenticate": wwwAuth }
|
|
1157
1153
|
});
|
|
1158
|
-
const
|
|
1159
|
-
const challengeRequest = challenge.request;
|
|
1160
|
-
const amountRaw = challengeRequest.amount;
|
|
1161
|
-
const amountUsdc = formatUnits(BigInt(amountRaw), 6);
|
|
1162
|
-
if (maxPay && Number.parseFloat(amountUsdc) > Number.parseFloat(maxPay)) {
|
|
1163
|
-
throw new Error(
|
|
1164
|
-
`Payment of ${amountUsdc} USDC exceeds --max-pay ${maxPay}`
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
const methodDetails = challengeRequest.methodDetails;
|
|
1168
|
-
const challengeChainId = challengeRequest.chainId ?? methodDetails?.chainId;
|
|
1169
|
-
const isTestnet = challengeChainId === TEMPO_TESTNET_CHAIN_ID;
|
|
1170
|
-
const balanceChain = isTestnet ? "tempo-testnet" : "tempo";
|
|
1171
|
-
const tempoBalance = await this.getBalanceRaw(balanceChain);
|
|
1172
|
-
if (tempoBalance < BigInt(amountRaw)) {
|
|
1173
|
-
if (isTestnet) {
|
|
1174
|
-
throw new Error(
|
|
1175
|
-
`Insufficient pathUSD on Tempo testnet: have ${formatUnits(tempoBalance, 6)}, need ${amountUsdc}. Fund your wallet with the Tempo testnet faucet: https://docs.tempo.xyz/quickstart/faucet`
|
|
1176
|
-
);
|
|
1177
|
-
}
|
|
1178
|
-
await this.bridgeToTempo(BigInt(amountRaw));
|
|
1179
|
-
}
|
|
1180
|
-
const mppx = this.getMppxClient();
|
|
1181
|
-
const credential = await mppx.createCredential(challengeResponse);
|
|
1182
|
-
const retryResponse = await fetch(url, {
|
|
1154
|
+
const response = await mppx.fetch(url, {
|
|
1183
1155
|
method: request.method,
|
|
1184
|
-
headers:
|
|
1185
|
-
...request.headers,
|
|
1186
|
-
// biome-ignore lint/style/useNamingConvention: HTTP header name
|
|
1187
|
-
Authorization: credential
|
|
1188
|
-
},
|
|
1156
|
+
headers: request.headers,
|
|
1189
1157
|
body: request.body
|
|
1190
1158
|
});
|
|
1191
1159
|
let txHash = null;
|
|
1192
1160
|
try {
|
|
1193
|
-
const receipt = Receipt.fromResponse(
|
|
1161
|
+
const receipt = Receipt.fromResponse(response);
|
|
1194
1162
|
txHash = receipt.reference ?? null;
|
|
1195
1163
|
} catch {
|
|
1196
1164
|
}
|
|
1197
1165
|
return {
|
|
1198
|
-
response
|
|
1166
|
+
response,
|
|
1199
1167
|
protocol: "mpp",
|
|
1200
1168
|
chain: "tempo",
|
|
1201
1169
|
txHash,
|
|
1202
|
-
amount:
|
|
1170
|
+
amount: capturedAmount,
|
|
1203
1171
|
asset: "USDC"
|
|
1204
1172
|
};
|
|
1205
1173
|
};
|
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",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"@relayprotocol/relay-sdk": "^5.2.1",
|
|
31
31
|
"@x402/core": "^2.9.0",
|
|
32
32
|
"@x402/evm": "^2.9.0",
|
|
33
|
+
"@x402/extensions": "^2.9.0",
|
|
34
|
+
"@x402/fetch": "^2.9.0",
|
|
33
35
|
"commander": "^13.0.0",
|
|
34
36
|
"mppx": "^0.5.9",
|
|
35
37
|
"open": "^11.0.0",
|