@bankr/cli 0.2.7 → 0.2.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/cli.js CHANGED
@@ -22,7 +22,7 @@ import { submitCommand, submitJsonCommand } from "./commands/submit.js";
22
22
  import { updateCommand } from "./commands/update.js";
23
23
  import { whoamiCommand } from "./commands/whoami.js";
24
24
  import { tokensSearchCommand, tokensInfoCommand } from "./commands/tokens.js";
25
- import { x402InitCommand, x402AddCommand, x402ConfigureCommand, x402DeployCommand, x402ListCommand, x402PauseResumeCommand, x402DeleteCommand, x402RevenueCommand, x402EnvSetCommand, x402EnvListCommand, x402EnvUnsetCommand, x402SearchCommand, } from "./commands/x402.js";
25
+ import { x402InitCommand, x402AddCommand, x402ConfigureCommand, x402DeployCommand, x402ListCommand, x402PauseResumeCommand, x402DeleteCommand, x402RevenueCommand, x402EnvSetCommand, x402EnvListCommand, x402EnvUnsetCommand, x402SearchCommand, x402SchemaCommand, x402CallCommand, } from "./commands/x402.js";
26
26
  import { profileViewCommand, profileCreateCommand, profileUpdateCommand, profileDeleteCommand, profileAddUpdateCommand, } from "./commands/profile.js";
27
27
  import * as output from "./lib/output.js";
28
28
  import { checkForUpdate } from "./lib/updateCheck.js";
@@ -774,6 +774,21 @@ x402Cmd
774
774
  .description("Search the x402 service marketplace (no auth required)")
775
775
  .option("--raw", "Output raw JSON (unformatted)")
776
776
  .action(async (query, opts) => x402SearchCommand(query, { raw: opts.raw }));
777
+ x402Cmd
778
+ .command("schema <url>")
779
+ .description("View input/output schema for a Bankr x402 endpoint (no auth required)")
780
+ .option("--raw", "Output raw JSON (unformatted)")
781
+ .action(async (url, opts) => x402SchemaCommand(url, opts));
782
+ x402Cmd
783
+ .command("call <url>")
784
+ .description("Call an x402-protected endpoint with automatic USDC payment")
785
+ .option("-X, --method <method>", "HTTP method (GET, POST, PUT, DELETE)", "GET")
786
+ .option("-d, --body <json>", "JSON request body")
787
+ .option("--max-payment <usd>", "Maximum payment in USD (default: 1, max: 10)", "1")
788
+ .option("-i, --interactive", "Fetch endpoint schema and prompt for input values")
789
+ .option("-y, --yes", "Skip payment confirmation")
790
+ .option("--raw", "Output raw JSON (unformatted)")
791
+ .action(async (url, opts) => x402CallCommand(url, opts));
777
792
  // Default: treat unrecognized arguments as a prompt
778
793
  program
779
794
  .arguments("[text...]")
@@ -59,6 +59,17 @@ export declare function x402EnvListCommand(): Promise<void>;
59
59
  * bankr x402 env unset KEY
60
60
  */
61
61
  export declare function x402EnvUnsetCommand(key: string): Promise<void>;
62
+ export declare function x402SchemaCommand(url: string, opts?: {
63
+ raw?: boolean;
64
+ }): Promise<void>;
65
+ export declare function x402CallCommand(url: string, opts?: {
66
+ method?: string;
67
+ body?: string;
68
+ maxPayment?: string;
69
+ yes?: boolean;
70
+ raw?: boolean;
71
+ interactive?: boolean;
72
+ }): Promise<void>;
62
73
  export declare function x402SearchCommand(queryParts: string[], opts?: {
63
74
  raw?: boolean;
64
75
  }): Promise<void>;
@@ -741,6 +741,479 @@ function printServiceFormatted(svc) {
741
741
  console.log(` ${chalk.dim("─".repeat(60))}`);
742
742
  console.log("");
743
743
  }
