@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.
Files changed (2) hide show
  1. package/dist/index.js +113 -145
  2. 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.8",
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
- Run \`zero review ${runResult.runId}\` to rate this capability.
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").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(
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 { Challenge, Receipt } from "mppx";
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
- toX402RawChallenge = (headerValue) => {
1074
- try {
1075
- return JSON.parse(Buffer.from(headerValue, "base64").toString("utf8"));
1076
- } catch {
1077
- return { encoded: headerValue };
1078
- }
1079
- };
1080
- toX402HeaderValue = (raw) => {
1081
- const encoded = raw?.encoded;
1082
- if (typeof encoded === "string" && encoded.length > 0) {
1083
- return encoded;
1084
- }
1085
- return Buffer.from(JSON.stringify(raw)).toString("base64");
1086
- };
1087
- payX402 = async (url, request, raw, maxPay) => {
1088
- const client = this.getX402Client();
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
- amountUsdc = formatUnits(BigInt(requirement.amount), 6);
1099
- if (maxPay && Number.parseFloat(amountUsdc) > Number.parseFloat(maxPay)) {
1100
- throw new Error(
1101
- `Payment of ${amountUsdc} USDC exceeds --max-pay ${maxPay}`
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
- const paymentPayload = await client.createPaymentPayload(paymentRequired);
1105
- const signatureHeader = encodePaymentSignatureHeader(paymentPayload);
1106
- const retryResponse = await fetch(url, {
1107
- method: request.method,
1108
- headers: {
1109
- ...request.headers,
1110
- "PAYMENT-SIGNATURE": signatureHeader
1111
- },
1112
- body: request.body
1113
- });
1114
- if (retryResponse.status === 402) {
1115
- if (attempt >= X402_PAYMENT_MAX_ATTEMPTS) {
1116
- throw new Error(
1117
- `x402 payment failed after ${X402_PAYMENT_MAX_ATTEMPTS} attempts`
1118
- );
1119
- }
1120
- const nextHeader = retryResponse.headers.get("payment-required") ?? retryResponse.headers.get("x-payment-required");
1121
- if (!nextHeader) {
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
- "x402 payment retry returned 402 without payment-required header"
1135
+ `Payment of ${capturedAmount} USDC exceeds --max-pay ${maxPay}`
1124
1136
  );
1125
1137
  }
1126
- currentRaw = this.toX402RawChallenge(nextHeader);
1127
- await new Promise(
1128
- (resolve) => setTimeout(resolve, attempt * X402_PAYMENT_RETRY_DELAY_MS)
1129
- );
1130
- continue;
1131
- }
1132
- let txHash = null;
1133
- const paymentResponseHeader = retryResponse.headers.get("payment-response") ?? retryResponse.headers.get("x-payment-response");
1134
- if (paymentResponseHeader) {
1135
- try {
1136
- const settlement = decodePaymentResponseHeader(paymentResponseHeader);
1137
- txHash = settlement.transaction ?? null;
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 challenge = Challenge.fromResponse(challengeResponse);
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(retryResponse);
1161
+ const receipt = Receipt.fromResponse(response);
1194
1162
  txHash = receipt.reference ?? null;
1195
1163
  } catch {
1196
1164
  }
1197
1165
  return {
1198
- response: retryResponse,
1166
+ response,
1199
1167
  protocol: "mpp",
1200
1168
  chain: "tempo",
1201
1169
  txHash,
1202
- amount: amountUsdc,
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.8",
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",