@paybond/kit 0.2.1 → 0.4.0

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 CHANGED
@@ -2,7 +2,7 @@
2
2
  * Paybond Kit — TypeScript Harbor client with tenant binding, retries, optional upstream JWT,
3
3
  * and gateway service-account token exchange (`POST /v1/auth/harbor-access`).
4
4
  */
5
- import { buildSignedCreateIntentBody } from "./principal-intent.js";
5
+ import { buildSignedCreateIntentBody, } from "./principal-intent.js";
6
6
  import { signPayeeEvidenceBinding } from "./payee-evidence.js";
7
7
  /**
8
8
  * Structured HTTP failure from Harbor with operator-facing diagnostics.
@@ -47,6 +47,60 @@ export class SignalHttpError extends Error {
47
47
  this.bodyText = init.bodyText;
48
48
  }
49
49
  }
50
+ const agentRecognitionProofHeader = "x-paybond-agent-recognition-proof";
51
+ export class A2AHttpError extends Error {
52
+ statusCode;
53
+ url;
54
+ bodyText;
55
+ constructor(message, init) {
56
+ super(message);
57
+ this.name = "A2AHttpError";
58
+ this.statusCode = init.statusCode;
59
+ this.url = init.url;
60
+ this.bodyText = init.bodyText;
61
+ }
62
+ }
63
+ export class ProtocolHttpError extends Error {
64
+ statusCode;
65
+ url;
66
+ bodyText;
67
+ errorCode;
68
+ errorMessage;
69
+ constructor(message, init) {
70
+ super(message);
71
+ this.name = "ProtocolHttpError";
72
+ this.statusCode = init.statusCode;
73
+ this.url = init.url;
74
+ this.bodyText = init.bodyText;
75
+ const parsed = parseGatewayErrorEnvelope(init.bodyText);
76
+ this.errorCode = init.errorCode ?? parsed.errorCode;
77
+ this.errorMessage = init.errorMessage ?? parsed.errorMessage;
78
+ }
79
+ }
80
+ function parseGatewayErrorEnvelope(text) {
81
+ if (!text.trim().startsWith("{")) {
82
+ return {};
83
+ }
84
+ try {
85
+ const body = JSON.parse(text);
86
+ if (body === null || Array.isArray(body) || typeof body !== "object") {
87
+ return {};
88
+ }
89
+ const errorCode = typeof body.error === "string" && body.error.trim() ? body.error.trim() : undefined;
90
+ const errorMessage = typeof body.message === "string" && body.message.trim() ? body.message.trim() : undefined;
91
+ return { errorCode, errorMessage };
92
+ }
93
+ catch {
94
+ return {};
95
+ }
96
+ }
97
+ function protocolHTTPErrorMessage(prefix, statusCode, bodyText) {
98
+ const parsed = parseGatewayErrorEnvelope(bodyText);
99
+ if (parsed.errorCode) {
100
+ return `${prefix} HTTP ${statusCode} (${parsed.errorCode}): ${parsed.errorMessage ?? bodyText}`;
101
+ }
102
+ return `${prefix} HTTP ${statusCode}: ${bodyText}`;
103
+ }
50
104
  function normalizeBase(url) {
51
105
  return url.trim().replace(/\/+$/, "");
52
106
  }
@@ -292,7 +346,7 @@ export class HarborClient {
292
346
  }
293
347
  throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
294
348
  }
295
- async fetchWithRetries(url, init, { retryBody }) {
349
+ async fetchWithRetries(url, init, { retryBody, retryBodyText, }) {
296
350
  let lastErr;
297
351
  for (let attempt = 0; attempt < this.maxRetries; attempt++) {
298
352
  let res;
@@ -318,10 +372,18 @@ export class HarborClient {
318
372
  const raSec = parseRetryAfterSeconds(res.headers.get("retry-after"));
319
373
  const delayMs = raSec != null ? raSec * 1000 : backoffMs(attempt);
320
374
  await new Promise((r) => setTimeout(r, delayMs));
321
- init = {
322
- ...init,
323
- body: JSON.stringify(retryBody),
324
- };
375
+ if (retryBodyText !== undefined) {
376
+ init = {
377
+ ...init,
378
+ body: retryBodyText,
379
+ };
380
+ }
381
+ else if (retryBody !== undefined) {
382
+ init = {
383
+ ...init,
384
+ body: JSON.stringify(retryBody),
385
+ };
386
+ }
325
387
  continue;
326
388
  }
327
389
  return res;
@@ -403,6 +465,79 @@ export class HarborClient {
403
465
  }
404
466
  return JSON.parse(text);
405
467
  }
468
+ /**
469
+ * POST `/intents/{intentId}/fund` for x402 / USDC-on-Base funding.
470
+ *
471
+ * Harbor returns:
472
+ * - `402` with `paymentRequired` details when a facilitator or wallet must sign
473
+ * - `202` while authorization is pending
474
+ * - `200` once the intent is funded and any capability token is available
475
+ *
476
+ * @throws HarborHttpError for non-funding HTTP errors
477
+ * @throws Error when Harbor echoes a different tenant or intent than requested
478
+ */
479
+ async fundIntent(intentId, options) {
480
+ const url = `${this.base}intents/${intentId}/fund`;
481
+ const headers = {
482
+ "x-tenant-id": this.tenantId,
483
+ };
484
+ if (options?.idempotencyKey?.trim()) {
485
+ headers["idempotency-key"] = options.idempotencyKey.trim();
486
+ }
487
+ if (options?.paymentSignature?.trim()) {
488
+ headers["payment-signature"] = options.paymentSignature.trim();
489
+ }
490
+ const res = await this.fetchWithRetries(url, {
491
+ method: "POST",
492
+ headers,
493
+ body: "",
494
+ }, { retryBodyText: "" });
495
+ const text = await res.text();
496
+ if (![200, 202, 402].includes(res.status)) {
497
+ throw new HarborHttpError(`Harbor fund intent HTTP ${res.status}: ${text}`, {
498
+ statusCode: res.status,
499
+ url,
500
+ bodyText: text,
501
+ });
502
+ }
503
+ const body = assertJSONObject(JSON.parse(text));
504
+ const tenant = String(body.tenant ?? "");
505
+ if (tenant !== this.tenantId) {
506
+ throw new Error(`fund tenant mismatch: client=${this.tenantId} harbor=${tenant}`);
507
+ }
508
+ const echoedIntentId = String(body.intent_id ?? "");
509
+ if (echoedIntentId !== intentId) {
510
+ throw new Error(`fund intent mismatch: requested=${intentId} harbor=${echoedIntentId}`);
511
+ }
512
+ if (typeof body.state !== "string" || !body.state.trim()) {
513
+ throw new Error("fund response missing state");
514
+ }
515
+ if (typeof body.currency !== "string" || !body.currency.trim()) {
516
+ throw new Error("fund response missing currency");
517
+ }
518
+ const amountCents = Number(body.amount_cents);
519
+ if (!Number.isFinite(amountCents)) {
520
+ throw new Error("fund response missing amount_cents");
521
+ }
522
+ return {
523
+ statusCode: res.status,
524
+ paymentRequired: res.headers.get("payment-required") ?? undefined,
525
+ paymentResponse: res.headers.get("payment-response") ?? undefined,
526
+ intentId: echoedIntentId,
527
+ tenant,
528
+ state: body.state,
529
+ settlementRail: readSettlementRailValue(body.settlement_rail, "fund settlement_rail"),
530
+ currency: body.currency,
531
+ amountCents,
532
+ funded: Boolean(body.funded),
533
+ capabilityToken: typeof body.capability_token === "string" && body.capability_token.trim()
534
+ ? body.capability_token
535
+ : undefined,
536
+ funding: body.funding === undefined || body.funding === null
537
+ ? undefined
538
+ : parseIntentFundingResult(body.funding),
539
+ };
540
+ }
406
541
  /**
407
542
  * POST `/intents/{intentId}/evidence` with a signed evidence JSON body.
408
543
  *
@@ -523,12 +658,71 @@ export class HarborClient {
523
658
  }
524
659
  }
525
660
  const DEFAULT_PRINCIPAL_PATH = "/v1/auth/principal";
661
+ const SETTLEMENT_RAIL_VALUES = new Set(["stripe_connect", "x402_usdc_base"]);
526
662
  function assertJSONObject(value) {
527
663
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
528
664
  throw new Error("expected JSON object");
529
665
  }
530
666
  return value;
531
667
  }
668
+ function readSettlementRailValue(value, field) {
669
+ if (typeof value !== "string" || !SETTLEMENT_RAIL_VALUES.has(value)) {
670
+ throw new Error(`invalid ${field}`);
671
+ }
672
+ return value;
673
+ }
674
+ function readStringArrayValue(value, field) {
675
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
676
+ throw new Error(`invalid ${field}`);
677
+ }
678
+ return [...value];
679
+ }
680
+ function parseIntentFundingResult(value) {
681
+ const body = assertJSONObject(value);
682
+ const onchainRaw = body.onchain_transaction_hashes;
683
+ const onchain = onchainRaw === undefined || onchainRaw === null
684
+ ? undefined
685
+ : (() => {
686
+ const parsed = assertJSONObject(onchainRaw);
687
+ return {
688
+ authorizations: parsed.authorizations === undefined
689
+ ? undefined
690
+ : readStringArrayValue(parsed.authorizations, "funding.onchain_transaction_hashes.authorizations"),
691
+ captures: parsed.captures === undefined
692
+ ? undefined
693
+ : readStringArrayValue(parsed.captures, "funding.onchain_transaction_hashes.captures"),
694
+ voids: parsed.voids === undefined
695
+ ? undefined
696
+ : readStringArrayValue(parsed.voids, "funding.onchain_transaction_hashes.voids"),
697
+ refunds: parsed.refunds === undefined
698
+ ? undefined
699
+ : readStringArrayValue(parsed.refunds, "funding.onchain_transaction_hashes.refunds"),
700
+ };
701
+ })();
702
+ const readOptionalString = (field) => {
703
+ const raw = body[field];
704
+ return typeof raw === "string" && raw.trim() ? raw : undefined;
705
+ };
706
+ return {
707
+ settlementRail: readSettlementRailValue(body.settlement_rail, "funding.settlement_rail"),
708
+ harborFundEndpoint: readOptionalString("harbor_fund_endpoint"),
709
+ status: readOptionalString("status"),
710
+ paymentSessionId: readOptionalString("payment_session_id"),
711
+ paymentUrl: readOptionalString("payment_url"),
712
+ asset: readOptionalString("asset"),
713
+ network: readOptionalString("network"),
714
+ authorizationId: readOptionalString("authorization_id"),
715
+ captureId: readOptionalString("capture_id"),
716
+ voidId: readOptionalString("void_id"),
717
+ refundId: readOptionalString("refund_id"),
718
+ sourceAddress: readOptionalString("source_address"),
719
+ targetAddress: readOptionalString("target_address"),
720
+ authorizationExpiresAt: readOptionalString("authorization_expires_at"),
721
+ captureExpiresAt: readOptionalString("capture_expires_at"),
722
+ refundExpiresAt: readOptionalString("refund_expires_at"),
723
+ onchainTransactionHashes: onchain,
724
+ };
725
+ }
532
726
  /**
533
727
  * Tenant-bound reader for gateway Signal routes.
534
728
  */
