@naulon/wayfarer 0.1.1

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 (86) hide show
  1. package/README.md +39 -0
  2. package/dist/agent.d.ts +77 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +235 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/allocation.d.ts +36 -0
  7. package/dist/allocation.d.ts.map +1 -0
  8. package/dist/allocation.js +45 -0
  9. package/dist/allocation.js.map +1 -0
  10. package/dist/appraise.d.ts +3 -0
  11. package/dist/appraise.d.ts.map +1 -0
  12. package/dist/appraise.js +60 -0
  13. package/dist/appraise.js.map +1 -0
  14. package/dist/buyer.d.ts +195 -0
  15. package/dist/buyer.d.ts.map +1 -0
  16. package/dist/buyer.js +254 -0
  17. package/dist/buyer.js.map +1 -0
  18. package/dist/decide.d.ts +115 -0
  19. package/dist/decide.d.ts.map +1 -0
  20. package/dist/decide.js +206 -0
  21. package/dist/decide.js.map +1 -0
  22. package/dist/discover.d.ts +10 -0
  23. package/dist/discover.d.ts.map +1 -0
  24. package/dist/discover.js +5 -0
  25. package/dist/discover.js.map +1 -0
  26. package/dist/discovery.d.ts +26 -0
  27. package/dist/discovery.d.ts.map +1 -0
  28. package/dist/discovery.js +93 -0
  29. package/dist/discovery.js.map +1 -0
  30. package/dist/gateway.d.ts +117 -0
  31. package/dist/gateway.d.ts.map +1 -0
  32. package/dist/gateway.js +187 -0
  33. package/dist/gateway.js.map +1 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +19 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/lib.d.ts +40 -0
  39. package/dist/lib.d.ts.map +1 -0
  40. package/dist/lib.js +44 -0
  41. package/dist/lib.js.map +1 -0
  42. package/dist/licenseStore.d.ts +56 -0
  43. package/dist/licenseStore.d.ts.map +1 -0
  44. package/dist/licenseStore.js +79 -0
  45. package/dist/licenseStore.js.map +1 -0
  46. package/dist/memo.d.ts +43 -0
  47. package/dist/memo.d.ts.map +1 -0
  48. package/dist/memo.js +102 -0
  49. package/dist/memo.js.map +1 -0
  50. package/dist/origin-policy.d.ts +74 -0
  51. package/dist/origin-policy.d.ts.map +1 -0
  52. package/dist/origin-policy.js +88 -0
  53. package/dist/origin-policy.js.map +1 -0
  54. package/dist/paidFetch.d.ts +14 -0
  55. package/dist/paidFetch.d.ts.map +1 -0
  56. package/dist/paidFetch.js +93 -0
  57. package/dist/paidFetch.js.map +1 -0
  58. package/dist/pay.d.ts +3 -0
  59. package/dist/pay.d.ts.map +1 -0
  60. package/dist/pay.js +82 -0
  61. package/dist/pay.js.map +1 -0
  62. package/dist/pop.d.ts +8 -0
  63. package/dist/pop.d.ts.map +1 -0
  64. package/dist/pop.js +25 -0
  65. package/dist/pop.js.map +1 -0
  66. package/dist/rail.d.ts +18 -0
  67. package/dist/rail.d.ts.map +1 -0
  68. package/dist/rail.js +73 -0
  69. package/dist/rail.js.map +1 -0
  70. package/dist/rss.d.ts +38 -0
  71. package/dist/rss.d.ts.map +1 -0
  72. package/dist/rss.js +110 -0
  73. package/dist/rss.js.map +1 -0
  74. package/dist/sign.d.ts +13 -0
  75. package/dist/sign.d.ts.map +1 -0
  76. package/dist/sign.js +52 -0
  77. package/dist/sign.js.map +1 -0
  78. package/dist/types.d.ts +75 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +2 -0
  81. package/dist/types.js.map +1 -0
  82. package/dist/wallet.d.ts +12 -0
  83. package/dist/wallet.d.ts.map +1 -0
  84. package/dist/wallet.js +46 -0
  85. package/dist/wallet.js.map +1 -0
  86. package/package.json +42 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Gateway buyer — the memo-LESS Circle rail (Base + every other Gateway chain). Where the
