@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 @@
1
+ {"version":3,"file":"memo.js","sourceRoot":"","sources":["../src/memo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * THE origin decision: may money touch this URL?
3
+ *
4
+ * There were two answers to that question in this codebase — `originPinRefusal` in
5
+ * wayfarer-mcp (unconditional host:port equality) and the inline branch at the top of
6
+ * decide()'s spend gates (equality UNLESS an allowlist is set). A comment asked the
7
+ * second one not to drift from the first. It drifted anyway, and a third surface (the
8
+ * cloud's /ask pay path) grew no check at all.
9
+ *
10
+ * A comment cannot hold an invariant. A type can. `authorizeOrigin` is the only
11
+ * function that mints a `PayableTarget`, and the pay/probe entrypoints accept nothing
12
+ * else — so a caller that skips the check does not compile.
13
+ *
14
+ * Scope, deliberately narrow: this module owns ENDPOINT IDENTITY — host:port must equal
15
+ * the configured gate, because a different port is a different service and must not
16
+ * satisfy the pin. That is the whole of its authority.
17
+ *
18
+ * It does NOT own domain policy. An allowlist names domains ("inneraxiom.com"), and
19
+ * which of them are payable — along with deny lists, per-domain caps, the kill switch
20
+ * and human approval — belongs to `spendGate`, the one shared evaluator. Stating an
21
+ * allowlist therefore REPLACES the gate pin rather than stacking with it: the operator
22
+ * has named a boundary, so identity steps aside and spendGate adjudicates. Checking it
23
+ * in both places would recreate the very divergence this module exists to end.
24
+ *
25
+ * So: "whose origin, by identity" here; "which domains, at what price" there. Neither
26
+ * answers the other's question, and neither has a second implementation.
27
+ */
28
+ declare const payable: unique symbol;
29
+ /**
30
+ * A URL that has passed `authorizeOrigin`. Structurally a string, but nominally
31
+ * unforgeable outside this module — pay/probe signatures take this, never `string`,
32
+ * so "I forgot to check" is a type error rather than an incident.
33
+ */
34
+ export type PayableTarget = string & {
35
+ readonly [payable]: true;
36
+ };
37
+ /** What to authorize, and against what trust boundary. */
38
+ export interface OriginRequest {
39
+ /** The URL money would touch. Model-supplied in the MCP tools — assume hostile. */
40
+ target: string;
41
+ /** The configured gate, when there is one. Absent in the fleet case. */
42
+ gate?: string | undefined;
43
+ /**
44
+ * The domains this caller may pay. `undefined` = no policy stated, so the gate pin
45
+ * is the whole boundary. An EMPTY array is a stated policy of "nothing", NOT
46
+ * "unrestricted" — an operator whose tenant lookup returned nothing must get
47
+ * refusals rather than an open wallet.
48
+ */
49
+ allowDomains?: readonly string[] | undefined;
50
+ }
51
+ /**
52
+ * Why a target was refused. Callers render their OWN prose from this — the research
53
+ * path's skip reason and an MCP tool's refusal address different readers, and forcing
54
+ * one wording on both would be a cosmetic unification of the wrong thing. The DECISION
55
+ * is what must not diverge; the sentence may.
56
+ */
57
+ export type OriginRefusalCode = "invalid-target" | "invalid-gate" | "off-gate";
58
+ export type OriginVerdict = {
59
+ ok: true;
60
+ target: PayableTarget;
61
+ } | {
62
+ ok: false;
63
+ code: OriginRefusalCode;
64
+ refusal: string;
65
+ };
66
+ /**
67
+ * Decide whether `target` may be quoted or paid, and if so hand back the one token
68
+ * the pay path accepts. Exact-match only at both levels: a subdomain is a different
69
+ * host and is never implied by its parent, because "*.example" trust is a decision an
70
+ * operator should have to write down.
71
+ */
72
+ export declare function authorizeOrigin(req: OriginRequest): OriginVerdict;
73
+ export {};
74
+ //# sourceMappingURL=origin-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin-policy.d.ts","sourceRoot":"","sources":["../src/origin-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,CAAC,MAAM,OAAO,EAAE,OAAO,MAAM,CAAC;AAErC;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAElE,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,cAAc,GAAG,UAAU,CAAC;AAE/E,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAW5D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,CA8CjE"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * THE origin decision: may money touch this URL?
3
+ *
4
+ * There were two answers to that question in this codebase — `originPinRefusal` in
5
+ * wayfarer-mcp (unconditional host:port equality) and the inline branch at the top of
6
+ * decide()'s spend gates (equality UNLESS an allowlist is set). A comment asked the
7
+ * second one not to drift from the first. It drifted anyway, and a third surface (the
8
+ * cloud's /ask pay path) grew no check at all.
9
+ *
10
+ * A comment cannot hold an invariant. A type can. `authorizeOrigin` is the only
11
+ * function that mints a `PayableTarget`, and the pay/probe entrypoints accept nothing
12
+ * else — so a caller that skips the check does not compile.
13
+ *
14
+ * Scope, deliberately narrow: this module owns ENDPOINT IDENTITY — host:port must equal
15
+ * the configured gate, because a different port is a different service and must not
16
+ * satisfy the pin. That is the whole of its authority.
17
+ *
18
+ * It does NOT own domain policy. An allowlist names domains ("inneraxiom.com"), and
19
+ * which of them are payable — along with deny lists, per-domain caps, the kill switch
20
+ * and human approval — belongs to `spendGate`, the one shared evaluator. Stating an
21
+ * allowlist therefore REPLACES the gate pin rather than stacking with it: the operator
22
+ * has named a boundary, so identity steps aside and spendGate adjudicates. Checking it
23
+ * in both places would recreate the very divergence this module exists to end.
24
+ *
25
+ * So: "whose origin, by identity" here; "which domains, at what price" there. Neither
26
+ * answers the other's question, and neither has a second implementation.
27
+ */
28
+ /** Lowercased host INCLUDING port — endpoint identity. */
29
+ function hostOf(u) {
30
+ try {
31
+ return new URL(u).host.toLowerCase();
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ /**
38
+ * Decide whether `target` may be quoted or paid, and if so hand back the one token
39
+ * the pay path accepts. Exact-match only at both levels: a subdomain is a different
40
+ * host and is never implied by its parent, because "*.example" trust is a decision an
41
+ * operator should have to write down.
42
+ */
43
+ export function authorizeOrigin(req) {
44
+ const { target, gate, allowDomains } = req;
45
+ const host = hostOf(target);
46
+ if (host === null)
47
+ return { ok: false, code: "invalid-target", refusal: `"${target}" is not a valid URL.` };
48
+ // The gate satisfies the pin on endpoint identity, when one is configured.
49
+ if (gate !== undefined) {
50
+ const gateHost = hostOf(gate);
51
+ if (gateHost === null) {
52
+ return {
53
+ ok: false,
54
+ code: "invalid-gate",
55
+ refusal: `the server's configured gate ("${gate}") is not a valid URL — fix TOLLGATE_URL.`,
56
+ };
57
+ }
58
+ if (host === gateHost)
59
+ return { ok: true, target: target };
60
+ }
61
+ // A stated domain boundary REPLACES the gate pin rather than stacking with it. Which
62
+ // domains are payable is `spendGate`'s question, and it already answers it; answering it
63
+ // here as well would be the second copy this module exists to prevent. So when the
64
+ // operator has named a boundary, identity defers and spendGate adjudicates.
65
+ //
66
+ // An empty array counts as "stated" for the same reason: spendGate reads an empty
67
+ // allowlist as deny-by-default, so deferring is strictly equivalent and keeps one owner.
68
+ if (allowDomains !== undefined)
69
+ return { ok: true, target: target };
70
+ // No gate configured and no allowlist stated ⇒ there is no identity boundary to apply,
71
+ // so defer rather than invent one. Refusing here would read as safer and is not: it
72
+ // would break the deny-list and per-domain-cap paths, which legitimately run with no
73
+ // gateBase and rely on spendGate alone.
74
+ //
75
+ // "A deployment that enables buying must configure a gate" is a real requirement, but it
76
+ // is a CONFIG-time one — a process that boots without it should fail to start, not
77
+ // discover it per-candidate at pay time. That check belongs in the server's config
78
+ // validation, not here.
79
+ if (gate === undefined)
80
+ return { ok: true, target: target };
81
+ return {
82
+ ok: false,
83
+ code: "off-gate",
84
+ refusal: `refusing to touch ${host}: this server only quotes and pays at its configured gate (${hostOf(gate)}). ` +
85
+ `Pass the slug instead of an off-gate url. The gate is server-config and cannot be changed from a tool.`,
86
+ };
87
+ }
88
+ //# sourceMappingURL=origin-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin-policy.js","sourceRoot":"","sources":["../src/origin-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAsCH,0DAA0D;AAC1D,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,GAAkB;IAChD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC;IAE3C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,IAAI,MAAM,uBAAuB,EAAE,CAAC;IAE5G,2EAA2E;IAC3E,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,kCAAkC,IAAI,2CAA2C;aAC3F,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAuB,EAAE,CAAC;IAC9E,CAAC;IAED,qFAAqF;IACrF,yFAAyF;IACzF,mFAAmF;IACnF,4EAA4E;IAC5E,EAAE;IACF,kFAAkF;IAClF,yFAAyF;IACzF,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAuB,EAAE,CAAC;IAErF,uFAAuF;IACvF,oFAAoF;IACpF,qFAAqF;IACrF,wCAAwC;IACxC,EAAE;IACF,yFAAyF;IACzF,mFAAmF;IACnF,mFAAmF;IACnF,wBAAwB;IACxB,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAuB,EAAE,CAAC;IAE7E,OAAO;QACL,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,UAAU;QAChB,OAAO,EACL,qBAAqB,IAAI,8DAA8D,MAAM,CAAC,IAAI,CAAC,KAAK;YACxG,wGAAwG;KAC3G,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The paid-fetch loop shared by every buyer rail: probe → re-quote-and-abort-if-moved →
3
+ * build the payment-signature (the ONE rail-specific step) → paid GET → classify. Extracted
4
+ * from memoBuyer/gatewayBuyer so a third buyer (railBuyer) reuses it without a third copy of
5
+ * the loop. Behaviour is byte-identical to the two originals (their suites are the regression).
6
+ */
7
+ import { classifyPaymentError, classifySignerRefusal, type Fetched, type PayGuard, type Quoted } from "./buyer.ts";
8
+ /** Build the base64 `payment-signature` value for a quote — the single step the rails diverge on. */
9
+ export type BuildPayment = (quoted: Quoted, nowMs: number) => Promise<string>;
10
+ /** Classify a throw from `buildPayment` into a typed Fetched — the rail's catch-branch. */
11
+ export type OnSignError = (error: string) => Fetched;
12
+ export declare function runPaidFetch(url: string, kind: "read" | "citation", address: `0x${string}`, guard: PayGuard | undefined, buildPayment: BuildPayment, onSignError: OnSignError): Promise<Fetched>;
13
+ export { classifyPaymentError, classifySignerRefusal };
14
+ //# sourceMappingURL=paidFetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paidFetch.d.ts","sourceRoot":"","sources":["../src/paidFetch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAEL,oBAAoB,EACpB,qBAAqB,EAKrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAGpB,qGAAqG;AACrG,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE9E,2FAA2F;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAErD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GAAG,UAAU,EACzB,OAAO,EAAE,KAAK,MAAM,EAAE,EACtB,KAAK,EAAE,QAAQ,GAAG,SAAS,EAC3B,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,OAAO,CAAC,CA2ElB;AAID,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The paid-fetch loop shared by every buyer rail: probe → re-quote-and-abort-if-moved →
3
+ * build the payment-signature (the ONE rail-specific step) → paid GET → classify. Extracted
4
+ * from memoBuyer/gatewayBuyer so a third buyer (railBuyer) reuses it without a third copy of
5
+ * the loop. Behaviour is byte-identical to the two originals (their suites are the regression).
6
+ */
7
+ import { AGENT_UA, classifyPaymentError, classifySignerRefusal, probe, probeFailure, quotedTotalAtomic, tollMovedOrNull, } from "./buyer.js";
8
+ import { agentFetch } from "./sign.js";
9
+ export async function runPaidFetch(url, kind, address, guard, buildPayment, onSignError) {
10
+ const outcome = await probe(url, kind, address);
11
+ if (outcome.status !== "gated")
12
+ return probeFailure(outcome, url);
13
+ const quoted = outcome.quoted;
14
+ // Re-quote at pay time and abort if the toll moved past the authorized ceiling.
15
+ const moved = tollMovedOrNull(quoted, guard);
16
+ if (moved)
17
+ return moved;
18
+ // Signing and the paid fetch are wrapped SEPARATELY, because their non-spend failures classify
19
+ // differently: a build/sign throw is rail-specific (a hosted session signer THROWS a coded grant
20
+ // refusal → needs_topup; a config/payload fault → the rail's fallback, memo→origin_error,
21
+ // gateway→classifyPaymentError), while a paid-GET socket throw (DNS/connection-refused) is
22
+ // rail-agnostic — always a retryable origin_error. Keeping them in one try (as a naive extract
23
+ // would) routes a socket error through the sign classifier, which mislabels gateway's ECONNREFUSED
24
+ // as `rejected` instead of `origin_error`. Neither must escape as a raw rejection: the host's retry
25
+ // loop only ever inspects a resolved Fetched.
26
+ const nowMs = Date.now();
27
+ let paymentSignature;
28
+ try {
29
+ paymentSignature = await buildPayment(quoted, nowMs);
30
+ }
31
+ catch (err) {
32
+ return onSignError(err instanceof Error ? err.message : String(err));
33
+ }
34
+ let res;
35
+ try {
36
+ res = await agentFetch(url, {
37
+ headers: {
38
+ "user-agent": AGENT_UA,
39
+ "x-naulon-agent": address,
40
+ "x-naulon-kind": kind,
41
+ "payment-signature": paymentSignature,
42
+ },
43
+ });
44
+ }
45
+ catch (err) {
46
+ const error = err instanceof Error ? err.message : String(err);
47
+ return { ok: false, error, errorCode: "origin_error", retryable: true };
48
+ }
49
+ if (res.status === 402) {
50
+ const body = (await res.json().catch(() => ({})));
51
+ const error = body.error ?? "payment rejected";
52
+ return { ok: false, error, ...classifyPaymentError(error) };
53
+ }
54
+ if (!res.ok)
55
+ return { ok: false, error: `origin returned ${res.status}`, errorCode: "origin_error", retryable: true };
56
+ let settlementRef;
57
+ const respHeader = res.headers.get("payment-response");
58
+ if (respHeader) {
59
+ try {
60
+ settlementRef = JSON.parse(Buffer.from(respHeader, "base64").toString("utf8")).transaction;
61
+ }
62
+ catch {
63
+ /* ignore */
64
+ }
65
+ }
66
+ const license = res.headers.get("x-naulon-license") ?? undefined;
67
+ // The gate settles the payment BEFORE it writes the 200 body, so by here the money has
68
+ // already moved. If reading the body throws (a mid-stream socket reset — a routine fetch
69
+ // failure), it is NOT safe to retry: a fresh pay would mint a second valid authorization
70
+ // and double-charge. Surface a distinct, non-retryable `settlement_ambiguous` instead of
71
+ // letting the raw rejection escape (the host loop only ever inspects a resolved Fetched).
72
+ let content;
73
+ try {
74
+ content = await res.text();
75
+ }
76
+ catch (err) {
77
+ const error = err instanceof Error ? err.message : String(err);
78
+ return {
79
+ ok: false,
80
+ errorCode: "settlement_ambiguous",
81
+ retryable: false,
82
+ error: `paid read: payment was sent and likely settled, but reading the response body failed (${error}). Do NOT blind-retry — verify via settlementRef / a held license before paying again.`,
83
+ };
84
+ }
85
+ // costUsdc is the TRUE total the buyer authorized (all legs), so callers debit budgets on
86
+ // it — not paidUsdc (author leg only), which under-counts a fee'd toll.
87
+ const costUsdc = Number(quotedTotalAtomic(quoted)) / 1_000_000;
88
+ return { ok: true, content, settlementRef, paidUsdc: quoted.priceUsdc, costUsdc, license };
89
+ }
90
+ // Re-export the two classifiers so a rail's `onSignError` can build the same typed result the
91
+ // memo/gateway catch-branches do without a second import site. (They live in buyer.ts.)
92
+ export { classifyPaymentError, classifySignerRefusal };
93
+ //# sourceMappingURL=paidFetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paidFetch.js","sourceRoot":"","sources":["../src/paidFetch.ts"],"names":[],"mappings":"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"}
package/dist/pay.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { type Buyer } from "./buyer.ts";
2
+ export declare function mockBuyer(): Buyer;
3
+ //# sourceMappingURL=pay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.d.ts","sourceRoot":"","sources":["../src/pay.ts"],"names":[],"mappings":"AAQA,OAAO,EASL,KAAK,KAAK,EAIX,MAAM,YAAY,CAAC;AAEpB,wBAAgB,SAAS,IAAI,KAAK,CAoEjC"}
package/dist/pay.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Mock buyer — exercises the full x402 protocol offline. It speaks the same
3
+ * header contract as a real client (probe → PAYMENT-REQUIRED → payment-signature
4
+ * → PAYMENT-RESPONSE), but signs a simple offline signature the mock tollgate
5
+ * accepts, so the whole loop runs with no chain, wallet, or Circle access.
6
+ */
7
+ import { agentFetch } from "./sign.js";
8
+ import { getWallet } from "./wallet.js";
9
+ import { AGENT_UA, assemblePayment, classifyPaymentError, probe, probeFailure, probePrice, quotedTotalAtomic, tollMovedOrNull, } from "./buyer.js";
10
+ export function mockBuyer() {
11
+ const wallet = getWallet();
12
+ return {
13
+ address: wallet.address,
14
+ async init() {
15
+ /* nothing to fund offline */
16
+ },
17
+ price(url, kind) {
18
+ return probePrice(url, kind, wallet.address);
19
+ },
20
+ async fetch(url, kind, guard) {
21
+ const outcome = await probe(url, kind, wallet.address);
22
+ if (outcome.status !== "gated")
23
+ return probeFailure(outcome, url);
24
+ const quoted = outcome.quoted;
25
+ // Re-quote at pay time and abort if the toll moved past the authorized ceiling.
26
+ const moved = tollMovedOrNull(quoted, guard);
27
+ if (moved)
28
+ return moved;
29
+ // One offline signature per advertised leg (operator fee → 2-leg array); a stock
30
+ // single-author quote stays the bare object assemblePayment emits for one leg.
31
+ const paymentSignature = await assemblePayment(quoted, (req, nonce) => ({
32
+ payer: wallet.address,
33
+ amount: req.amount,
34
+ nonce,
35
+ }));
36
+ const res = await agentFetch(url, {
37
+ headers: {
38
+ "user-agent": AGENT_UA,
39
+ "x-naulon-agent": wallet.address,
40
+ "x-naulon-kind": kind,
41
+ "payment-signature": paymentSignature,
42
+ },
43
+ });
44
+ if (res.status === 402) {
45
+ const body = (await res.json().catch(() => ({})));
46
+ const error = body.error ?? "payment rejected";
47
+ return { ok: false, error, ...classifyPaymentError(error) };
48
+ }
49
+ if (!res.ok)
50
+ return { ok: false, error: `origin returned ${res.status}`, errorCode: "origin_error", retryable: true };
51
+ let settlementRef;
52
+ const respHeader = res.headers.get("payment-response");
53
+ if (respHeader) {
54
+ try {
55
+ settlementRef = JSON.parse(Buffer.from(respHeader, "base64").toString("utf8")).transaction;
56
+ }
57
+ catch {
58
+ /* ignore */
59
+ }
60
+ }
61
+ const license = res.headers.get("x-naulon-license") ?? undefined;
62
+ // Mirror paidFetch: a body-read throw after the 200 is settlement-ambiguous, not a
63
+ // safe retry. costUsdc carries the true total (all legs) for correct budget debit.
64
+ let content;
65
+ try {
66
+ content = await res.text();
67
+ }
68
+ catch (err) {
69
+ const error = err instanceof Error ? err.message : String(err);
70
+ return {
71
+ ok: false,
72
+ errorCode: "settlement_ambiguous",
73
+ retryable: false,
74
+ error: `paid read: payment sent but reading the response body failed (${error}). Do NOT blind-retry.`,
75
+ };
76
+ }
77
+ const costUsdc = Number(quotedTotalAtomic(quoted)) / 1_000_000;
78
+ return { ok: true, content, settlementRef, paidUsdc: quoted.priceUsdc, costUsdc, license };
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=pay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.js","sourceRoot":"","sources":["../src/pay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,QAAQ,EACR,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,eAAe,GAKhB,MAAM,YAAY,CAAC;AAEpB,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,CAAC,IAAI;YACR,6BAA6B;QAC/B,CAAC;QACD,KAAK,CAAC,GAAG,EAAE,IAAI;YACb,OAAO,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,KAAgB;YACrC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO;gBAAE,OAAO,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAClE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,gFAAgF;YAChF,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC7C,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;YACxB,iFAAiF;YACjF,+EAA+E;YAC/E,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBACtE,KAAK,EAAE,MAAM,CAAC,OAAO;gBACrB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK;aACN,CAAC,CAAC,CAAC;YACJ,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE;gBAChC,OAAO,EAAE;oBACP,YAAY,EAAE,QAAQ;oBACtB,gBAAgB,EAAE,MAAM,CAAC,OAAO;oBAChC,eAAe,EAAE,IAAI;oBACrB,mBAAmB,EAAE,gBAAgB;iBACtC;aACF,CAAC,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAuB,CAAC;gBACxE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,kBAAkB,CAAC;gBAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,GAAG,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YACtH,IAAI,aAAiC,CAAC;YACtC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;YACvD,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC;oBACH,aAAa,GAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAE5E,CAAC,WAAW,CAAC;gBACjB,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY;gBACd,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,SAAS,CAAC;YACjE,mFAAmF;YACnF,mFAAmF;YACnF,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC7B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC/D,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,SAAS,EAAE,sBAAsB;oBACjC,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,iEAAiE,KAAK,wBAAwB;iBACtG,CAAC;YACJ,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;YAC/D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QAC7F,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/pop.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { HeldLicense } from "./licenseStore.ts";
2
+ import type { AgentWallet } from "./wallet.ts";
3
+ /**
4
+ * Build a proof-of-possession header for a held license, or null if the wallet
5
+ * can't sign (no key) — in which case the caller must fall back to paying.
6
+ */
7
+ export declare function buildPopProof(held: HeldLicense, wallet: AgentWallet, now: number): Promise<string | null>;
8
+ //# sourceMappingURL=pop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pop.d.ts","sourceRoot":"","sources":["../src/pop.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
package/dist/pop.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Buyer-side holder-of-key proof. When the agent holds a cnf-bound license, a
3
+ * free re-read requires proving it controls the payer wallet: it signs a fresh
4
+ * challenge (EIP-191) over the same canonical bytes the gate reconstructs.
5
+ *
6
+ * The proof wire format is `<ts>.<nonce>.<sig>` (see tollgate/src/pop.ts). `ts`
7
+ * is the agent's current epoch-seconds (the gate checks it against a freshness
8
+ * window), `nonce` is a single-use random salt, `sig` is the wallet signature.
9
+ */
10
+ import { randomBytes } from "node:crypto";
11
+ import { popMessage } from "@naulon/shared";
12
+ /**
13
+ * Build a proof-of-possession header for a held license, or null if the wallet
14
+ * can't sign (no key) — in which case the caller must fall back to paying.
15
+ */
16
+ export async function buildPopProof(held, wallet, now) {
17
+ if (!wallet.signMessage)
18
+ return null;
19
+ const ts = Math.floor(now / 1000);
20
+ const nonce = randomBytes(16).toString("hex");
21
+ const message = popMessage({ aud: held.aud, jti: held.jti, slug: held.slug, ts, nonce });
22
+ const sig = await wallet.signMessage(message);
23
+ return `${ts}.${nonce}.${sig}`;
24
+ }
25
+ //# sourceMappingURL=pop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pop.js","sourceRoot":"","sources":["../src/pop.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI5C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAiB,EACjB,MAAmB,EACnB,GAAW;IAEX,IAAI,CAAC,MAAM,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACzF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,GAAG,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;AACjC,CAAC"}
package/dist/rail.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * railBuyer — a buyer that settles on whatever chain the TENANT's 402 advertises, not the fleet
3
+ * default. The gate stamps each tenant's rail into the 402 (RAS-B sell side: a gateway network sets
4
+ * extra.name 'GatewayWalletBatched'; a memo network does not). This buyer reads that and signs the
5
+ * matching envelope, so one buyer serves a mixed fleet (an Arc-default fleet with a Base tenant).
6
+ * Both signers wrap the SAME sealed session key (only the EIP-712 domain differs); the cloud injects
7
+ * both. Absent per-tenant divergence the 402 carries the fleet rail, so railBuyer picks the same
8
+ * builder activeNetwork() would have — byte-identical to memoBuyer/gatewayBuyer for a single-rail fleet.
9
+ */
10
+ import { type Buyer } from "./buyer.ts";
11
+ import { type MemoSigner } from "./memo.ts";
12
+ import { type GatewaySigner } from "./gateway.ts";
13
+ export interface RailSigners {
14
+ memo?: MemoSigner;
15
+ gateway?: GatewaySigner;
16
+ }
17
+ export declare function railBuyer(signers: RailSigners): Buyer;
18
+ //# sourceMappingURL=rail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail.d.ts","sourceRoot":"","sources":["../src/rail.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAKL,KAAK,KAAK,EAKX,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAgD,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhG,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAcD,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,KAAK,CA6CrD"}
package/dist/rail.js ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * railBuyer — a buyer that settles on whatever chain the TENANT's 402 advertises, not the fleet
3
+ * default. The gate stamps each tenant's rail into the 402 (RAS-B sell side: a gateway network sets
4
+ * extra.name 'GatewayWalletBatched'; a memo network does not). This buyer reads that and signs the
5
+ * matching envelope, so one buyer serves a mixed fleet (an Arc-default fleet with a Base tenant).
6
+ * Both signers wrap the SAME sealed session key (only the EIP-712 domain differs); the cloud injects
7
+ * both. Absent per-tenant divergence the 402 carries the fleet rail, so railBuyer picks the same
8
+ * builder activeNetwork() would have — byte-identical to memoBuyer/gatewayBuyer for a single-rail fleet.
9
+ */
10
+ import { assemblePayment, classifyPaymentError, classifySignerRefusal, probe, } from "./buyer.js";
11
+ import { runPaidFetch } from "./paidFetch.js";
12
+ import { memoLegPayload } from "./memo.js";
13
+ import { gatewayLegPayload } from "./gateway.js";
14
+ import { networkByCaip2, supportsMemo } from "@naulon/shared";
15
+ /** Pick the rail the GATE will settle this 402 on. Registry first: a known CAIP-2 network uses the
16
+ * same predicate the gate's verifyAndSettle uses (`supportsMemo` — memo self-relay vs Circle
17
+ * Gateway), so buyer and gate can never disagree. The `extra.name 'GatewayWalletBatched'` tell is
18
+ * only the fallback for a network we don't know: build402 stamps that descriptor on EVERY
19
+ * gateway-mode 402 — memo chains included — so on a known memo network it is noise, not a signal
20
+ * (trusting it signed the Gateway envelope against Arc's memo settle: "malformed memo payload"). */
21
+ function isGateway402(quoted) {
22
+ const net = networkByCaip2(quoted.requirements.network);
23
+ if (net)
24
+ return !supportsMemo(net);
25
+ return quoted.requirements.extra?.name === "GatewayWalletBatched";
26
+ }
27
+ export function railBuyer(signers) {
28
+ const address = (signers.memo?.address ?? signers.gateway?.address ?? "0x");
29
+ return {
30
+ address,
31
+ async init() {
32
+ // Injected-signer buyer: no deposit, custody-free (mirrors memoBuyer/gatewayBuyer init).
33
+ },
34
+ price(url, kind) {
35
+ return probe(url, kind, address).then((o) => (o.status === "gated" ? o.quoted : null));
36
+ },
37
+ async fetch(url, kind, guard) {
38
+ const buildPayment = async (quoted, nowMs) => {
39
+ if (isGateway402(quoted)) {
40
+ if (!signers.gateway) {
41
+ throw new Error("no gateway signer for a Circle Gateway 402 (this tenant settles on a memo-less chain)");
42
+ }
43
+ // gatewayLegPayload owns the N-leg + GatewayWalletBatched guards, so the mixed-fleet
44
+ // rail gets the same loud, actionable refusals gatewayBuyer gives (it previously had
45
+ // neither, silently dropping extra legs until the gate rejected on leg-count mismatch).
46
+ const payload = await gatewayLegPayload(signers.gateway, quoted, 2);
47
+ return Buffer.from(JSON.stringify(payload)).toString("base64");
48
+ }
49
+ if (!signers.memo) {
50
+ throw new Error("no memo signer for a memo-rail 402 (this tenant settles on a memo chain)");
51
+ }
52
+ // Resolve the memo chain from the 402's advertised network so a non-fleet memo chain signs
53
+ // against the right USDC domain (registry: memo = arcTestnet today, but keep it honest). An
54
+ // unknown CAIP-2 falls back to activeNetwork() inside memoLegPayload (undefined triggers the default).
55
+ const net = networkByCaip2(quoted.requirements.network);
56
+ const memo = signers.memo;
57
+ return assemblePayment(quoted, (req) => memoLegPayload(req, nowMs, memo, net));
58
+ };
59
+ const onSignError = (error) => {
60
+ // Parity with memoBuyer/gatewayBuyer: a hosted session signer throws a coded refusal
61
+ // (grant exhausted/expired/no session) → typed so the agent can act; any other throw (incl.
62
+ // a "no <rail> signer" config fault) → classifyPaymentError. A socket error never reaches
63
+ // here — the shared loop classifies the paid GET as a rail-agnostic origin_error itself.
64
+ const refusal = classifySignerRefusal(error);
65
+ return refusal
66
+ ? { ok: false, error, ...refusal }
67
+ : { ok: false, error, ...classifyPaymentError(error) };
68
+ };
69
+ return runPaidFetch(url, kind, address, guard, buildPayment, onSignError);
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=rail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail.js","sourceRoot":"","sources":["../src/rail.ts"],"names":[],"mappings":"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"}
package/dist/rss.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * RSS parsing — turn a publisher's `/rss.xml` into discovery candidates.
3
+ *
4
+ * Pure and dependency-free: a small RSS 2.0 reader, no XML library. The agent
5
+ * reads only the free teaser fields here (it hasn't paid yet), so the parser
6
+ * deliberately distinguishes the teaser (`<description>`/`<summary>`) from the
7
+ * full body (`<content:encoded>`) — see `extractTeaser`.
8
+ */
9
+ import type { Candidate } from "./types.ts";
10
+ /** One `<item>` lifted from the feed, before we decide what's free to read. */
11
+ export interface RssItem {
12
+ title: string;
13
+ link: string;
14
+ /** RSS `<description>` — conventionally a teaser/lede, not the full body. */
15
+ description: string;
16
+ /** `<content:encoded>` — may be the FULL article body. Never a teaser. */
17
+ contentEncoded?: string;
18
+ guid?: string;
19
+ }
20
+ /** Parse an RSS 2.0 (or Atom-ish) document into its items. Lenient by design. */
21
+ export declare function parseRss(xml: string): RssItem[];
22
+ /** Slug = the last non-empty path segment of the item's link (or guid). */
23
+ export declare function slugFromLink(link: string): string;
24
+ /**
25
+ * The teaser boundary — what the agent may read for FREE, pre-payment.
26
+ *
27
+ * This is a product decision, not a parsing detail: a feed that ships the full
28
+ * body in `<content:encoded>` would let the agent bypass the toll at discovery.
29
+ * So we read the teaser field only and never the body, regardless of what the
30
+ * feed includes. Keeping it a named function makes that line explicit and easy
31
+ * to retune per publisher.
32
+ */
33
+ export declare function extractTeaser(item: RssItem): string;
34
+ /** Map a parsed item to a free-teaser Candidate (slug + title + teaser). */
35
+ export declare function rssItemToCandidate(item: RssItem): Candidate;
36
+ /** Full feed → candidates, dropping anything with no usable slug. */
37
+ export declare function rssToCandidates(xml: string): Candidate[];
38
+ //# sourceMappingURL=rss.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../src/rss.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,+EAA+E;AAC/E,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA+CD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE,CAgB/C;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAS3D;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAIxD"}