@@ -630,6 +824,21 @@ export class GatewaySignalClient {
630
824
  this.assertTenant(body, url);
631
825
  return body;
632
826
  }
827
+ async getSignedPortfolioArtifact(scoreVersion) {
828
+ const url = `${this.base}signal/v1/portfolio/signed-export${this.scoreQuery(scoreVersion)}`;
829
+ const res = await this.fetchGetWithRetries(url);
830
+ const text = await res.text();
831
+ if (!res.ok) {
832
+ throw new SignalHttpError(`Signal signed portfolio artifact HTTP ${res.status}: ${text}`, {
833
+ statusCode: res.status,
834
+ url,
835
+ bodyText: text,
836
+ });
837
+ }
838
+ const body = assertJSONObject(JSON.parse(text));
839
+ this.assertTenant(body, url);
840
+ return body;
841
+ }
633
842
  async getOperatorExplanation(operatorDid, scoreVersion) {
634
843
  const enc = encodeURIComponent(operatorDid);
635
844
  const url = `${this.base}signal/v1/operators/${enc}/explanation${this.scoreQuery(scoreVersion)}`;
@@ -675,6 +884,273 @@ export class GatewaySignalClient {
675
884
  return body;
676
885
  }
677
886
  }
887
+ /**
888
+ * Public or optionally authenticated reader for the gateway's A2A discovery surface.
889
+ */
890
+ export class GatewayA2AClient {
891
+ base;
892
+ bearerToken;
893
+ maxRetries;
894
+ constructor(gatewayBaseUrl, options) {
895
+ this.base = normalizeBase(gatewayBaseUrl) + "/";
896
+ this.bearerToken = options?.staticGatewayBearerToken?.trim() || undefined;
897
+ this.maxRetries = Math.max(1, options?.maxRetries ?? 3);
898
+ }
899
+ async fetchGetWithRetries(url) {
900
+ let lastErr;
901
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
902
+ let res;
903
+ try {
904
+ const headers = new Headers({
905
+ accept: "application/json",
906
+ });
907
+ if (this.bearerToken) {
908
+ headers.set("authorization", `Bearer ${this.bearerToken}`);
909
+ }
910
+ res = await fetch(url, {
911
+ method: "GET",
912
+ headers,
913
+ });
914
+ }
915
+ catch (e) {
916
+ lastErr = e;
917
+ if (attempt + 1 >= this.maxRetries)
918
+ throw e;
919
+ await new Promise((r) => setTimeout(r, backoffMs(attempt)));
920
+ continue;
921
+ }
922
+ if ([429, 500, 502, 503, 504].includes(res.status)) {
923
+ if (attempt + 1 >= this.maxRetries) {
924
+ return res;
925
+ }
926
+ const raSec = parseRetryAfterSeconds(res.headers.get("retry-after"));
927
+ const delayMs = raSec != null ? raSec * 1000 : backoffMs(attempt);
928
+ await new Promise((r) => setTimeout(r, delayMs));
929
+ continue;
930
+ }
931
+ return res;
932
+ }
933
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
934
+ }
935
+ async getAgentCard() {
936
+ const url = `${this.base}.well-known/agent-card.json`;
937
+ const res = await this.fetchGetWithRetries(url);
938
+ const text = await res.text();
939
+ if (!res.ok) {
940
+ throw new A2AHttpError(`A2A agent card HTTP ${res.status}: ${text}`, {
941
+ statusCode: res.status,
942
+ url,
943
+ bodyText: text,
944
+ });
945
+ }
946
+ return assertJSONObject(JSON.parse(text));
947
+ }
948
+ async getTaskContracts() {
949
+ const url = `${this.base}protocol/v2/a2a/task-contracts`;
950
+ const res = await this.fetchGetWithRetries(url);
951
+ const text = await res.text();
952
+ if (!res.ok) {
953
+ throw new A2AHttpError(`A2A task contracts HTTP ${res.status}: ${text}`, {
954
+ statusCode: res.status,
955
+ url,
956
+ bodyText: text,
957
+ });
958
+ }
959
+ return assertJSONObject(JSON.parse(text));
960
+ }
961
+ async getTaskContract(contractId) {
962
+ const enc = encodeURIComponent(contractId);
963
+ const url = `${this.base}protocol/v2/a2a/task-contracts/${enc}`;
964
+ const res = await this.fetchGetWithRetries(url);
965
+ const text = await res.text();
966
+ if (!res.ok) {
967
+ throw new A2AHttpError(`A2A task contract HTTP ${res.status}: ${text}`, {
968
+ statusCode: res.status,
969
+ url,
970
+ bodyText: text,
971
+ });
972
+ }
973
+ return assertJSONObject(JSON.parse(text));
974
+ }
975
+ }
976
+ export class GatewayProtocolClient {
977
+ tenantId;
978
+ base;
979
+ staticGatewayBearerToken;
980
+ maxRetries;
981
+ constructor(gatewayBaseUrl, tenantId, init) {
982
+ this.base = `${normalizeBase(gatewayBaseUrl)}/`;
983
+ this.tenantId = tenantId.trim();
984
+ this.staticGatewayBearerToken = init?.staticGatewayBearerToken?.trim() || null;
985
+ this.maxRetries = Math.max(1, init?.maxRetries ?? 3);
986
+ }
987
+ async fetchWithRetries(url, init) {
988
+ let lastErr;
989
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
990
+ let res;
991
+ try {
992
+ res = await fetch(url, init);
993
+ }
994
+ catch (e) {
995
+ lastErr = e;
996
+ if (attempt + 1 >= this.maxRetries) {
997
+ throw e;
998
+ }
999
+ await new Promise((r) => setTimeout(r, backoffMs(attempt)));
1000
+ continue;
1001
+ }
1002
+ if ([429, 500, 502, 503, 504].includes(res.status) && attempt + 1 < this.maxRetries) {
1003
+ const raSec = parseRetryAfterSeconds(res.headers.get("retry-after"));
1004
+ const delayMs = raSec != null ? raSec * 1000 : backoffMs(attempt);
1005
+ await new Promise((r) => setTimeout(r, delayMs));
1006
+ continue;
1007
+ }
1008
+ return res;
1009
+ }
1010
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
1011
+ }
1012
+ headers(extra) {
1013
+ const headers = new Headers(extra);
1014
+ headers.set("accept", "application/json");
1015
+ headers.set("x-tenant-id", this.tenantId);
1016
+ if (this.staticGatewayBearerToken) {
1017
+ headers.set("authorization", `Bearer ${this.staticGatewayBearerToken}`);
1018
+ }
1019
+ return headers;
1020
+ }
1021
+ async postJSON(path, payload, extraHeaders) {
1022
+ const url = `${this.base}${path.replace(/^\/+/, "")}`;
1023
+ const res = await this.fetchWithRetries(url, {
1024
+ method: "POST",
1025
+ headers: this.headers({
1026
+ "content-type": "application/json",
1027
+ ...(extraHeaders ?? {}),
1028
+ }),
1029
+ body: JSON.stringify(payload),
1030
+ });
1031
+ const text = await res.text();
1032
+ if (!res.ok) {
1033
+ const parsed = parseGatewayErrorEnvelope(text);
1034
+ throw new ProtocolHttpError(protocolHTTPErrorMessage(`gateway POST ${path}`, res.status, text), {
1035
+ statusCode: res.status,
1036
+ url,
1037
+ bodyText: text,
1038
+ errorCode: parsed.errorCode,
1039
+ errorMessage: parsed.errorMessage,
1040
+ });
1041
+ }
1042
+ return assertJSONObject(JSON.parse(text));
1043
+ }
1044
+ async importAgentMandateV1(init) {
1045
+ const url = `${this.base}protocol/v2/mandates`;
1046
+ const res = await this.fetchWithRetries(url, {
1047
+ method: "POST",
1048
+ headers: this.headers({ "content-type": "application/json" }),
1049
+ body: JSON.stringify({
1050
+ signed_mandate: init.signedMandate,
1051
+ intent_id: init.intentId,
1052
+ transport_binding: init.transportBinding ?? {},
1053
+ recognition_proof: init.recognitionProof,
1054
+ }),
1055
+ });
1056
+ const text = await res.text();
1057
+ if (!res.ok) {
1058
+ const parsed = parseGatewayErrorEnvelope(text);
1059
+ throw new ProtocolHttpError(protocolHTTPErrorMessage("protocol mandate import", res.status, text), {
1060
+ statusCode: res.status,
1061
+ url,
1062
+ bodyText: text,
1063
+ errorCode: parsed.errorCode,
1064
+ errorMessage: parsed.errorMessage,
1065
+ });
1066
+ }
1067
+ const body = assertJSONObject(JSON.parse(text));
1068
+ if (String(body.intent_id ?? "").trim() !== init.intentId) {
1069
+ throw new Error(`protocol intent mismatch: requested=${init.intentId} gateway=${String(body.intent_id ?? "")}`);
1070
+ }
1071
+ if (String(body.mandate?.authorization?.tenant_id ?? "").trim() !== this.tenantId) {
1072
+ throw new Error(`protocol mandate tenant mismatch: client=${this.tenantId} gateway=${String(body.mandate?.authorization?.tenant_id ?? "")}`);
1073
+ }
1074
+ return body;
1075
+ }
1076
+ async getSettlementReceiptV1(receiptId) {
1077
+ const enc = encodeURIComponent(receiptId);
1078
+ const url = `${this.base}protocol/v2/receipts/${enc}`;
1079
+ const res = await this.fetchWithRetries(url, {
1080
+ method: "GET",
1081
+ headers: this.headers(),
1082
+ });
1083
+ const text = await res.text();
1084
+ if (!res.ok) {
1085
+ const parsed = parseGatewayErrorEnvelope(text);
1086
+ throw new ProtocolHttpError(protocolHTTPErrorMessage("protocol settlement receipt", res.status, text), {
1087
+ statusCode: res.status,
1088
+ url,
1089
+ bodyText: text,
1090
+ errorCode: parsed.errorCode,
1091
+ errorMessage: parsed.errorMessage,
1092
+ });
1093
+ }
1094
+ const body = assertJSONObject(JSON.parse(text));
1095
+ if (String(body.receipt_id ?? "").trim() !== receiptId) {
1096
+ throw new Error(`protocol receipt mismatch: requested=${receiptId} gateway=${String(body.receipt_id ?? "")}`);
1097
+ }
1098
+ if (String(body.tenant_id ?? "").trim() !== this.tenantId) {
1099
+ throw new Error(`protocol receipt tenant mismatch: client=${this.tenantId} gateway=${String(body.tenant_id ?? "")}`);
1100
+ }
1101
+ return body;
1102
+ }
1103
+ async verifyProtocolReceiptV1(receipt) {
1104
+ const url = `${this.base}protocol/v2/receipts/verify`;
1105
+ const res = await this.fetchWithRetries(url, {
1106
+ method: "POST",
1107
+ headers: this.headers({ "content-type": "application/json" }),
1108
+ body: JSON.stringify(receipt),
1109
+ });
1110
+ const text = await res.text();
1111
+ if (!res.ok) {
1112
+ const parsed = parseGatewayErrorEnvelope(text);
1113
+ throw new ProtocolHttpError(protocolHTTPErrorMessage("protocol receipt verify", res.status, text), {
1114
+ statusCode: res.status,
1115
+ url,
1116
+ bodyText: text,
1117
+ errorCode: parsed.errorCode,
1118
+ errorMessage: parsed.errorMessage,
1119
+ });
1120
+ }
1121
+ return assertJSONObject(JSON.parse(text));
1122
+ }
1123
+ async createHarborIntent(init) {
1124
+ return this.postJSON("/harbor/intents", init.body, gatewayMutationHeaders(init.recognitionProof, {
1125
+ ...(init.idempotencyKey?.trim() ? { "idempotency-key": init.idempotencyKey.trim() } : {}),
1126
+ }));
1127
+ }
1128
+ async fundHarborIntent(init) {
1129
+ return this.postJSON(`/harbor/intents/${encodeURIComponent(init.intentId)}/fund`, {}, gatewayMutationHeaders(init.recognitionProof, {
1130
+ ...(init.paymentSignature?.trim() ? { "payment-signature": init.paymentSignature.trim() } : {}),
1131
+ ...(init.idempotencyKey?.trim() ? { "idempotency-key": init.idempotencyKey.trim() } : {}),
1132
+ }));
1133
+ }
1134
+ async submitHarborEvidence(init) {
1135
+ return this.postJSON(`/harbor/intents/${encodeURIComponent(init.intentId)}/evidence`, init.body, gatewayMutationHeaders(init.recognitionProof, {
1136
+ ...(init.idempotencyKey?.trim() ? { "idempotency-key": init.idempotencyKey.trim() } : {}),
1137
+ }));
1138
+ }
1139
+ async confirmHarborSettlement(init) {
1140
+ return this.postJSON(`/harbor/intents/${encodeURIComponent(init.intentId)}/settlement/confirm`, init.body, gatewayMutationHeaders(init.recognitionProof, {
1141
+ ...(init.idempotencyKey?.trim() ? { "idempotency-key": init.idempotencyKey.trim() } : {}),
1142
+ }));
1143
+ }
1144
+ }
1145
+ function gatewayMutationHeaders(recognitionProof, headers) {
1146
+ return {
1147
+ ...(headers ?? {}),
1148
+ [agentRecognitionProofHeader]: encodeRecognitionProofHeader(recognitionProof),
1149
+ };
1150
+ }
1151
+ function encodeRecognitionProofHeader(proof) {
1152
+ return Buffer.from(JSON.stringify(proof), "utf8").toString("base64url");
1153
+ }
678
1154
  async function resolveGatewayTenantId(gatewayBaseUrl, apiKey, principalPath, maxRetries) {
679
1155
  const base = normalizeBase(gatewayBaseUrl);
680
1156
  const path = principalPath.startsWith("/") ? principalPath : `/${principalPath}`;
@@ -743,7 +1219,7 @@ export class ServiceAccountSignalSession {
743
1219
  }
744
1220
  }