744
+ // ── Schema ────────────────────────────────────────────────────────────
745
+ export async function x402SchemaCommand(url, opts = {}) {
746
+ if (!url) {
747
+ output.error("Usage: bankr x402 schema <url>\n Example: bankr x402 schema https://x402.bankr.bot/0xabc.../my-service");
748
+ process.exit(1);
749
+ }
750
+ let parsed;
751
+ try {
752
+ parsed = new URL(url);
753
+ }
754
+ catch {
755
+ output.error("Invalid URL");
756
+ process.exit(1);
757
+ return; // unreachable but makes TS happy
758
+ }
759
+ if (!parsed.hostname.endsWith("x402.bankr.bot")) {
760
+ output.error("Schema lookup is only available for Bankr-hosted endpoints (x402.bankr.bot).");
761
+ process.exit(1);
762
+ }
763
+ const parts = parsed.pathname.split("/").filter(Boolean);
764
+ if (parts.length < 2) {
765
+ output.error("Invalid Bankr x402 URL. Expected: https://x402.bankr.bot/<address>/<service>");
766
+ process.exit(1);
767
+ }
768
+ const spin = output.spinner("Fetching endpoint schema...");
769
+ try {
770
+ const res = await fetch(`${getApiUrl()}/x402/endpoints/schema/${parts[0]}/${parts[1]}`);
771
+ if (res.status === 404) {
772
+ spin.fail("Endpoint not found");
773
+ output.error(`No active endpoint found for ${parts[0]}/${parts[1]}`);
774
+ process.exit(1);
775
+ }
776
+ const result = await handleResponse(res);
777
+ spin.stop();
778
+ if (opts.raw) {
779
+ console.log(JSON.stringify(result, null, 2));
780
+ return;
781
+ }
782
+ // Header
783
+ console.log();
784
+ console.log(` ${chalk.bold(result.name)}`);
785
+ if (result.description) {
786
+ console.log(` ${chalk.dim(result.description)}`);
787
+ }
788
+ console.log();
789
+ // Metadata
790
+ console.log(` ${chalk.dim("URL")} ${url}`);
791
+ console.log(` ${chalk.dim("Method")} ${chalk.bold(result.methods.join(", ") || "GET")}`);
792
+ console.log(` ${chalk.dim("Price")} ${chalk.green(`$${result.price} ${result.currency}`)}`);
793
+ console.log(` ${chalk.dim("Network")} ${result.network}`);
794
+ // Input schema
795
+ if (result.schema?.input) {
796
+ console.log();
797
+ const isGet = result.methods.includes("GET") && !result.methods.includes("POST");
798
+ const label = isGet ? "Input (query params)" : "Input (JSON body)";
799
+ console.log(` ${chalk.bold(label)}`);
800
+ if (isJsonSchema(result.schema.input)) {
801
+ for (const line of renderJsonSchemaProps(result.schema.input)) {
802
+ console.log(line);
803
+ }
804
+ }
805
+ else {
806
+ for (const line of renderLegacySchema(result.schema.input)) {
807
+ console.log(line);
808
+ }
809
+ }
810
+ // Example
811
+ if (isJsonSchema(result.schema.input) &&
812
+ result.schema.input.properties) {
813
+ const props = Object.entries(result.schema.input.properties);
814
+ console.log();
815
+ if (isGet) {
816
+ const qs = props.map(([k, p]) => `${k}=<${p.type}>`).join("&");
817
+ console.log(` ${chalk.dim("Example")} ${chalk.dim("GET")} ${url}?${qs}`);
818
+ }
819
+ else {
820
+ const bodyObj = {};
821
+ for (const [k, p] of props)
822
+ bodyObj[k] = `<${p.type}>`;
823
+ console.log(` ${chalk.dim("Example")} ${chalk.dim(result.methods[0] || "POST")} ${url}`);
824
+ console.log(` ${chalk.dim(JSON.stringify(bodyObj))}`);
825
+ }
826
+ }
827
+ }
828
+ // Output schema
829
+ if (result.schema?.output) {
830
+ console.log();
831
+ console.log(` ${chalk.bold("Output")}`);
832
+ if (isJsonSchema(result.schema.output)) {
833
+ for (const line of renderJsonSchemaProps(result.schema.output)) {
834
+ console.log(line);
835
+ }
836
+ }
837
+ else {
838
+ for (const line of renderLegacySchema(result.schema.output)) {
839
+ console.log(line);
840
+ }
841
+ }
842
+ }
843
+ if (!result.schema?.input && !result.schema?.output) {
844
+ console.log();
845
+ console.log(` ${chalk.dim("No input/output schema defined for this endpoint.")}`);
846
+ }
847
+ // Tags
848
+ if (result.tags?.length) {
849
+ console.log();
850
+ console.log(` ${result.tags.map((t) => chalk.dim(`#${t}`)).join(" ")}`);
851
+ }
852
+ console.log(` ${chalk.dim("─".repeat(60))}`);
853
+ console.log();
854
+ }
855
+ catch (err) {
856
+ spin.fail("Schema lookup failed");
857
+ output.error(err instanceof Error ? err.message : String(err));
858
+ process.exit(1);
859
+ }
860
+ }
861
+ async function fetchSchemaForInteractive(url) {
862
+ let parsed;
863
+ try {
864
+ parsed = new URL(url);
865
+ }
866
+ catch {
867
+ return null;
868
+ }
869
+ const isBankr = parsed.hostname.endsWith("x402.bankr.bot");
870
+ const parts = parsed.pathname.split("/").filter(Boolean);
871
+ // Strategy 1: Direct schema API (works once deployed)
872
+ if (isBankr && parts.length >= 2) {
873
+ try {
874
+ const res = await fetch(`${getApiUrl()}/x402/endpoints/schema/${parts[0]}/${parts[1]}`);
875
+ if (res.ok) {
876
+ const data = (await res.json());
877
+ if (data.success) {
878
+ const rawInput = data.schema?.input;
879
+ const inputSchema = rawInput && isJsonSchema(rawInput)
880
+ ? rawInput
881
+ : null;
882
+ return {
883
+ name: data.name,
884
+ description: data.description,
885
+ methods: data.methods,
886
+ price: data.price,
887
+ currency: data.currency,
888
+ inputSchema,
889
+ };
890
+ }
891
+ }
892
+ }
893
+ catch {
894
+ // Fall through to next strategy
895
+ }
896
+ }
897
+ // Strategy 2: Discover API (already deployed, searches by service name)
898
+ if (isBankr && parts.length >= 2) {
899
+ try {
900
+ const serviceName = parts[parts.length - 1];
901
+ const discoverRes = await fetch(`${getApiUrl()}/x402/endpoints/discover?${new URLSearchParams({ q: serviceName, limit: "5" })}`);
902
+ if (discoverRes.ok) {
903
+ const discoverData = (await discoverRes.json());
904
+ // Match by slug or URL path
905
+ const slug = `${parts[0]}/${parts[1]}`;
906
+ const match = discoverData.services?.find((s) => s.slug === slug || s.slug.endsWith(`/${serviceName}`));
907
+ if (match) {
908
+ const route = match.routes?.[0];
909
+ const { input: rawInput } = resolveSchemas(route?.schema);
910
+ const inputSchema = rawInput && isJsonSchema(rawInput)
911
+ ? rawInput
912
+ : null;
913
+ return {
914
+ name: match.name,
915
+ description: match.description,
916
+ methods: route?.methods?.filter((m) => m !== "*") ?? ["GET"],
917
+ price: route?.price ?? "?",
918
+ currency: route?.currency ?? "USDC",
919
+ inputSchema,
920
+ };
921
+ }
922
+ }
923
+ }
924
+ catch {
925
+ // Fall through to next strategy
926
+ }
927
+ }
928
+ // Strategy 3: Probe endpoint for 402 response to get price info.
929
+ // Checks header first (Bankr endpoints), then falls back to response
930
+ // body (Coinbase x402 standard used by external endpoints).
931
+ try {
932
+ const probeRes = await fetch(url, { method: "GET" });
933
+ if (probeRes.status === 402) {
934
+ const paymentHeader = probeRes.headers.get("payment-required") ??
935
+ probeRes.headers.get("x-payment-required");
936
+ let accept = null;
937
+ if (paymentHeader) {
938
+ try {
939
+ const decoded = Buffer.from(paymentHeader, "base64").toString("utf-8");
940
+ const requirements = JSON.parse(decoded);
941
+ accept = requirements.accepts?.[0] ?? requirements;
942
+ }
943
+ catch {
944
+ // Header parse failed, try body
945
+ }
946
+ }
947
+ if (!accept) {
948
+ try {
949
+ const bodyText = await probeRes.text();
950
+ const bodyJson = JSON.parse(bodyText);
951
+ accept = bodyJson.accepts?.[0] ?? null;
952
+ }
953
+ catch {
954
+ // Body parse failed too
955
+ }
956
+ }
957
+ const acceptAmount = accept?.maxAmountRequired ?? accept?.amount;
958
+ if (acceptAmount) {
959
+ const amountAtomic = BigInt(acceptAmount);
960
+ const amountUsd = Number(amountAtomic) / 1000000;
961
+ // Extract service name from URL path
962
+ const serviceName = parts[parts.length - 1] ?? "endpoint";
963
+ return {
964
+ name: serviceName,
965
+ description: accept?.description ?? "",
966
+ methods: ["GET"],
967
+ price: amountUsd.toFixed(6),
968
+ currency: "USDC",
969
+ inputSchema: null,
970
+ };
971
+ }
972
+ }
973
+ }
974
+ catch {
975
+ // Fall through
976
+ }
977
+ return null;
978
+ }
979
+ // ── Call ──────────────────────────────────────────────────────────────
980
+ export async function x402CallCommand(url, opts = {}) {
981
+ if (!url) {
982
+ output.error("Usage: bankr x402 call <url> [--method POST] [--body '{}']");
983
+ process.exit(1);
984
+ }
985
+ let method = opts.method?.toUpperCase() ?? "GET";
986
+ const maxPaymentUsd = opts.maxPayment ? parseFloat(opts.maxPayment) : 1;
987
+ let requestBody = opts.body;
988
+ if (isNaN(maxPaymentUsd) || maxPaymentUsd <= 0 || maxPaymentUsd > 10) {
989
+ output.error("--max-payment must be between 0 and 10 (USD)");
990
+ process.exit(1);
991
+ }
992
+ // Interactive mode: fetch schema and prompt for input values
993
+ let resolvedPrice = null;
994
+ if (opts.interactive) {
995
+ const schema = await fetchSchemaForInteractive(url);
996
+ if (schema) {
997
+ // Full schema available — show endpoint info and prompt for fields
998
+ console.log();
999
+ console.log(` ${chalk.bold(schema.name)}`);
1000
+ if (schema.description) {
1001
+ console.log(` ${chalk.dim(schema.description)}`);
1002
+ }
1003
+ console.log(` ${chalk.dim("Price")} ${chalk.green(`$${schema.price} ${schema.currency}`)}`);
1004
+ console.log();
1005
+ // Use the endpoint's actual price and preferred method
1006
+ resolvedPrice = parseFloat(schema.price);
1007
+ if (schema.methods.length > 0) {
1008
+ method = schema.methods.includes("POST") ? "POST" : schema.methods[0];
1009
+ }
1010
+ // Prompt for each input field
1011
+ if (schema.inputSchema && schema.inputSchema.properties) {
1012
+ const required = new Set(schema.inputSchema.required ?? []);
1013
+ const values = {};
1014
+ for (const [key, prop] of Object.entries(schema.inputSchema.properties)) {
1015
+ const typedProp = prop;
1016
+ const isRequired = required.has(key);
1017
+ const label = isRequired
1018
+ ? `${chalk.bold(key)} ${chalk.dim(`(${typedProp.type})`)}`
1019
+ : `${key} ${chalk.dim(`(${typedProp.type}, optional)`)}`;
1020
+ const hint = typedProp.description ?? "";
1021
+ // Enum fields → select prompt
1022
+ if (typedProp.enum && typedProp.enum.length > 0) {
1023
+ const selected = await select({
1024
+ message: `${label}${hint ? ` — ${chalk.dim(hint)}` : ""}`,
1025
+ choices: typedProp.enum.map((v) => ({
1026
+ name: String(v),
1027
+ value: String(v),
1028
+ })),
1029
+ });
1030
+ values[key] =
1031
+ typedProp.type === "number" || typedProp.type === "integer"
1032
+ ? Number(selected)
1033
+ : selected;
1034
+ }
1035
+ else {
1036
+ // Text input
1037
+ const answer = await input({
1038
+ message: `${label}${hint ? ` — ${chalk.dim(hint)}` : ""}`,
1039
+ required: isRequired,
1040
+ });
1041
+ if (answer === "" && !isRequired)
1042
+ continue;
1043
+ // Coerce types
1044
+ if (typedProp.type === "number" || typedProp.type === "integer") {
1045
+ const num = Number(answer);
1046
+ if (isNaN(num)) {
1047
+ output.error(`Invalid number for ${key}: "${answer}"`);
1048
+ process.exit(1);
1049
+ }
1050
+ values[key] = num;
1051
+ }
1052
+ else if (typedProp.type === "boolean") {
1053
+ values[key] = answer.toLowerCase() === "true" || answer === "1";
1054
+ }
1055
+ else {
1056
+ values[key] = answer;
1057
+ }
1058
+ }
1059
+ }
1060
+ if (Object.keys(values).length > 0) {
1061
+ if (method === "GET") {
1062
+ // Append as query params
1063
+ const params = new URLSearchParams();
1064
+ for (const [k, v] of Object.entries(values)) {
1065
+ params.set(k, String(v));
1066
+ }
1067
+ url = `${url}${url.includes("?") ? "&" : "?"}${params.toString()}`;
1068
+ }
1069
+ else {
1070
+ requestBody = JSON.stringify(values);
1071
+ }
1072
+ }
1073
+ }
1074
+ }
1075
+ else {
1076
+ // No schema available — fallback: prompt for method and body
1077
+ output.warn("No schema found for this endpoint. Falling back to manual input.");
1078
+ console.log();
1079
+ method = await select({
1080
+ message: "HTTP method",
1081
+ choices: [
1082
+ { name: "GET", value: "GET" },
1083
+ { name: "POST", value: "POST" },
1084
+ { name: "PUT", value: "PUT" },
1085
+ { name: "DELETE", value: "DELETE" },
1086
+ ],
1087
+ default: method,
1088
+ theme: output.bankrTheme,
1089
+ });
1090
+ if (method === "POST" || method === "PUT") {
1091
+ const bodyInput = await input({
1092
+ message: "JSON body (or leave empty)",
1093
+ theme: output.bankrTheme,
1094
+ });
1095
+ if (bodyInput.trim()) {
1096
+ try {
1097
+ JSON.parse(bodyInput);
1098
+ requestBody = bodyInput;
1099
+ }
1100
+ catch {
1101
+ output.error("Invalid JSON body");
1102
+ process.exit(1);
1103
+ }
1104
+ }
1105
+ }
1106
+ else {
1107
+ const qsInput = await input({
1108
+ message: "Query params (key=val&key2=val2, or leave empty)",
1109
+ theme: output.bankrTheme,
1110
+ });
1111
+ if (qsInput.trim()) {
1112
+ url = `${url}${url.includes("?") ? "&" : "?"}${qsInput}`;
1113
+ }
1114
+ }
1115
+ }
1116
+ // Show what we're about to send
1117
+ console.log(` ${chalk.dim("─".repeat(60))}`);
1118
+ console.log(` ${chalk.dim("Method")} ${method}`);
1119
+ console.log(` ${chalk.dim("URL")} ${url}`);
1120
+ if (requestBody) {
1121
+ console.log(` ${chalk.dim("Body")} ${requestBody}`);
1122
+ }
1123
+ console.log();
1124
+ }
1125
+ // Non-interactive: probe the endpoint to show the actual price before confirming
1126
+ if (!opts.interactive && !opts.yes && resolvedPrice === null) {
1127
+ const spin = output.spinner("Checking endpoint price...");
1128
+ const schema = await fetchSchemaForInteractive(url);
1129
+ spin.stop();
1130
+ if (schema) {
1131
+ resolvedPrice = parseFloat(schema.price);
1132
+ }
1133
+ }
1134
+ // Confirm payment unless --yes
1135
+ if (!opts.yes && !opts.interactive) {
1136
+ const priceDisplay = resolvedPrice !== null
1137
+ ? chalk.green(`$${resolvedPrice} USDC`)
1138
+ : chalk.green(`up to $${maxPaymentUsd} USDC`);
1139
+ const confirmed = await confirm({
1140
+ message: `Call ${chalk.bold(url)} with ${priceDisplay} payment?`,
1141
+ default: true,
1142
+ });
1143
+ if (!confirmed) {
1144
+ output.info("Cancelled.");
1145
+ return;
1146
+ }
1147
+ }
1148
+ else if (opts.interactive) {
1149
+ const confirmed = await confirm({
1150
+ message: `Submit with ${chalk.green(`$${resolvedPrice ?? maxPaymentUsd} USDC`)} payment?`,
1151
+ default: true,
1152
+ });
1153
+ if (!confirmed) {
1154
+ output.info("Cancelled.");
1155
+ return;
1156
+ }
1157
+ }
1158
+ const spin = output.spinner(`Calling ${method} ${url}...`);
1159
+ try {
1160
+ const res = await fetch(`${getApiUrl()}/wallet/x402-pay`, {
1161
+ method: "POST",
1162
+ headers: authHeaders(),
1163
+ body: JSON.stringify({
1164
+ url,
1165
+ method,
1166
+ body: requestBody,
1167
+ maxPaymentUsd: resolvedPrice ?? maxPaymentUsd,
1168
+ }),
1169
+ });
1170
+ const result = await handleResponse(res);
1171
+ spin.stop();
1172
+ if (opts.raw) {
1173
+ console.log(JSON.stringify(result, null, 2));
1174
+ return;
1175
+ }
1176
+ if (!result.success) {
1177
+ output.error(result.error ?? "Call failed");
1178
+ process.exit(1);
1179
+ }
1180
+ // Show endpoint schema info (Bankr-hosted endpoints only)
1181
+ if (result.endpointSchema) {
1182
+ const ep = result.endpointSchema;
1183
+ console.log();
1184
+ console.log(` ${chalk.dim("Endpoint")} ${chalk.bold(ep.name)} — ${ep.description}`);
1185
+ if (ep.schema?.input) {
1186
+ console.log(` ${chalk.dim("Input")} ${chalk.dim(JSON.stringify(ep.schema.input))}`);
1187
+ }
1188
+ if (ep.schema?.output) {
1189
+ console.log(` ${chalk.dim("Output")} ${chalk.dim(JSON.stringify(ep.schema.output))}`);
1190
+ }
1191
+ }
1192
+ // Show payment info
1193
+ if (result.paymentMade) {
1194
+ console.log();
1195
+ console.log(` ${chalk.green("Paid")} ${chalk.bold(`$${result.paymentMade.amountUsd.toFixed(4)}`)} USDC on ${result.paymentMade.network}`);
1196
+ }
1197
+ // Show response
1198
+ console.log();
1199
+ console.log(` ${chalk.dim("Status")} ${result.status}`);
1200
+ console.log(` ${chalk.dim("Response")}`);
1201
+ console.log();
1202
+ if (typeof result.response === "string") {
1203
+ console.log(` ${result.response}`);
1204
+ }
1205
+ else {
1206
+ console.log(JSON.stringify(result.response, null, 2));
1207
+ }
1208
+ console.log();
1209
+ }
1210
+ catch (err) {
1211
+ spin.fail("Call failed");
1212
+ output.error(err instanceof Error ? err.message : String(err));
1213
+ process.exit(1);
1214
+ }
1215
+ }
1216
+ // ── Search ────────────────────────────────────────────────────────────
744
1217
  export async function x402SearchCommand(queryParts, opts = {}) {
745
1218
  const query = queryParts.join(" ").trim();
746
1219
  if (!query) {
package/dist/lib/api.d.ts CHANGED
@@ -381,5 +381,24 @@ export interface TransferResponse {
381
381
  error?: string;
382
382
  }
383
383
  export declare function transfer(request: TransferRequest): Promise<TransferResponse>;
384
+ export interface X402PayRequest {
385
+ url: string;
386
+ method?: string;
387
+ body?: string;
388
+ headers?: Record<string, string>;
389
+ maxPaymentUsd?: number;
390
+ }
391
+ export interface X402PayResponse {
392
+ success: boolean;
393
+ status?: number;
394
+ response?: unknown;
395
+ paymentMade?: {
396
+ amountUsd: number;
397
+ network: string;
398
+ payTo: string;
399
+ };
400
+ error?: string;
401
+ }
402
+ export declare function x402Pay(request: X402PayRequest): Promise<X402PayResponse>;
384
403
  export declare function buildPublicClaimTxs(beneficiaryAddress: string, tokenAddresses: string[]): Promise<BuildClaimResponse>;
385
404
  //# sourceMappingURL=api.d.ts.map
package/dist/lib/api.js CHANGED
@@ -280,6 +280,14 @@ export async function transfer(request) {
280
280
  });
281
281
  return handleResponse(res);
282
282
  }
283
+ export async function x402Pay(request) {
284
+ const res = await fetch(`${getApiUrl()}/wallet/x402-pay`, {
285
+ method: "POST",
286
+ headers: authHeaders(),
287
+ body: JSON.stringify(request),
288
+ });
289
+ return handleResponse(res);
290
+ }
283
291
  export async function buildPublicClaimTxs(beneficiaryAddress, tokenAddresses) {
284
292
  const res = await fetch(`${getApiUrl()}/public/doppler/build-claim`, {
285
293
  method: "POST",
@@ -61,6 +61,13 @@ export function spinner(text) {
61
61
  export function maskApiKey(key) {
62
62
  if (key.length <= 8)
63
63
  return "****";
64
+ // New prefixed keys: bk_usr_{keyId}_{secret} → bk_usr_{keyId}_*****
65
+ const parts = key.split("_");
66
+ if (parts.length === 4 &&
67
+ parts[0] === "bk" &&
68
+ (parts[1] === "usr" || parts[1] === "ptr")) {
69
+ return `${parts[0]}_${parts[1]}_${parts[2]}_*****`;
70
+ }
64
71
  return key.slice(0, 6) + "..." + key.slice(-4);
65
72
  }
66
73
  export function formatDuration(ms) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bankr/cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Official CLI for the Bankr AI agent platform",
5
5
  "type": "module",
6
6
  "bin": {