@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.
- package/README.md +39 -0
- package/dist/agent.d.ts +77 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +235 -0
- package/dist/agent.js.map +1 -0
- package/dist/allocation.d.ts +36 -0
- package/dist/allocation.d.ts.map +1 -0
- package/dist/allocation.js +45 -0
- package/dist/allocation.js.map +1 -0
- package/dist/appraise.d.ts +3 -0
- package/dist/appraise.d.ts.map +1 -0
- package/dist/appraise.js +60 -0
- package/dist/appraise.js.map +1 -0
- package/dist/buyer.d.ts +195 -0
- package/dist/buyer.d.ts.map +1 -0
- package/dist/buyer.js +254 -0
- package/dist/buyer.js.map +1 -0
- package/dist/decide.d.ts +115 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +206 -0
- package/dist/decide.js.map +1 -0
- package/dist/discover.d.ts +10 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +5 -0
- package/dist/discover.js.map +1 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +93 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gateway.d.ts +117 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +187 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +40 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +44 -0
- package/dist/lib.js.map +1 -0
- package/dist/licenseStore.d.ts +56 -0
- package/dist/licenseStore.d.ts.map +1 -0
- package/dist/licenseStore.js +79 -0
- package/dist/licenseStore.js.map +1 -0
- package/dist/memo.d.ts +43 -0
- package/dist/memo.d.ts.map +1 -0
- package/dist/memo.js +102 -0
- package/dist/memo.js.map +1 -0
- package/dist/origin-policy.d.ts +74 -0
- package/dist/origin-policy.d.ts.map +1 -0
- package/dist/origin-policy.js +88 -0
- package/dist/origin-policy.js.map +1 -0
- package/dist/paidFetch.d.ts +14 -0
- package/dist/paidFetch.d.ts.map +1 -0
- package/dist/paidFetch.js +93 -0
- package/dist/paidFetch.js.map +1 -0
- package/dist/pay.d.ts +3 -0
- package/dist/pay.d.ts.map +1 -0
- package/dist/pay.js +82 -0
- package/dist/pay.js.map +1 -0
- package/dist/pop.d.ts +8 -0
- package/dist/pop.d.ts.map +1 -0
- package/dist/pop.js +25 -0
- package/dist/pop.js.map +1 -0
- package/dist/rail.d.ts +18 -0
- package/dist/rail.d.ts.map +1 -0
- package/dist/rail.js +73 -0
- package/dist/rail.js.map +1 -0
- package/dist/rss.d.ts +38 -0
- package/dist/rss.d.ts.map +1 -0
- package/dist/rss.js +110 -0
- package/dist/rss.js.map +1 -0
- package/dist/sign.d.ts +13 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +52 -0
- package/dist/sign.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wallet.d.ts +12 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +46 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +42 -0
package/dist/memo.js.map
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/pay.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pay.js","sourceRoot":"","sources":["../src/pay.ts"],"names":[],"mappings":"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"}
|
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
|
package/dist/pop.js.map
ADDED
|
@@ -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
|
package/dist/rail.js.map
ADDED
|
@@ -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"}
|