745
1221
  /**
746
- * Ergonomic intent helpers: principal-signed intent create and payee-signed evidence.
1222
+ * Ergonomic intent helpers: principal-signed intent create, x402 funding, and payee-signed evidence.
747
1223
  */
748
1224
  export class PaybondIntents {
749
1225
  harbor;
@@ -751,7 +1227,8 @@ export class PaybondIntents {
751
1227
  this.harbor = harbor;
752
1228
  }
753
1229
  /**
754
- * Build a principal-signed `POST /intents` body and submit it. `principalSigningSeed` must be 32 bytes.
1230
+ * Build a principal-signed `POST /intents` body and submit it. `principalSigningSeed` must be
1231
+ * 32 bytes. `settlementRail` only requests an allowed rail; destinations stay server-owned.
755
1232
  */
756
1233
  async create(params) {
757
1234
  const { idempotencyKey, intentId: maybeIntentId, ...fields } = params;
@@ -763,6 +1240,15 @@ export class PaybondIntents {
763
1240
  });
764
1241
  return this.harbor.createIntent(body, { idempotencyKey });
765
1242
  }
1243
+ /**
1244
+ * Advance Harbor `/intents/{id}/fund` for x402 / USDC-on-Base intents.
1245
+ */
1246
+ async fund(params) {
1247
+ return this.harbor.fundIntent(params.intentId, {
1248
+ paymentSignature: params.paymentSignature,
1249
+ idempotencyKey: params.idempotencyKey,
1250
+ });
1251
+ }
766
1252
  /**
767
1253
  * Sign payee evidence and POST it. `payeeSigningSeed` must be 32 bytes.
768
1254
  */
