@absol-labs/agent 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -23
- package/dist/capability/invocation-capability.d.ts +18 -6
- package/dist/capability/invocation-capability.d.ts.map +1 -1
- package/dist/capability/invocation-capability.js.map +1 -1
- package/dist/cli/commands/claim.d.ts.map +1 -1
- package/dist/cli/commands/claim.js +20 -4
- package/dist/cli/commands/claim.js.map +1 -1
- package/dist/cli/commands/close.d.ts.map +1 -1
- package/dist/cli/commands/close.js +16 -4
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/invoke.d.ts.map +1 -1
- package/dist/cli/commands/invoke.js +1 -0
- package/dist/cli/commands/invoke.js.map +1 -1
- package/dist/cli/commands/open.d.ts.map +1 -1
- package/dist/cli/commands/open.js +63 -11
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
- package/dist/cli/commands/reclaim-unverified.js +12 -4
- package/dist/cli/commands/reclaim-unverified.js.map +1 -1
- package/dist/cli/commands/reclaim.d.ts.map +1 -1
- package/dist/cli/commands/reclaim.js +16 -5
- package/dist/cli/commands/reclaim.js.map +1 -1
- package/dist/cli/commands/streams.js +1 -1
- package/dist/cli/commands/streams.js.map +1 -1
- package/dist/cli/deps.d.ts +53 -7
- package/dist/cli/deps.d.ts.map +1 -1
- package/dist/cli/deps.js +65 -15
- package/dist/cli/deps.js.map +1 -1
- package/dist/cli/privy.d.ts +83 -0
- package/dist/cli/privy.d.ts.map +1 -0
- package/dist/cli/privy.js +230 -0
- package/dist/cli/privy.js.map +1 -0
- package/dist/cli/spend-policy.d.ts +60 -0
- package/dist/cli/spend-policy.d.ts.map +1 -0
- package/dist/cli/spend-policy.js +65 -0
- package/dist/cli/spend-policy.js.map +1 -0
- package/dist/cli/wallet.d.ts +108 -39
- package/dist/cli/wallet.d.ts.map +1 -1
- package/dist/cli/wallet.js +108 -43
- package/dist/cli/wallet.js.map +1 -1
- package/dist/gateway/caller-auth-gateway.d.ts +38 -3
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
- package/dist/gateway/caller-auth-gateway.js +25 -1
- package/dist/gateway/caller-auth-gateway.js.map +1 -1
- package/dist/gateway/http-server.d.ts +53 -0
- package/dist/gateway/http-server.d.ts.map +1 -1
- package/dist/gateway/http-server.js +189 -4
- package/dist/gateway/http-server.js.map +1 -1
- package/dist/gateway/x402-mode.d.ts +123 -0
- package/dist/gateway/x402-mode.d.ts.map +1 -0
- package/dist/gateway/x402-mode.js +212 -0
- package/dist/gateway/x402-mode.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/mandates/env.d.ts +3 -2
- package/dist/mandates/env.d.ts.map +1 -1
- package/dist/mandates/env.js +3 -2
- package/dist/mandates/env.js.map +1 -1
- package/dist/mcp/server.d.ts +49 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +513 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/service-invoker.d.ts +97 -0
- package/dist/mcp/service-invoker.d.ts.map +1 -0
- package/dist/mcp/service-invoker.js +167 -0
- package/dist/mcp/service-invoker.js.map +1 -0
- package/dist/sdk/invoke.d.ts +19 -4
- package/dist/sdk/invoke.d.ts.map +1 -1
- package/dist/sdk/invoke.js +69 -5
- package/dist/sdk/invoke.js.map +1 -1
- package/dist/x402/delivery-oracle.d.ts +108 -0
- package/dist/x402/delivery-oracle.d.ts.map +1 -0
- package/dist/x402/delivery-oracle.js +174 -0
- package/dist/x402/delivery-oracle.js.map +1 -0
- package/dist/x402/facilitator-server-entry.d.ts +3 -0
- package/dist/x402/facilitator-server-entry.d.ts.map +1 -0
- package/dist/x402/facilitator-server-entry.js +27 -0
- package/dist/x402/facilitator-server-entry.js.map +1 -0
- package/dist/x402/facilitator-server.d.ts +73 -0
- package/dist/x402/facilitator-server.d.ts.map +1 -0
- package/dist/x402/facilitator-server.js +305 -0
- package/dist/x402/facilitator-server.js.map +1 -0
- package/dist/x402/facilitator.d.ts +69 -17
- package/dist/x402/facilitator.d.ts.map +1 -1
- package/dist/x402/facilitator.js +76 -33
- package/dist/x402/facilitator.js.map +1 -1
- package/dist/x402/payment-requirements.d.ts +106 -0
- package/dist/x402/payment-requirements.d.ts.map +1 -0
- package/dist/x402/payment-requirements.js +138 -0
- package/dist/x402/payment-requirements.js.map +1 -0
- package/dist/x402/relayer.d.ts +41 -0
- package/dist/x402/relayer.d.ts.map +1 -0
- package/dist/x402/relayer.js +64 -0
- package/dist/x402/relayer.js.map +1 -0
- package/dist/x402/resource-index.d.ts +75 -0
- package/dist/x402/resource-index.d.ts.map +1 -0
- package/dist/x402/resource-index.js +126 -0
- package/dist/x402/resource-index.js.map +1 -0
- package/dist/x402/verified-delivery-facilitator.d.ts +285 -0
- package/dist/x402/verified-delivery-facilitator.d.ts.map +1 -0
- package/dist/x402/verified-delivery-facilitator.js +423 -0
- package/dist/x402/verified-delivery-facilitator.js.map +1 -0
- package/dist/zktls/t2-delivery-proof.d.ts +6 -6
- package/docs/agent-layer.md +12 -8
- package/docs/cli.md +158 -22
- package/docs/mcp-hosted.md +4 -3
- package/docs/mcp-reference.md +175 -0
- package/docs/quickstart.md +18 -4
- package/docs/threat-model.md +4 -3
- package/docs/x402.md +648 -0
- package/package.json +18 -7
- package/src/capability/invocation-capability.ts +17 -1
- package/src/cli/commands/claim.ts +30 -4
- package/src/cli/commands/close.ts +20 -4
- package/src/cli/commands/invoke.ts +1 -0
- package/src/cli/commands/open.ts +76 -10
- package/src/cli/commands/reclaim-unverified.ts +16 -4
- package/src/cli/commands/reclaim.ts +22 -5
- package/src/cli/commands/streams.ts +1 -1
- package/src/cli/deps.ts +115 -24
- package/src/cli/privy.ts +396 -0
- package/src/cli/spend-policy.ts +139 -0
- package/src/cli/wallet.ts +188 -57
- package/src/gateway/caller-auth-gateway.ts +77 -6
- package/src/gateway/http-server.ts +280 -6
- package/src/gateway/x402-mode.ts +350 -0
- package/src/index.ts +40 -5
- package/src/mandates/env.ts +3 -2
- package/src/mcp/server.ts +681 -29
- package/src/mcp/service-invoker.ts +285 -0
- package/src/sdk/invoke.ts +90 -12
- package/src/x402/delivery-oracle.ts +285 -0
- package/src/x402/facilitator-server-entry.ts +35 -0
- package/src/x402/facilitator-server.ts +445 -0
- package/src/x402/facilitator.ts +125 -46
- package/src/x402/payment-requirements.ts +231 -0
- package/src/x402/relayer.ts +122 -0
- package/src/x402/resource-index.ts +228 -0
- package/src/x402/verified-delivery-facilitator.ts +744 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discoverServices,
|
|
3
|
+
type DiscoverServicesOptions,
|
|
4
|
+
type ServiceListing,
|
|
5
|
+
} from "../discovery/registry.js";
|
|
6
|
+
import type { SignedListingPayee } from "./payment-requirements.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Maps an x402 `PaymentRequirements.resource` URL onto the Metrik listing that
|
|
10
|
+
* serves it — the step that decides whether Metrik has any business having an
|
|
11
|
+
* opinion about a payment at all.
|
|
12
|
+
*
|
|
13
|
+
* ## Derived, never supplied
|
|
14
|
+
*
|
|
15
|
+
* The only thing taken from the request is the `resource` URL itself, because
|
|
16
|
+
* the x402 wire format gives the facilitator nothing else to key on. Everything
|
|
17
|
+
* the verdict is actually computed from — which listing that URL belongs to, the
|
|
18
|
+
* operator address on it, and the delivery record for it — is resolved HERE,
|
|
19
|
+
* from Metrik's own verified view of the registry. In particular the
|
|
20
|
+
* `extra.metrikServiceRef` hint that `buildMetrikPaymentRequirements` publishes
|
|
21
|
+
* is deliberately ignored: it rides inside a caller-supplied body, and reading
|
|
22
|
+
* it would let a caller choose which service's delivery record gets checked.
|
|
23
|
+
*
|
|
24
|
+
* ## Origin matching, and why not path matching
|
|
25
|
+
*
|
|
26
|
+
* A listing owns an ORIGIN (scheme + host + port). Matching on origin means a
|
|
27
|
+
* seller does not have to re-register every route, and it cannot over-reach: two
|
|
28
|
+
* listings never share an origin, because the origin is what the oracle probes
|
|
29
|
+
* and what `serviceRef` is derived from. A resource whose origin matches no
|
|
30
|
+
* listing is simply not ours.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export interface ResolvedMetrikResource extends SignedListingPayee {
|
|
34
|
+
/** The listing, as verified by `discoverServices` (EIP-712 signer recovered). */
|
|
35
|
+
readonly listing: ServiceListing;
|
|
36
|
+
/** Which of the listing's signed origins the resource matched. */
|
|
37
|
+
readonly matchedOrigin: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type ResourceIndexStatus =
|
|
41
|
+
| "resolved"
|
|
42
|
+
| "not-listed"
|
|
43
|
+
/**
|
|
44
|
+
* Two or more signed listings claim this origin, so there is no single right
|
|
45
|
+
* answer about whose delivery record applies.
|
|
46
|
+
*
|
|
47
|
+
* Picking one — which "first claimant wins" did — is the origin-squatting
|
|
48
|
+
* primitive: a second listing claiming a victim's origin could take over the
|
|
49
|
+
* victim's gate and permanently refuse their payments. Not exploitable while
|
|
50
|
+
* listing insertion requires an operator-held key, but "unresolvable" costs
|
|
51
|
+
* nothing and removes the primitive before registration ever opens.
|
|
52
|
+
*/
|
|
53
|
+
| "ambiguous"
|
|
54
|
+
| "registry-unavailable";
|
|
55
|
+
|
|
56
|
+
export interface ResourceLookup {
|
|
57
|
+
readonly status: ResourceIndexStatus;
|
|
58
|
+
readonly resource?: ResolvedMetrikResource;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface MetrikResourceIndex {
|
|
62
|
+
/** Never throws: an unreachable registry is a status, not an exception. */
|
|
63
|
+
lookup(resourceUrl: string): Promise<ResourceLookup>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** How long a registry snapshot is reused before a refresh is attempted. */
|
|
67
|
+
export const DEFAULT_RESOURCE_INDEX_REFRESH_MS = 60_000;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* How long a stale snapshot keeps being served after refreshes start failing.
|
|
71
|
+
* Serving a stale listing is safe in a way that serving a stale DELIVERY verdict
|
|
72
|
+
* is not: listings change on a human timescale (a new signed descriptor), and
|
|
73
|
+
* the fields used from them — `operator`, `serviceRef` — are the stable ones. The
|
|
74
|
+
* live verdict is always fetched fresh by the delivery oracle.
|
|
75
|
+
*/
|
|
76
|
+
export const DEFAULT_RESOURCE_INDEX_MAX_STALE_MS = 15 * 60_000;
|
|
77
|
+
|
|
78
|
+
export interface RegistryResourceIndexOptions {
|
|
79
|
+
readonly discover?: () => Promise<readonly ServiceListing[]>;
|
|
80
|
+
readonly discoverOptions?: DiscoverServicesOptions;
|
|
81
|
+
readonly refreshMs?: number;
|
|
82
|
+
readonly maxStaleMs?: number;
|
|
83
|
+
readonly now?: () => number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function createRegistryResourceIndex(
|
|
87
|
+
options: RegistryResourceIndexOptions = {},
|
|
88
|
+
): MetrikResourceIndex {
|
|
89
|
+
const refreshMs = options.refreshMs ?? DEFAULT_RESOURCE_INDEX_REFRESH_MS;
|
|
90
|
+
const maxStaleMs = options.maxStaleMs ?? DEFAULT_RESOURCE_INDEX_MAX_STALE_MS;
|
|
91
|
+
const now = options.now ?? (() => Date.now());
|
|
92
|
+
const discover =
|
|
93
|
+
options.discover ??
|
|
94
|
+
(() =>
|
|
95
|
+
discoverServices(options.discoverOptions ?? {}) as Promise<
|
|
96
|
+
readonly ServiceListing[]
|
|
97
|
+
>);
|
|
98
|
+
|
|
99
|
+
interface Snapshot {
|
|
100
|
+
readonly loadedAtMs: number;
|
|
101
|
+
readonly byOrigin: ReadonlyMap<string, ResolvedMetrikResource>;
|
|
102
|
+
readonly ambiguous: ReadonlySet<string>;
|
|
103
|
+
}
|
|
104
|
+
let snapshot: Snapshot | undefined;
|
|
105
|
+
let inFlight: Promise<Snapshot | undefined> | undefined;
|
|
106
|
+
|
|
107
|
+
async function refresh(): Promise<Snapshot | undefined> {
|
|
108
|
+
try {
|
|
109
|
+
const listings = await discover();
|
|
110
|
+
const byOrigin = new Map<string, ResolvedMetrikResource>();
|
|
111
|
+
const ambiguous = new Set<string>();
|
|
112
|
+
for (const listing of listings) {
|
|
113
|
+
for (const origin of signedOriginsOf(listing)) {
|
|
114
|
+
const claimed = byOrigin.get(origin);
|
|
115
|
+
if (claimed === undefined) {
|
|
116
|
+
byOrigin.set(origin, {
|
|
117
|
+
serviceRef: listing.serviceRef,
|
|
118
|
+
operator: listing.operator,
|
|
119
|
+
listing,
|
|
120
|
+
matchedOrigin: origin,
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// A DIFFERENT service claiming an origin another already claims makes
|
|
125
|
+
// the origin unresolvable, not first-come-first-served. See the
|
|
126
|
+
// `ambiguous` doc on `ResourceIndexStatus`. A listing re-claiming its
|
|
127
|
+
// OWN origin (e.g. publicUrl and baseUrl agreeing) is not a conflict.
|
|
128
|
+
if (
|
|
129
|
+
claimed.serviceRef.toLowerCase() !==
|
|
130
|
+
listing.serviceRef.toLowerCase()
|
|
131
|
+
) {
|
|
132
|
+
ambiguous.add(origin);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const origin of ambiguous) {
|
|
137
|
+
byOrigin.delete(origin);
|
|
138
|
+
}
|
|
139
|
+
return { loadedAtMs: now(), byOrigin, ambiguous };
|
|
140
|
+
} catch {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function currentSnapshot(): Promise<Snapshot | undefined> {
|
|
146
|
+
const cached = snapshot;
|
|
147
|
+
if (cached !== undefined && now() - cached.loadedAtMs < refreshMs) {
|
|
148
|
+
return cached;
|
|
149
|
+
}
|
|
150
|
+
inFlight ??= refresh().finally(() => {
|
|
151
|
+
inFlight = undefined;
|
|
152
|
+
});
|
|
153
|
+
const loaded = await inFlight;
|
|
154
|
+
if (loaded !== undefined) {
|
|
155
|
+
snapshot = loaded;
|
|
156
|
+
return loaded;
|
|
157
|
+
}
|
|
158
|
+
if (cached !== undefined && now() - cached.loadedAtMs <= maxStaleMs) {
|
|
159
|
+
return cached;
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
async lookup(resourceUrl) {
|
|
166
|
+
const origin = originOf(resourceUrl);
|
|
167
|
+
if (origin === undefined) {
|
|
168
|
+
return { status: "not-listed" };
|
|
169
|
+
}
|
|
170
|
+
const loaded = await currentSnapshot();
|
|
171
|
+
if (loaded === undefined) {
|
|
172
|
+
return { status: "registry-unavailable" };
|
|
173
|
+
}
|
|
174
|
+
if (loaded.ambiguous.has(origin)) {
|
|
175
|
+
return { status: "ambiguous" };
|
|
176
|
+
}
|
|
177
|
+
const resource = loaded.byOrigin.get(origin);
|
|
178
|
+
return resource === undefined
|
|
179
|
+
? { status: "not-listed" }
|
|
180
|
+
: { status: "resolved", resource };
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Every origin the SIGNED record claims for a listing. */
|
|
186
|
+
export function signedOriginsOf(listing: ServiceListing): readonly string[] {
|
|
187
|
+
const candidates: (string | undefined)[] = [
|
|
188
|
+
listing.publicUrl,
|
|
189
|
+
listing.accessUrl,
|
|
190
|
+
];
|
|
191
|
+
const descriptor = (
|
|
192
|
+
listing.signed as {
|
|
193
|
+
descriptor?: {
|
|
194
|
+
interface?: { baseUrl?: string };
|
|
195
|
+
callerAuth?: { accessUrl?: string };
|
|
196
|
+
publicUrl?: string;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
).descriptor;
|
|
200
|
+
if (descriptor !== undefined) {
|
|
201
|
+
candidates.push(
|
|
202
|
+
descriptor.publicUrl,
|
|
203
|
+
descriptor.interface?.baseUrl,
|
|
204
|
+
descriptor.callerAuth?.accessUrl,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const origins = new Set<string>();
|
|
208
|
+
for (const candidate of candidates) {
|
|
209
|
+
const origin = candidate === undefined ? undefined : originOf(candidate);
|
|
210
|
+
if (origin !== undefined) {
|
|
211
|
+
origins.add(origin);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return [...origins];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Normalized `scheme://host[:port]`, or `undefined` if not a usable URL. */
|
|
218
|
+
export function originOf(url: string): string | undefined {
|
|
219
|
+
try {
|
|
220
|
+
const parsed = new URL(url);
|
|
221
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
return `${parsed.protocol}//${parsed.host.toLowerCase()}`;
|
|
225
|
+
} catch {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|