3
+ * memo rail (memo.ts) relays a raw USDC EIP-3009 authorization, the gateway rail signs an
4
+ * EIP-3009 authorization against the Circle **GatewayWallet** contract (the `extra.
5
+ * verifyingContract` the gate advertises) and posts the full x402 envelope `{x402Version,
6
+ * payload:{authorization,signature}, resource, accepted}` as `payment-signature` — the shape
7
+ * Circle's facilitator `verify` requires (a bare/mock shape is rejected 400
8
+ * `x402Version/resource/accepted/payload: Required` — the Base-settle bug this path fixes).
9
+ *
10
+ * Custody-free seam (mirrors `memoBuyer(MemoSigner)`): a cloud host injects a sign-only
11
+ * `GatewaySigner` (its address + a `signTypedData` that signs the GatewayWallet-domain typed
12
+ * data elsewhere — a grant-checked BFF holding the encrypted session key), so the private key
13
+ * never lives in this process. A viem `PrivateKeyAccount` satisfies the same shape, keeping
14
+ * the CLI/self-host path (env `BUYER_PRIVATE_KEY`) symmetric. The signing wraps the SDK's
15
+ * `BatchEvmScheme` so the signed shape / validity clamp can never drift from the rail (the
16
+ * same reason the gate's own `gatewayLegPayload` does — see tollgate/src/x402.ts).
17
+ *
18
+ * The Gateway balance is funded out-of-band (a one-time deposit into Circle's non-custodial
19
+ * Gateway Wallet); the pay path here is pure sign-only. On the env/CLI path `init()` still
20
+ * deposits via the SDK `GatewayClient` for backwards compatibility.
21
+ */
22
+ import {} from "viem";
23
+ import { activeNetwork, getConfig } from "@naulon/shared";
24
+ import { classifyPaymentError, classifySignerRefusal, probe, } from "./buyer.js";
25
+ import { runPaidFetch } from "./paidFetch.js";
26
+ function envAccountKey() {
27
+ const cfg = getConfig();
28
+ if (!cfg.BUYER_PRIVATE_KEY) {
29
+ throw new Error(`PAYMENT_MODE=gateway on ${activeNetwork().chainName} requires an injected signer or a funded ` +
30
+ `BUYER_PRIVATE_KEY (a wallet whose USDC is deposited in the Circle Gateway Wallet).`);
31
+ }
32
+ return (cfg.BUYER_PRIVATE_KEY.startsWith("0x") ? cfg.BUYER_PRIVATE_KEY : `0x${cfg.BUYER_PRIVATE_KEY}`);
33
+ }
34
+ /** Sign one Gateway leg's EIP-3009 authorization against the GatewayWallet domain and return
35
+ * the full envelope `{x402Version, payload, resource, accepted}` — exactly the gate's
36
+ * `gatewayLegPayload` shape. Wraps the SDK's `BatchEvmScheme` so the domain / validity clamp
37
+ * never drifts from the rail. SDK loaded lazily so the mock path never pulls it in. */
38
+ export async function gatewayLegPayload(signer, quoted, x402Version) {
39
+ // Both pre-sign guards live HERE, not in the callers. They used to sit in gatewayBuyer only,
40
+ // while railBuyer (the mixed-fleet path) called this helper directly and got neither — the
41
+ // classic drifted-sibling shape. Taking the whole `quoted` instead of bare requirements makes
42
+ // them unbypassable: a caller cannot reach the signer without passing through both.
43
+ //
44
+ // 1. The Circle SDK's batched pay signs ONE leg. An N-leg (operator-fee) quote would be silently
45
+ // underpaid — refuse loudly and point at the memo rail (N-leg-capable).
46
+ if (quoted.legs && quoted.legs.length > 1) {
47
+ throw new Error(`gateway (Circle SDK) mode cannot pay a ${quoted.legs.length}-leg toll (operator fee): ` +
48
+ `the SDK signs only the author leg. Use the memo (Arc) rail for multi-leg settlement.`);
49
+ }
50
+ const requirements = quoted.requirements;
51
+ // 2. Refuse to sign a Circle envelope for a 402 that is not actually a Gateway batching option.
52
+ if (requirements.extra?.name !== "GatewayWalletBatched") {
53
+ throw new Error("gateway mode expects a Circle Gateway batching option (extra.name 'GatewayWalletBatched'); " +
54
+ "the gate advertised a non-gateway 402. Check PAYMENT_MODE / the settlement network.");
55
+ }
56
+ const { BatchEvmScheme } = await import("@circle-fin/x402-batching/client");
57
+ const scheme = new BatchEvmScheme(signer);
58
+ const signed = await scheme.createPaymentPayload(x402Version, requirements);
59
+ return { ...signed, resource: quoted.resource, accepted: requirements };
60
+ }
61
+ export function gatewayBuyer(signer) {
62
+ const cfg = getConfig();
63
+ // Resolve the signer once. An injected signer NEVER reads BUYER_PRIVATE_KEY (the whole point
64
+ // of the cloud wallet — the key lives in the grant-checked BFF, not this process).
65
+ let resolved = signer ?? null;
66
+ const getSigner = async () => {
67
+ if (resolved)
68
+ return resolved;
69
+ const { privateKeyToAccount } = await import("viem/accounts");
70
+ return (resolved = privateKeyToAccount(envAccountKey()));
71
+ };
72
+ const fallbackAddress = signer ? signer.address : (cfg.BUYER_ADDRESS ?? "0x");
73
+ return {
74
+ get address() {
75
+ return resolved?.address ?? fallbackAddress;
76
+ },
77
+ async init() {
78
+ // Custody-free out-of-band deposit: the Gateway balance is funded separately, so the
79
+ // injected-signer (cloud) path is a no-op. The env/CLI path keeps the SDK deposit for
80
+ // backwards compatibility — it needs the raw key, which only exists on that path.
81
+ if (signer)
82
+ return;
83
+ // Resolve the env-key address here so `address` is honest from init onward, matching
84
+ // memoBuyer (which resolves eagerly at construction). Without this the env/CLI path
85
+ // reported the "0x" placeholder until the first pay — agent.ts logs `wallet ${address}`
86
+ // BEFORE init(), so an operator saw `wallet 0x` instead of the real derived address.
87
+ await getSigner();
88
+ const { GatewayClient } = await import("@circle-fin/x402-batching/client");
89
+ const client = new GatewayClient({ chain: activeNetwork().chainName, privateKey: envAccountKey() });
90
+ console.log(` depositing ${cfg.DEPOSIT_AMOUNT_USDC} USDC into the Gateway Wallet...`);
91
+ const result = await client.deposit(cfg.DEPOSIT_AMOUNT_USDC);
92
+ console.log(` deposit tx ${result.depositTxHash}`);
93
+ },
94
+ price(url, kind) {
95
+ return probe(url, kind, this.address).then((o) => (o.status === "gated" ? o.quoted : null));
96
+ },
97
+ async fetch(url, kind, guard) {
98
+ const address = this.address;
99
+ // The shared loop owns probe→moved-guard→paid-GET→classify. The gateway rail supplies only
100
+ // how it builds the payment (the two pre-sign guards live INSIDE the builder so a bad quote
101
+ // throws into onSignError, keeping a typed result) and how it classifies a sign throw
102
+ // (grant refusal → typed needs_topup; any other → classifyPaymentError — a gateway sign path
103
+ // never sees a socket error, the shared loop handles that as a rail-agnostic origin_error).
104
+ return runPaidFetch(url, kind, address, guard, async (quoted) => {
105
+ // Both pre-sign guards (N-leg refusal + GatewayWalletBatched check) now live inside
106
+ // gatewayLegPayload, so this rail and railBuyer enforce them identically. They still
107
+ // throw into onSignError below, keeping a typed Fetched.
108
+ const payload = await gatewayLegPayload(await getSigner(), quoted, 2);
109
+ return Buffer.from(JSON.stringify(payload)).toString("base64");
110
+ }, (error) => {
111
+ const refusal = classifySignerRefusal(error);
112
+ return refusal
113
+ ? { ok: false, error, ...refusal }
114
+ : { ok: false, error, ...classifyPaymentError(error) };
115
+ });
116
+ },
117
+ };
118
+ }
119
+ /**
120
+ * Out-of-band, custody-free deposit into the Circle Gateway Wallet — the non-custodial contract that
121
+ * holds the buyer's unified balance (Circle infra, buyer-controlled; naulon custodies nothing). The
122
+ * cloud (injected-signer) path funds the Gateway balance HERE, out of band, because `gatewayBuyer.init()`
123
+ * is a deposit NO-OP when a signer is injected (the key lives in the grant-checked BFF, not this process).
124
+ * The self-host/CLI path still deposits inside `init()`; this is the standalone entry a deposit
125
+ * script/operator calls. SDK loaded lazily so the mock/memo paths never pull it in.
126
+ */
127
+ export async function gatewayDeposit(opts) {
128
+ const { GatewayClient } = await import("@circle-fin/x402-batching/client");
129
+ const client = new GatewayClient({ chain: opts.chain, privateKey: opts.privateKey });
130
+ return client.deposit(opts.amountUsdc);
131
+ }
132
+ /** Read the wallet + Gateway balances for a key — the preflight a deposit script shows before it moves
133
+ * funds, and the check the buyer uses to see if its unified balance covers a toll. Pass `address` to
134
+ * read ANOTHER account's balances (e.g. confirm the AUTHOR received a settle) — the client key only
135
+ * authenticates the read, it needn't own the address. SDK loaded lazily. */
136
+ export async function gatewayBalances(opts) {
137
+ const { GatewayClient } = await import("@circle-fin/x402-batching/client");
138
+ return new GatewayClient({ chain: opts.chain, privateKey: opts.privateKey }).getBalances(opts.address);
139
+ }
140
+ /**
141
+ * Look up a single Gateway transfer (a settlement) by its Circle id — the `settlementRef` a gateway
142
+ * settle stamps. On the Gateway rail that ref is a **Circle UUID, not an on-chain tx hash**; the
143
+ * response carries the authoritative `status` plus the eventual on-chain `txHash`. This is the
144
+ * correct "did the settle land?" check — pair `status` with `classifyGatewaySettlement`. SDK lazy.
145
+ */
146
+ export async function gatewayTransferStatus(opts) {
147
+ const { GatewayClient } = await import("@circle-fin/x402-batching/client");
148
+ return new GatewayClient({ chain: opts.chain, privateKey: opts.privateKey }).getTransferById(opts.id);
149
+ }
150
+ /**
151
+ * Search Gateway transfers with optional filters (`to`/`from`/`status`/`network`/date range). Use to
152
+ * confirm a payee (author) received without holding the transfer id — e.g. `{ to: authorAddress }`.
153
+ * SDK loaded lazily.
154
+ */
155
+ export async function gatewayTransfers(opts) {
156
+ const { chain, privateKey, ...params } = opts;
157
+ const { GatewayClient } = await import("@circle-fin/x402-batching/client");
158
+ return new GatewayClient({ chain, privateKey }).searchTransfers(params);
159
+ }
160
+ /**
161
+ * Classify a Circle Gateway transfer `status` into settled / pending / failed. This is the CODE form
162
+ * of the hard-won rule that a Gateway settle credits the payee's OFF-CHAIN Gateway balance — so
163
+ * `balanceOf(payee)` is the wrong check and the transfer's own `status` is the authoritative signal.
164
+ * `completed` ⇒ settled; the in-pipeline states ⇒ pending; `failed` ⇒ failed. An unknown/future status
165
+ * is treated as `pending` — never falsely report the money landed.
166
+ */
167
+ export function classifyGatewaySettlement(status) {
168
+ switch (status) {
169
+ case "completed":
170
+ return "settled";
171
+ case "failed":
172
+ return "failed";
173
+ case "received":
174
+ case "batched":
175
+ case "confirmed":
176
+ return "pending";
177
+ default: {
178
+ // Exhaustiveness guard: if Circle adds a TransferStatus, this line fails tsc —
179
+ // forcing a human to classify it, NOT silently bucketing it as pending. At
180
+ // runtime an unknown value is treated as pending (never falsely "settled").
181
+ const _exhaustive = status;
182
+ void _exhaustive;
183
+ return "pending";
184
+ }
185
+ }
186
+ }
187
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Wayfarer CLI — an autonomous research agent that pays per citation.
3
+ *
4
+ * npm run wayfarer -- "what does the author say about payment and passage?"
5
+ *
6
+ * It discovers essays, decides which are worth paying to cite under a budget
7
+ * (visible reasoning, not hardcoded), pays the naulon through the tollgate, and
8
+ * grounds a cited answer. Requires a running tollgate (npm run tollgate).
9
+ */
10
+ import { run } from "./lib.js";
11
+ const topic = process.argv.slice(2).join(" ").trim();
12
+ if (!topic) {
13
+ console.error('usage: npm run wayfarer -- "<research topic>"');
14
+ process.exit(1);
15
+ }
16
+ const result = await run(topic, (line) => console.log(line));
17
+ console.log("\n" + "─".repeat(60));
18
+ console.log(result.answer);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAE7D,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AACnC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC"}
package/dist/lib.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @naulon/wayfarer — public library surface.
3
+ *
4
+ * The autonomous buy-side research agent: discover → quote → appraise → decide →
5
+ * pay → ground, with reusable Citation Licenses (pay once, re-read free). This
6
+ * barrel is the side-effect-free entry the package.json `exports` map points at;
7
+ * the CLI (`./index.ts`) is a thin consumer of it, never the other way round.
8
+ *
9
+ * Stage-name map (buy-side spec → the genuine export):
10
+ * quote → probePrice (free 402 probe, no spend)
11
+ * pay → selectBuyer + the Buyer seam (mock | memo | gateway)
12
+ * read → rereadWithLicense (free re-read of a held live license)
13
+ */
14
+ export { run } from "./agent.ts";
15
+ export type { Logger, RunOptions } from "./agent.ts";
16
+ export { tollgateBase, articleUrl, fetchJwks, verifyAgainst } from "./agent.ts";
17
+ export { discover } from "./discover.ts";
18
+ export { appraise } from "./appraise.ts";
19
+ export { probe, probePrice, probeFailure, assemblePayment, rereadWithLicense, selectBuyer, quotedTotalAtomic, tollMovedOrNull, classifyPaymentError, } from "./buyer.ts";
20
+ export type { Buyer, Quoted, LegRequirements, Fetched, FetchErrorCode, PayGuard, ProbeOutcome } from "./buyer.ts";
21
+ export { mockBuyer } from "./pay.ts";
22
+ export { gatewayBuyer, gatewayDeposit, gatewayBalances, gatewayTransferStatus, gatewayTransfers, classifyGatewaySettlement, } from "./gateway.ts";
23
+ export type { GatewaySigner, GatewayDepositOpts, GatewaySettlementState } from "./gateway.ts";
24
+ export { memoBuyer, signMemoPayment } from "./memo.ts";
25
+ export type { MemoSigner } from "./memo.ts";
26
+ export { railBuyer } from "./rail.ts";
27
+ export type { RailSigners } from "./rail.ts";
28
+ export { decide, DEFAULT_POLICY, spendGate } from "./decide.ts";
29
+ export type { DecisionPolicy, DecideContext, SpendVerdict } from "./decide.ts";
30
+ export { authorizeOrigin } from "./origin-policy.ts";
31
+ export type { OriginRequest, OriginVerdict, PayableTarget } from "./origin-policy.ts";
32
+ export { allocateByContribution } from "./allocation.ts";
33
+ export type { SourceAllocation } from "./allocation.ts";
34
+ export { decodeHeld, fileHeldStore, isLive, loadHeld, memoryHeldStore, saveHeld } from "./licenseStore.ts";
35
+ export type { HeldLicense, HeldStore } from "./licenseStore.ts";
36
+ export { buildPopProof } from "./pop.ts";
37
+ export { getWallet } from "./wallet.ts";
38
+ export type { AgentWallet } from "./wallet.ts";
39
+ export type { Candidate, PricedCandidate, AppraisedCandidate, Action, Decision, Source, RunResult, } from "./types.ts";
40
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKrD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhF,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EACL,KAAK,EACL,UAAU,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAClH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM/E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC3G,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,YAAY,EACV,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,QAAQ,EACR,MAAM,EACN,SAAS,GACV,MAAM,YAAY,CAAC"}
package/dist/lib.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @naulon/wayfarer — public library surface.
3
+ *
4
+ * The autonomous buy-side research agent: discover → quote → appraise → decide →
5
+ * pay → ground, with reusable Citation Licenses (pay once, re-read free). This
6
+ * barrel is the side-effect-free entry the package.json `exports` map points at;
7
+ * the CLI (`./index.ts`) is a thin consumer of it, never the other way round.
8
+ *
9
+ * Stage-name map (buy-side spec → the genuine export):
10
+ * quote → probePrice (free 402 probe, no spend)
11
+ * pay → selectBuyer + the Buyer seam (mock | memo | gateway)
12
+ * read → rereadWithLicense (free re-read of a held live license)
13
+ */
14
+ // ── pipeline entry ──────────────────────────────────────────────────────────
15
+ export { run } from "./agent.js";
16
+ // Pipeline primitives a second consumer (the MCP server) reuses to resolve an
17
+ // article URL from a slug against the configured gate, and to verify a captured
18
+ // license against the gate's JWKS — single-sourced here so the two can't drift.
19
+ export { tollgateBase, articleUrl, fetchJwks, verifyAgainst } from "./agent.js";
20
+ // ── discover ────────────────────────────────────────────────────────────────
21
+ export { discover } from "./discover.js";
22
+ // ── appraise ────────────────────────────────────────────────────────────────
23
+ export { appraise } from "./appraise.js";
24
+ // ── quote + pay (the Buyer seam) ────────────────────────────────────────────
25
+ export { probe, probePrice, probeFailure, assemblePayment, rereadWithLicense, selectBuyer, quotedTotalAtomic, tollMovedOrNull, classifyPaymentError, } from "./buyer.js";
26
+ export { mockBuyer } from "./pay.js";
27
+ export { gatewayBuyer, gatewayDeposit, gatewayBalances, gatewayTransferStatus, gatewayTransfers, classifyGatewaySettlement, } from "./gateway.js";
28
+ export { memoBuyer, signMemoPayment } from "./memo.js";
29
+ export { railBuyer } from "./rail.js";
30
+ // ── decide (policy) ─────────────────────────────────────────────────────────
31
+ export { decide, DEFAULT_POLICY, spendGate } from "./decide.js";
32
+ // ── origin policy (whose origin may money touch) ─────────────────────────────
33
+ // The one answer to that question; `spendGate` above stays the one answer to "how
34
+ // much". `PayableTarget` is mintable only by `authorizeOrigin`, so a pay path that
35
+ // skips the check fails to typecheck rather than failing in production.
36
+ export { authorizeOrigin } from "./origin-policy.js";
37
+ // ── cross-source allocation (buyer-side citation-reward policy) ──────────────
38
+ export { allocateByContribution } from "./allocation.js";
39
+ // ── citation licenses (pay once, re-read free) ──────────────────────────────
40
+ export { decodeHeld, fileHeldStore, isLive, loadHeld, memoryHeldStore, saveHeld } from "./licenseStore.js";
41
+ export { buildPopProof } from "./pop.js";
42
+ // ── wallet ──────────────────────────────────────────────────────────────────
43
+ export { getWallet } from "./wallet.js";
44
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,+EAA+E;AAC/E,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAGjC,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhF,+EAA+E;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,+EAA+E;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,+EAA+E;AAC/E,OAAO,EACL,KAAK,EACL,UAAU,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,+EAA+E;AAC/E,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGhE,gFAAgF;AAChF,kFAAkF;AAClF,mFAAmF;AACnF,wEAAwE;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrD,gFAAgF;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE3G,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,+EAA+E;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,56 @@
1
+ export interface HeldLicense {
2
+ slug: string;
3
+ title: string;
4
+ jti: string;
5
+ /** exp (epoch seconds) decoded from the token, for the liveness check. */
6
+ exp: number;
7
+ /** The token's audience (= gate identity); the value a PoP proof must bind to. */
8
+ aud: string;
9
+ /** Holder-of-key: true when the license carries a `cnf` claim — a re-read needs
10
+ * a wallet proof-of-possession, not just the token. */
11
+ pop: boolean;
12
+ /** The compact-JWS token to present on a re-read. */
13
+ jws: string;
14
+ /** The canonical URL this source was actually PAID at — captured at pay time so a
15
+ * later `read_held` re-fetches the exact link (`/articles/<slug>`, a custom domain,
16
+ * whatever the publisher serves) instead of reconstructing a `/essays/<slug>`
17
+ * template that 404s off-shape. Optional: a license held from before this field
18
+ * existed has none, and the re-read falls back to the template. NOT part of the
19
+ * decoded token — it's the buyer's own bookkeeping, so `decodeHeld` never sets it. */
20
+ url?: string;
21
+ }
22
+ /**
23
+ * Decode a token's `jti`, `exp`, and `naulon.slug/title` WITHOUT verifying — this
24
+ * is the agent reading its own captured receipt for bookkeeping, not trusting a
25
+ * third party. (The agent separately verifies the signature against the gate's
26
+ * JWKS when it captures the license; see agent.ts.) Returns null if unparseable.
27
+ */
28
+ export declare function decodeHeld(jws: string): Omit<HeldLicense, "jws"> | null;
29
+ /** Is this held license still valid at `nowSec` (epoch seconds)? */
30
+ export declare function isLive(held: HeldLicense, nowSec: number): boolean;
31
+ export declare function loadHeld(): Promise<Map<string, HeldLicense>>;
32
+ export declare function saveHeld(held: Map<string, HeldLicense>): Promise<void>;
33
+ /**
34
+ * The held-license backend as a seam. The stdio funnel uses the process-global
35
+ * file (`fileHeldStore`); the hosted path (many buyer sessions in one process)
36
+ * MUST inject a store scoped to the caller — else session B could re-read the
37
+ * license session A paid for, since the file store is keyed by slug alone and
38
+ * shared across every session in the process (a cross-buyer isolation leak).
39
+ * The interface is deliberately the shape `loadHeld`/`saveHeld` already have, so
40
+ * the file default and an injected store are interchangeable at every call site.
41
+ */
42
+ export interface HeldStore {
43
+ load(): Promise<Map<string, HeldLicense>>;
44
+ save(held: Map<string, HeldLicense>): Promise<void>;
45
+ }
46
+ /** The OSS default: the process-global JSON file at `WAYFARER_LICENSE_PATH`. */
47
+ export declare const fileHeldStore: HeldStore;
48
+ /**
49
+ * An in-process, isolated held store — the hosted-path default. Each instance
50
+ * owns a private Map; two instances never share state, so building one per MCP
51
+ * session gives per-session isolation for free (session B's `load()` cannot see
52
+ * what session A `save()`d). Ephemeral by design: a held license lives with the
53
+ * session, not across process restarts — the right trade for a capped hot session.
54
+ */
55
+ export declare function memoryHeldStore(seed?: Iterable<readonly [string, HeldLicense]>): HeldStore;
56
+ //# sourceMappingURL=licenseStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licenseStore.d.ts","sourceRoot":"","sources":["../src/licenseStore.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ;2DACuD;IACvD,GAAG,EAAE,OAAO,CAAC;IACb,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;0FAKsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAID;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,GAAG,IAAI,CAuBvE;AAED,oEAAoE;AACpE,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CASlE;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,gFAAgF;AAChF,eAAO,MAAM,aAAa,EAAE,SAA8C,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,SAAS,CAS1F"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The agent's wallet of Citation Licenses. When the gate hands back a license on
3
+ * a paid read, the agent keeps it; a live (unexpired) one lets it re-read that
4
+ * essay free on a later run instead of paying again — the buyer-side half of the
5
+ * "toll becomes an asset" thesis. Persisted to a small JSON file (WAYFARER_LICENSE_PATH).
6
+ */
7
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
8
+ import { dirname } from "node:path";
9
+ import { getConfig } from "@naulon/shared";
10
+ const file = () => getConfig().WAYFARER_LICENSE_PATH;
11
+ /**
12
+ * Decode a token's `jti`, `exp`, and `naulon.slug/title` WITHOUT verifying — this
13
+ * is the agent reading its own captured receipt for bookkeeping, not trusting a
14
+ * third party. (The agent separately verifies the signature against the gate's
15
+ * JWKS when it captures the license; see agent.ts.) Returns null if unparseable.
16
+ */
17
+ export function decodeHeld(jws) {
18
+ try {
19
+ const seg = jws.split(".")[1];
20
+ if (!seg)
21
+ return null;
22
+ const claims = JSON.parse(Buffer.from(seg, "base64url").toString("utf8"));
23
+ if (!claims.jti || !claims.exp || !claims.aud || !claims.naulon?.slug)
24
+ return null;
25
+ return {
26
+ slug: claims.naulon.slug,
27
+ title: claims.naulon.title ?? claims.naulon.slug,
28
+ jti: claims.jti,
29
+ exp: claims.exp,
30
+ aud: claims.aud,
31
+ pop: typeof claims.cnf?.["naulon:addr"] === "string",
32
+ };
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /** Is this held license still valid at `nowSec` (epoch seconds)? */
39
+ export function isLive(held, nowSec) {
40
+ return held.exp > nowSec;
41
+ }
42
+ export async function loadHeld() {
43
+ try {
44
+ const raw = await readFile(file(), "utf8");
45
+ const arr = JSON.parse(raw);
46
+ return new Map(arr.map((h) => [h.slug, h]));
47
+ }
48
+ catch (err) {
49
+ if (err.code === "ENOENT")
50
+ return new Map();
51
+ throw err;
52
+ }
53
+ }
54
+ export async function saveHeld(held) {
55
+ const path = file();
56
+ await mkdir(dirname(path), { recursive: true });
57
+ await writeFile(path, JSON.stringify([...held.values()], null, 2), "utf8");
58
+ }
59
+ /** The OSS default: the process-global JSON file at `WAYFARER_LICENSE_PATH`. */
60
+ export const fileHeldStore = { load: loadHeld, save: saveHeld };
61
+ /**
62
+ * An in-process, isolated held store — the hosted-path default. Each instance
63
+ * owns a private Map; two instances never share state, so building one per MCP
64
+ * session gives per-session isolation for free (session B's `load()` cannot see
65
+ * what session A `save()`d). Ephemeral by design: a held license lives with the
66
+ * session, not across process restarts — the right trade for a capped hot session.
67
+ */
68
+ export function memoryHeldStore(seed) {
69
+ const store = new Map(seed);
70
+ return {
71
+ load: async () => new Map(store),
72
+ save: async (held) => {
73
+ store.clear();
74
+ for (const [k, v] of held)
75
+ store.set(k, v);
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=licenseStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licenseStore.js","sourceRoot":"","sources":["../src/licenseStore.ts"],"names":[],"mappings":"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"}
package/dist/memo.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { type TypedDataDomain } from "viem";
2
+ import { TRANSFER_WITH_AUTHORIZATION_TYPES, type MemoAuthorization } from "@naulon/shared";
3
+ import { type Buyer, type LegRequirements, type Quoted } from "./buyer.ts";
4
+ /**
5
+ * The signer seam (BUY-2). By default the memo buyer signs each EIP-3009 leg with the local
6
+ * `BUYER_PRIVATE_KEY` (the OSS self-host path). A cloud host instead injects a `MemoSigner` — an
7
+ * object that signs the SAME typed data elsewhere (a grant-checked BFF holding an encrypted session
8
+ * key), so the private key never lives in the MCP process. A `PrivateKeyAccount` from viem satisfies
9
+ * this shape too, which keeps the two paths symmetric. The env key is only ever read on the default
10
+ * path — when a signer is injected, `BUYER_PRIVATE_KEY` is never touched.
11
+ */
12
+ export interface MemoSigner {
13
+ address: `0x${string}`;
14
+ signTypedData(args: {
15
+ domain: TypedDataDomain;
16
+ types: typeof TRANSFER_WITH_AUTHORIZATION_TYPES;
17
+ primaryType: "TransferWithAuthorization";
18
+ message: {
19
+ from: `0x${string}`;
20
+ to: `0x${string}`;
21
+ value: bigint;
22
+ validAfter: bigint;
23
+ validBefore: bigint;
24
+ nonce: `0x${string}`;
25
+ };
26
+ }): Promise<`0x${string}`>;
27
+ }
28
+ /** Sign a raw USDC EIP-3009 `TransferWithAuthorization` against the active network's USDC
29
+ * EIP-712 domain and return the base64 `{authorization, signature}` payload the gate's
30
+ * memo settle path parses. The memo id is NOT signed (the gate attaches it at relay), so
31
+ * the buyer needs nothing extra. Exported for unit testing against the gate's pre-verify. */
32
+ export declare function signMemoPayment(requirements: Quoted["requirements"], nowMs: number): Promise<string>;
33
+ /** The raw `{ authorization, signature }` for ONE leg — signs the leg's payTo + amount
34
+ * against the active network's USDC domain, with its own fresh EIP-3009 nonce. The
35
+ * per-leg primitive `assemblePayment` calls once per advertised leg (mirrors the gate's
36
+ * `memoLegPayload` → `buildMemoSignatures`). The memo id is NOT signed (the relayer
37
+ * attaches it at submit), so each leg needs nothing beyond its own authorization. */
38
+ export declare function memoLegPayload(requirements: LegRequirements, nowMs: number, signer?: MemoSigner, net?: import("@naulon/shared").SettlementNetwork): Promise<{
39
+ authorization: MemoAuthorization;
40
+ signature: `0x${string}`;
41
+ }>;
42
+ export declare function memoBuyer(signer?: MemoSigner): Buyer;
43
+ //# sourceMappingURL=memo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../src/memo.ts"],"names":[],"mappings":"AAeA,OAAO,EAAS,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AACnD,OAAO,EAIL,iCAAiC,EACjC,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAIL,KAAK,KAAK,EAEV,KAAK,eAAe,EAEpB,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAGpB;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,eAAe,CAAC;QACxB,KAAK,EAAE,OAAO,iCAAiC,CAAC;QAChD,WAAW,EAAE,2BAA2B,CAAC;QACzC,OAAO,EAAE;YACP,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YACpB,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,UAAU,EAAE,MAAM,CAAC;YACnB,WAAW,EAAE,MAAM,CAAC;YACpB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;SACtB,CAAC;KACH,GAAG,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;CAC5B;AAaD;;;8FAG8F;AAC9F,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,EACpC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;sFAIsF;AACtF,wBAAsB,cAAc,CAClC,YAAY,EAAE,eAAe,EAC7B,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,UAAU,EACnB,GAAG,6CAAkB,GACpB,OAAO,CAAC;IAAE,aAAa,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,KAAK,MAAM,EAAE,CAAA;CAAE,CAAC,CAoCzE;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,KAAK,CA+BpD"}
package/dist/memo.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Memo buyer — the client side of the Arc self-relay rail. On a memo-capable network the
3
+ * gate settles by RELAYING a raw USDC EIP-3009 authorization through the Arc Memo
4
+ * predeploy (not Circle Gateway), so the buyer signs the authorization against the USDC
5
+ * EIP-712 domain — the shared descriptor — and posts `{authorization, signature}` as the
6
+ * `payment-signature`. There is NO Gateway deposit: the transfer moves straight from the
7
+ * buyer's USDC balance and the gate's relayer only pays gas (custody-free).
8
+ *
9
+ * Counterpart to gateway.ts (Circle SDK, GatewayWallet domain) and pay.ts (mock offline).
10
+ * The signing here is wayfarer's own viem call over shared's `TRANSFER_WITH_AUTHORIZATION`
11
+ * descriptor + `usdcDomain` — the same "each consumer signs against the one shared
12
+ * descriptor" split the gate's own `buildMemoSignature` uses (the gate then verifies
13
+ * against that identical descriptor). `selectBuyer` routes here when `supportsMemo`.
14
+ */
15
+ import { privateKeyToAccount } from "viem/accounts";
16
+ import { toHex } from "viem";
17
+ import { activeNetwork, getConfig, usdcDomain, TRANSFER_WITH_AUTHORIZATION_TYPES, } from "@naulon/shared";
18
+ import { assemblePayment, classifySignerRefusal, probePrice, } from "./buyer.js";
19
+ import { runPaidFetch } from "./paidFetch.js";
20
+ function buyerKey() {
21
+ const cfg = getConfig();
22
+ if (!cfg.BUYER_PRIVATE_KEY) {
23
+ throw new Error(`PAYMENT_MODE=gateway on ${activeNetwork().chainName} (memo rail) requires BUYER_PRIVATE_KEY ` +
24
+ `— a wallet holding USDC on that network (the EIP-3009 transfer moves straight from it).`);
25
+ }
26
+ return (cfg.BUYER_PRIVATE_KEY.startsWith("0x") ? cfg.BUYER_PRIVATE_KEY : `0x${cfg.BUYER_PRIVATE_KEY}`);
27
+ }
28
+ /** Sign a raw USDC EIP-3009 `TransferWithAuthorization` against the active network's USDC
29
+ * EIP-712 domain and return the base64 `{authorization, signature}` payload the gate's
30
+ * memo settle path parses. The memo id is NOT signed (the gate attaches it at relay), so
31
+ * the buyer needs nothing extra. Exported for unit testing against the gate's pre-verify. */
32
+ export async function signMemoPayment(requirements, nowMs) {
33
+ return Buffer.from(JSON.stringify(await memoLegPayload(requirements, nowMs))).toString("base64");
34
+ }
35
+ /** The raw `{ authorization, signature }` for ONE leg — signs the leg's payTo + amount
36
+ * against the active network's USDC domain, with its own fresh EIP-3009 nonce. The
37
+ * per-leg primitive `assemblePayment` calls once per advertised leg (mirrors the gate's
38
+ * `memoLegPayload` → `buildMemoSignatures`). The memo id is NOT signed (the relayer
39
+ * attaches it at submit), so each leg needs nothing beyond its own authorization. */
40
+ export async function memoLegPayload(requirements, nowMs, signer, net = activeNetwork()) {
41
+ const cfg = getConfig();
42
+ // Default path resolves the local key; an injected signer NEVER reads BUYER_PRIVATE_KEY (the whole
43
+ // point of the cloud wallet — the key lives in the grant-checked BFF, not this process).
44
+ const envAccount = signer ? undefined : privateKeyToAccount(buyerKey());
45
+ const from = (signer?.address ?? envAccount.address);
46
+ // Stamp validity at PAY time (nowMs is `Date.now()` from fetch, not quote time) with a
47
+ // MARGIN: floor the window to WAYFARER_MIN_VALIDITY_SECONDS so a gate advertising a
48
+ // too-short maxTimeoutSeconds can't make the authorization expire before the relay
49
+ // submits it (the facilitator's `authorization_validity_too_short` — the Keryx ~1-day
50
+ // lesson; see memory `x402-validity-window-floor`). The window only widens, never
51
+ // shrinks — a long gate window is honored as-is.
52
+ const window = Math.max(requirements.maxTimeoutSeconds, cfg.WAYFARER_MIN_VALIDITY_SECONDS);
53
+ const authorization = {
54
+ from,
55
+ to: requirements.payTo,
56
+ value: requirements.amount,
57
+ validAfter: "0",
58
+ validBefore: String(Math.floor(nowMs / 1000) + window),
59
+ nonce: toHex(crypto.getRandomValues(new Uint8Array(32))),
60
+ };
61
+ const typedData = {
62
+ domain: usdcDomain(net, cfg.USDC_EIP712_NAME),
63
+ types: TRANSFER_WITH_AUTHORIZATION_TYPES,
64
+ primaryType: "TransferWithAuthorization",
65
+ message: {
66
+ from: authorization.from,
67
+ to: authorization.to,
68
+ value: BigInt(authorization.value),
69
+ validAfter: BigInt(authorization.validAfter),
70
+ validBefore: BigInt(authorization.validBefore),
71
+ nonce: authorization.nonce,
72
+ },
73
+ };
74
+ const signature = signer ? await signer.signTypedData(typedData) : await envAccount.signTypedData(typedData);
75
+ return { authorization, signature };
76
+ }
77
+ export function memoBuyer(signer) {
78
+ const address = signer ? signer.address : privateKeyToAccount(buyerKey()).address;
79
+ return {
80
+ address,
81
+ async init() {
82
+ // No Gateway deposit — the EIP-3009 transfer pays straight from the buyer's USDC
83
+ // balance, and the relayer (gate-side) covers gas. `make arc-preflight` checks funding.
84
+ },
85
+ price(url, kind) {
86
+ return probePrice(url, kind, address);
87
+ },
88
+ async fetch(url, kind, guard) {
89
+ // One raw EIP-3009 authorization per advertised leg (operator fee → 2-leg array); a stock
90
+ // single-author quote stays the bare object, byte-identical to before. The shared loop owns
91
+ // probe→moved-guard→paid-GET→classify; the memo rail supplies only how it signs and how it
92
+ // types a sign refusal (grant exhausted/expired/no session → typed; else a transient origin throw).
93
+ return runPaidFetch(url, kind, address, guard, (quoted, nowMs) => assemblePayment(quoted, (req) => memoLegPayload(req, nowMs, signer)), (error) => {
94
+ const refusal = classifySignerRefusal(error);
95
+ return refusal
96
+ ? { ok: false, error, ...refusal }
97
+ : { ok: false, error, errorCode: "origin_error", retryable: true };
98
+ });
99
+ },
100
+ };
101
+ }
102
+ //# sourceMappingURL=memo.js.map