@@ -781,12 +1267,16 @@ export class PaybondIntents {
781
1267
  export class Paybond {
782
1268
  harbor;
783
1269
  signal;
1270
+ a2a;
1271
+ protocol;
784
1272
  intents;
785
1273
  session;
786
- constructor(session, signal) {
1274
+ constructor(session, signal, a2a, protocol) {
787
1275
  this.session = session;
788
1276
  this.harbor = session.harbor;
789
1277
  this.signal = signal;
1278
+ this.a2a = a2a;
1279
+ this.protocol = protocol;
790
1280
  this.intents = new PaybondIntents(session.harbor);
791
1281
  }
792
1282
  /** Open a tenant-bound session via gateway `harbor-access` exchange. */
@@ -796,7 +1286,15 @@ export class Paybond {
796
1286
  staticGatewayBearerToken: init.apiKey,
797
1287
  maxRetries: init.maxRetries ?? 3,
798
1288
  });
799
- return new Paybond(session, signal);
1289
+ const a2a = new GatewayA2AClient(init.gatewayBaseUrl, {
1290
+ staticGatewayBearerToken: init.apiKey,
1291
+ maxRetries: init.maxRetries ?? 3,
1292
+ });
1293
+ const protocol = new GatewayProtocolClient(init.gatewayBaseUrl, session.harbor.tenantId, {
1294
+ staticGatewayBearerToken: init.apiKey,
1295
+ maxRetries: init.maxRetries ?? 3,
1296
+ });
1297
+ return new Paybond(session, signal, a2a, protocol);
800
1298
  }
801
1299
  async rotateHarborToken() {
802
1300
  await this.session.rotateHarborToken();
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ type JSONRPCID = string | number | null;
3
+ type JSONRPCRequest = {
4
+ jsonrpc?: unknown;
5
+ id?: JSONRPCID;
6
+ method?: unknown;
7
+ params?: unknown;
8
+ };
9
+ type JSONRPCResponse = {
10
+ jsonrpc: "2.0";
11
+ id: JSONRPCID;
12
+ result?: unknown;
13
+ error?: {
14
+ code: number;
15
+ message: string;
16
+ };
17
+ };
18
+ type MCPCallToolResult = {
19
+ content: Array<{
20
+ type: "text";
21
+ text: string;
22
+ }>;
23
+ structuredContent?: Record<string, unknown>;
24
+ isError?: boolean;
25
+ };
26
+ export type PaybondMCPSettings = {
27
+ gatewayBaseUrl: string;
28
+ apiKey: string;
29
+ harborBaseUrl?: string;
30
+ harborAccessPath?: string;
31
+ principalPath?: string;
32
+ maxRetries?: number;
33
+ clockSkewSeconds?: number;
34
+ };
35
+ export declare class PaybondMCPServer {
36
+ private readonly runtime;
37
+ private readonly tools;
38
+ private initialized;
39
+ constructor(settings: PaybondMCPSettings);
40
+ listTools(): Array<Record<string, unknown>>;
41
+ callTool(name: string, args?: Record<string, unknown>): Promise<MCPCallToolResult>;
42
+ handleMessage(message: JSONRPCRequest): Promise<JSONRPCResponse | null>;
43
+ runStdio(): void;
44
+ private handleLine;
45
+ private writeResponse;
46
+ private buildTools;
47
+ }
48
+ export declare function settingsFromEnv(env?: Record<string, string | undefined>): PaybondMCPSettings;
49
+ export declare function main(argv?: string[]): number;
50
+ export {};