@absol-labs/agent 0.4.0 → 0.6.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 +187 -0
- package/dist/capability/invocation-capability.d.ts +184 -0
- package/dist/capability/invocation-capability.d.ts.map +1 -0
- package/dist/capability/invocation-capability.js +183 -0
- package/dist/capability/invocation-capability.js.map +1 -0
- package/dist/discovery/registry.d.ts +260 -15
- package/dist/discovery/registry.d.ts.map +1 -1
- package/dist/discovery/registry.js +174 -31
- package/dist/discovery/registry.js.map +1 -1
- package/dist/frameworks/agentkit.js +3 -3
- package/dist/frameworks/agentkit.js.map +1 -1
- package/dist/frameworks/crewai.js +1 -1
- package/dist/frameworks/crewai.js.map +1 -1
- package/dist/frameworks/eliza.js +2 -2
- package/dist/frameworks/eliza.js.map +1 -1
- package/dist/frameworks/langchain.js +2 -2
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/gateway/caller-auth-gateway.d.ts +108 -0
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -0
- package/dist/gateway/caller-auth-gateway.js +191 -0
- package/dist/gateway/caller-auth-gateway.js.map +1 -0
- package/dist/gateway/http-server.d.ts +51 -0
- package/dist/gateway/http-server.d.ts.map +1 -0
- package/dist/gateway/http-server.js +241 -0
- package/dist/gateway/http-server.js.map +1 -0
- package/dist/gateway/server-entry.d.ts +2 -0
- package/dist/gateway/server-entry.d.ts.map +1 -0
- package/dist/gateway/server-entry.js +30 -0
- package/dist/gateway/server-entry.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +4 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/invoke.d.ts +136 -0
- package/dist/sdk/invoke.d.ts.map +1 -0
- package/dist/sdk/invoke.js +274 -0
- package/dist/sdk/invoke.js.map +1 -0
- package/dist/wallet/lifecycle.d.ts +101 -0
- package/dist/wallet/lifecycle.d.ts.map +1 -0
- package/dist/wallet/lifecycle.js +57 -0
- package/dist/wallet/lifecycle.js.map +1 -0
- package/package.json +6 -3
- package/src/capability/invocation-capability.ts +255 -0
- package/src/discovery/registry.ts +213 -35
- package/src/frameworks/agentkit.ts +3 -3
- package/src/frameworks/crewai.ts +1 -1
- package/src/frameworks/eliza.ts +2 -2
- package/src/frameworks/langchain.ts +2 -2
- package/src/gateway/caller-auth-gateway.ts +332 -0
- package/src/gateway/http-server.ts +342 -0
- package/src/gateway/server-entry.ts +38 -0
- package/src/index.ts +86 -0
- package/src/mcp/server.ts +4 -2
- package/src/sdk/invoke.ts +495 -0
- package/src/wallet/lifecycle.ts +158 -0
package/README.md
CHANGED
|
@@ -120,6 +120,63 @@ const decision = await checkMandate(
|
|
|
120
120
|
if (!decision.allowed) throw new Error(`mandate denied: ${decision.reason}`);
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
### Agent wallet lifecycle: create/restore → fund → sign
|
|
124
|
+
|
|
125
|
+
An agent gets a usable wallet entirely through this package — no out-of-band key. `resolveAgentWallet()`
|
|
126
|
+
accepts an injected viem private key OR a Coinbase CDP config; the CDP path is **create-or-restore**:
|
|
127
|
+
the same `ownerName` always resolves to the same CDP-managed address, so re-running with the same env
|
|
128
|
+
restores the identical wallet instead of minting a new one.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import {
|
|
132
|
+
resolveAgentWallet,
|
|
133
|
+
parseAgentWalletEnv,
|
|
134
|
+
getWalletBalances,
|
|
135
|
+
faucetHint,
|
|
136
|
+
requestCdpFaucet,
|
|
137
|
+
} from "@absol-labs/agent";
|
|
138
|
+
import { MetrikClient } from "@absol-labs/sdk";
|
|
139
|
+
import { CdpClient } from "@coinbase/cdp-sdk";
|
|
140
|
+
|
|
141
|
+
// 1) Create or restore, from env (CDP_API_KEY_ID/CDP_API_KEY_SECRET/CDP_WALLET_SECRET +
|
|
142
|
+
// METRIK_AGENT_CDP_OWNER_NAME, or METRIK_AGENT_PRIVATE_KEY for an injected key).
|
|
143
|
+
const wallet = await resolveAgentWallet(parseAgentWalletEnv());
|
|
144
|
+
console.log("address:", wallet.account.address);
|
|
145
|
+
|
|
146
|
+
// 2) Read balance.
|
|
147
|
+
const metrik = MetrikClient.baseSepolia({ account: wallet.account });
|
|
148
|
+
const balances = await getWalletBalances({
|
|
149
|
+
publicClient: metrik.publicClient,
|
|
150
|
+
address: wallet.account.address,
|
|
151
|
+
usdc: metrik.config.usdc,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// 3) Fund (testnet only) — a faucet hint, or request funds programmatically for a CDP wallet.
|
|
155
|
+
console.log(faucetHint(84532)); // { cdpFaucetUrl, usdcFaucetUrl, ... }
|
|
156
|
+
if (wallet.source === "cdp" && balances.nativeWei === 0n) {
|
|
157
|
+
const cdp = new CdpClient({
|
|
158
|
+
/* same CDP_* creds */
|
|
159
|
+
});
|
|
160
|
+
await requestCdpFaucet({
|
|
161
|
+
cdp,
|
|
162
|
+
address: wallet.account.address,
|
|
163
|
+
token: "eth",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 4) Sign — `wallet.account` is a plain viem `Account`; no extra wrapper needed.
|
|
168
|
+
const signature = await wallet.account.signTypedData?.(/* ... */);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`getWalletBalances` and `faucetHint`/`requestCdpFaucet` are deliberately thin: they don't introduce a
|
|
172
|
+
second wallet system, they just fill in the "read balance" and "get testnet funds" gaps around the
|
|
173
|
+
existing `resolveAgentWallet()` / `createWalletBackedAgentClient()` surface. `faucetHint` and
|
|
174
|
+
`requestCdpFaucet` only support Base Sepolia (`84532`) — Metrik is testnet-only, single-chain.
|
|
175
|
+
|
|
176
|
+
See [`scripts/e2e-hire.ts`](./scripts/e2e-hire.ts) for the full journey (create/restore wallet → fund →
|
|
177
|
+
hire → invoke → settle) run against real Base Sepolia infrastructure: `pnpm e2e:hire` (needs real CDP
|
|
178
|
+
creds — see `.env.example`).
|
|
179
|
+
|
|
123
180
|
### Hire via x402: `402` challenge → signed payload → open stream
|
|
124
181
|
|
|
125
182
|
```ts
|
|
@@ -183,6 +240,136 @@ Core env surface: `METRIK_AGENT_RPC_URL`, `METRIK_AGENT_ESCROW`, `METRIK_AGENT_U
|
|
|
183
240
|
`RECLAIM_APP_ID` / `RECLAIM_APP_SECRET` to enable the `prove_https_response` zkTLS tool.
|
|
184
241
|
See [`docs/quickstart.md`](./docs/quickstart.md) for the full list.
|
|
185
242
|
|
|
243
|
+
## Closed loop: `hire → invoke → (prove) → settle`
|
|
244
|
+
|
|
245
|
+
Opening and funding a stream only pays for a service - it does not, on its own,
|
|
246
|
+
authorize the buyer to _call_ it. The caller-auth gateway (metrik-protocol#62/#63/#64)
|
|
247
|
+
closes that gap: a seller runs a small reverse-proxy gateway in front of its real
|
|
248
|
+
service, and a buyer's SDK calls it directly with a short-lived, single-use,
|
|
249
|
+
stream-bound `InvocationCapability` - no separate credential, no manual API key.
|
|
250
|
+
|
|
251
|
+
**Seller side** - front any HTTP service with the reference gateway:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import {
|
|
255
|
+
CallerAuthGateway,
|
|
256
|
+
createCallerAuthGatewayServer,
|
|
257
|
+
} from "@absol-labs/agent";
|
|
258
|
+
|
|
259
|
+
const gateway = new CallerAuthGateway({
|
|
260
|
+
escrowAddress: "0x...", // the StreamEscrowV2 this service's streams settle on
|
|
261
|
+
rpcUrl: process.env.METRIK_GATEWAY_RPC_URL!,
|
|
262
|
+
serviceRef: "0x...", // this gateway's serviceRef - must match the listing
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const server = createCallerAuthGatewayServer({
|
|
266
|
+
gateway,
|
|
267
|
+
upstreamUrl: "https://my-real-service.example.com",
|
|
268
|
+
});
|
|
269
|
+
await server.listen(8787);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Or run the ready-made standalone server from env (`METRIK_GATEWAY_ESCROW_ADDRESS`,
|
|
273
|
+
`METRIK_GATEWAY_RPC_URL`, `METRIK_GATEWAY_SERVICE_REF`, `METRIK_GATEWAY_UPSTREAM_URL`,
|
|
274
|
+
optional comma-separated `METRIK_GATEWAY_SERVICE_REFS` migration aliases,
|
|
275
|
+
optional `METRIK_GATEWAY_CHAIN_ID` / `METRIK_GATEWAY_PORT` / `METRIK_GATEWAY_HOST`):
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
pnpm gateway
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Every request must carry a valid capability; every failure mode (missing header, bad
|
|
282
|
+
signature, wrong buyer, closed/expired/underfunded stream, replayed nonce, wrong
|
|
283
|
+
method/path, wrong `serviceRef`) is rejected with a distinct machine-readable `reason`
|
|
284
|
+
and a `402`/`403` - the real upstream is never touched on a rejection. Access is revoked
|
|
285
|
+
automatically: once a stream is closed, expired, or reclaimed, the next on-chain read
|
|
286
|
+
fails closed with no extra bookkeeping.
|
|
287
|
+
|
|
288
|
+
**Buyer side** - after `hireVerifiedService`/`open()`, call the purchased service
|
|
289
|
+
directly:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
293
|
+
import {
|
|
294
|
+
createSdkInvokeStreamReader,
|
|
295
|
+
discoverServices,
|
|
296
|
+
invoke,
|
|
297
|
+
} from "@absol-labs/agent";
|
|
298
|
+
|
|
299
|
+
const buyer = privateKeyToAccount(
|
|
300
|
+
process.env.METRIK_AGENT_PRIVATE_KEY as `0x${string}`,
|
|
301
|
+
);
|
|
302
|
+
const streamReader = createSdkInvokeStreamReader({
|
|
303
|
+
escrowAddress: "0x...",
|
|
304
|
+
rpcUrl: process.env.METRIK_AGENT_RPC_URL!,
|
|
305
|
+
});
|
|
306
|
+
const { serviceRef } = await streamReader.getStreamV2(streamId);
|
|
307
|
+
const listing = (await discoverServices()).find(
|
|
308
|
+
(service) => service.serviceRef.toLowerCase() === serviceRef.toLowerCase(),
|
|
309
|
+
);
|
|
310
|
+
if (!listing) throw new Error("verified signed listing unavailable");
|
|
311
|
+
|
|
312
|
+
const { response } = await invoke(
|
|
313
|
+
streamId, // from hireVerifiedService()
|
|
314
|
+
{
|
|
315
|
+
method: "POST",
|
|
316
|
+
path: "/v1/infer",
|
|
317
|
+
body: JSON.stringify({ prompt: "..." }),
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
streamReader,
|
|
321
|
+
buyer,
|
|
322
|
+
domain: { chainId: 84532, verifyingContract: "0x..." }, // same escrow as above
|
|
323
|
+
listing, // verified signature + signed callerAuth.accessUrl; no manual credential/URL
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
console.log(await response.json());
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`invoke()` loads the stream, fails closed BEFORE any network call if it is not active,
|
|
331
|
+
is expired, or does not belong to the signing account, then builds and signs a
|
|
332
|
+
capability scoped to exactly that one `method`+`path` (short expiry, single-use nonce)
|
|
333
|
+
and attaches it as the `x-metrik-capability` header. Use `capabilityFor()` directly if
|
|
334
|
+
you only need the signed capability without the SDK also performing the `fetch`.
|
|
335
|
+
|
|
336
|
+
For a public T2 (consumer-zkTLS) service, `invokeWithT2DeliveryProof()` attaches the SAME
|
|
337
|
+
capability to the exact request the buyer's Reclaim attestor proves, so the
|
|
338
|
+
capability-authorized call IS the delivery evidence - usage and proof become one action:
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
import { discoverServices, invokeWithT2DeliveryProof } from "@absol-labs/agent";
|
|
342
|
+
|
|
343
|
+
const { serviceRef: t2ServiceRef } = await streamReader.getStreamV2(streamId);
|
|
344
|
+
const publicT2Listing = (await discoverServices()).find(
|
|
345
|
+
(service) => service.serviceRef.toLowerCase() === t2ServiceRef.toLowerCase(),
|
|
346
|
+
);
|
|
347
|
+
if (!publicT2Listing || publicT2Listing.access !== "public") {
|
|
348
|
+
throw new Error("verified public T2 listing unavailable");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const { t2 } = await invokeWithT2DeliveryProof(streamId, {
|
|
352
|
+
streamReader,
|
|
353
|
+
buyer,
|
|
354
|
+
domain: { chainId: 84532, verifyingContract: "0x..." },
|
|
355
|
+
request: {
|
|
356
|
+
url: "https://public-t2-service.example.com/v1/infer",
|
|
357
|
+
method: "POST",
|
|
358
|
+
responseMatches: [{ type: "regex", value: '"result":"(?<result>.*)"' }],
|
|
359
|
+
nonceInjection: { in: "query", name: "nonce" },
|
|
360
|
+
},
|
|
361
|
+
intervalIndex: 0n,
|
|
362
|
+
nonce: deliveryNonce, // from GET /delivery/nonce
|
|
363
|
+
listing: publicT2Listing,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// t2.body is the exact POST /delivery/proof request payload.
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Gated consumer-attested invocation fails closed for now: proof-target pinning does
|
|
370
|
+
not yet distinguish the public oracle origin from a signed access gateway. Gated
|
|
371
|
+
services use output-probe verification until that protocol support exists.
|
|
372
|
+
|
|
186
373
|
## Consumer zkTLS (delivery proofs)
|
|
187
374
|
|
|
188
375
|
`ReclaimConsumerProofService` (and `createReclaimConsumerProofServiceFromEnv`) generate a
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { type Address, type Hex, type LocalAccount } from "viem";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/** HTTP methods a capability may authorize. */
|
|
4
|
+
export declare const httpMethodSchema: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]>;
|
|
5
|
+
export type HttpMethod = z.infer<typeof httpMethodSchema>;
|
|
6
|
+
export declare const invocationCapabilitySchema: z.ZodObject<{
|
|
7
|
+
/** The on-chain `StreamEscrowV2` stream this capability draws authority from. */
|
|
8
|
+
streamId: z.ZodEffects<z.ZodString, string, string>;
|
|
9
|
+
/** Must equal `stream.buyer` and the EIP-712 signer. */
|
|
10
|
+
buyer: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
11
|
+
/** Must equal the target service's `serviceRef` (and the stream's). */
|
|
12
|
+
serviceRef: z.ZodEffects<z.ZodString, string, string>;
|
|
13
|
+
/** The single HTTP method this capability authorizes. */
|
|
14
|
+
method: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]>;
|
|
15
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
16
|
+
pathHash: z.ZodEffects<z.ZodString, string, string>;
|
|
17
|
+
/** Single-use anti-replay nonce. */
|
|
18
|
+
nonce: z.ZodEffects<z.ZodString, string, string>;
|
|
19
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
20
|
+
expiry: z.ZodNumber;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
streamId: string;
|
|
23
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
24
|
+
serviceRef: string;
|
|
25
|
+
nonce: string;
|
|
26
|
+
buyer: `0x${string}`;
|
|
27
|
+
pathHash: string;
|
|
28
|
+
expiry: number;
|
|
29
|
+
}, {
|
|
30
|
+
streamId: string;
|
|
31
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
32
|
+
serviceRef: string;
|
|
33
|
+
nonce: string;
|
|
34
|
+
buyer: string;
|
|
35
|
+
pathHash: string;
|
|
36
|
+
expiry: number;
|
|
37
|
+
}>;
|
|
38
|
+
export type InvocationCapability = z.infer<typeof invocationCapabilitySchema>;
|
|
39
|
+
export declare const signedInvocationCapabilitySchema: z.ZodObject<{
|
|
40
|
+
/** The on-chain `StreamEscrowV2` stream this capability draws authority from. */
|
|
41
|
+
streamId: z.ZodEffects<z.ZodString, string, string>;
|
|
42
|
+
/** Must equal `stream.buyer` and the EIP-712 signer. */
|
|
43
|
+
buyer: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
44
|
+
/** Must equal the target service's `serviceRef` (and the stream's). */
|
|
45
|
+
serviceRef: z.ZodEffects<z.ZodString, string, string>;
|
|
46
|
+
/** The single HTTP method this capability authorizes. */
|
|
47
|
+
method: z.ZodEnum<["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]>;
|
|
48
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
49
|
+
pathHash: z.ZodEffects<z.ZodString, string, string>;
|
|
50
|
+
/** Single-use anti-replay nonce. */
|
|
51
|
+
nonce: z.ZodEffects<z.ZodString, string, string>;
|
|
52
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
53
|
+
expiry: z.ZodNumber;
|
|
54
|
+
} & {
|
|
55
|
+
signature: z.ZodEffects<z.ZodString, string, string>;
|
|
56
|
+
}, "strip", z.ZodTypeAny, {
|
|
57
|
+
signature: string;
|
|
58
|
+
streamId: string;
|
|
59
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
60
|
+
serviceRef: string;
|
|
61
|
+
nonce: string;
|
|
62
|
+
buyer: `0x${string}`;
|
|
63
|
+
pathHash: string;
|
|
64
|
+
expiry: number;
|
|
65
|
+
}, {
|
|
66
|
+
signature: string;
|
|
67
|
+
streamId: string;
|
|
68
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
69
|
+
serviceRef: string;
|
|
70
|
+
nonce: string;
|
|
71
|
+
buyer: string;
|
|
72
|
+
pathHash: string;
|
|
73
|
+
expiry: number;
|
|
74
|
+
}>;
|
|
75
|
+
export type SignedInvocationCapability = z.infer<typeof signedInvocationCapabilitySchema>;
|
|
76
|
+
/** Chain-bound domain: a capability for one chain/escrow can never be replayed against another. */
|
|
77
|
+
export interface InvocationCapabilityDomainInput {
|
|
78
|
+
readonly chainId: number;
|
|
79
|
+
/** The `StreamEscrowV2` the stream settles on. */
|
|
80
|
+
readonly verifyingContract: Address;
|
|
81
|
+
}
|
|
82
|
+
export declare const invocationCapabilityDomainName: "Metrik Invocation Capability";
|
|
83
|
+
export declare const invocationCapabilityDomainVersion: "1";
|
|
84
|
+
export declare const invocationCapabilityTypedData: {
|
|
85
|
+
readonly InvocationCapability: readonly [{
|
|
86
|
+
readonly name: "streamId";
|
|
87
|
+
readonly type: "bytes32";
|
|
88
|
+
}, {
|
|
89
|
+
readonly name: "buyer";
|
|
90
|
+
readonly type: "address";
|
|
91
|
+
}, {
|
|
92
|
+
readonly name: "serviceRef";
|
|
93
|
+
readonly type: "bytes32";
|
|
94
|
+
}, {
|
|
95
|
+
readonly name: "method";
|
|
96
|
+
readonly type: "string";
|
|
97
|
+
}, {
|
|
98
|
+
readonly name: "pathHash";
|
|
99
|
+
readonly type: "bytes32";
|
|
100
|
+
}, {
|
|
101
|
+
readonly name: "nonce";
|
|
102
|
+
readonly type: "bytes32";
|
|
103
|
+
}, {
|
|
104
|
+
readonly name: "expiry";
|
|
105
|
+
readonly type: "uint64";
|
|
106
|
+
}];
|
|
107
|
+
};
|
|
108
|
+
/** Default single-use lifetime for a freshly-issued capability. Deliberately short. */
|
|
109
|
+
export declare const DEFAULT_CAPABILITY_TTL_SECONDS = 60;
|
|
110
|
+
/** Header a buyer's request carries the signed capability in. */
|
|
111
|
+
export declare const CAPABILITY_HEADER_NAME = "x-metrik-capability";
|
|
112
|
+
export declare function createInvocationCapabilityEip712Domain(domain: InvocationCapabilityDomainInput): {
|
|
113
|
+
readonly name: "Metrik Invocation Capability";
|
|
114
|
+
readonly version: "1";
|
|
115
|
+
readonly chainId: number;
|
|
116
|
+
readonly verifyingContract: `0x${string}`;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* `keccak256` of the exact request path this capability authorizes (e.g.
|
|
120
|
+
* `/v1/infer`). Path only - no scheme/host/query/fragment - so the signer and
|
|
121
|
+
* the gateway hash the identical canonical string regardless of how each side
|
|
122
|
+
* received the full URL.
|
|
123
|
+
*/
|
|
124
|
+
export declare function hashInvocationPath(path: string): Hex;
|
|
125
|
+
/** Strips query/fragment and any trailing slash (except the root) for a stable canonical form. */
|
|
126
|
+
export declare function normalizeInvocationPath(path: string): string;
|
|
127
|
+
export declare function buildInvocationCapabilityTypedData(capability: InvocationCapability, domain: InvocationCapabilityDomainInput): {
|
|
128
|
+
domain: {
|
|
129
|
+
readonly name: "Metrik Invocation Capability";
|
|
130
|
+
readonly version: "1";
|
|
131
|
+
readonly chainId: number;
|
|
132
|
+
readonly verifyingContract: `0x${string}`;
|
|
133
|
+
};
|
|
134
|
+
types: {
|
|
135
|
+
readonly InvocationCapability: readonly [{
|
|
136
|
+
readonly name: "streamId";
|
|
137
|
+
readonly type: "bytes32";
|
|
138
|
+
}, {
|
|
139
|
+
readonly name: "buyer";
|
|
140
|
+
readonly type: "address";
|
|
141
|
+
}, {
|
|
142
|
+
readonly name: "serviceRef";
|
|
143
|
+
readonly type: "bytes32";
|
|
144
|
+
}, {
|
|
145
|
+
readonly name: "method";
|
|
146
|
+
readonly type: "string";
|
|
147
|
+
}, {
|
|
148
|
+
readonly name: "pathHash";
|
|
149
|
+
readonly type: "bytes32";
|
|
150
|
+
}, {
|
|
151
|
+
readonly name: "nonce";
|
|
152
|
+
readonly type: "bytes32";
|
|
153
|
+
}, {
|
|
154
|
+
readonly name: "expiry";
|
|
155
|
+
readonly type: "uint64";
|
|
156
|
+
}];
|
|
157
|
+
};
|
|
158
|
+
primaryType: "InvocationCapability";
|
|
159
|
+
message: {
|
|
160
|
+
streamId: Hex;
|
|
161
|
+
buyer: `0x${string}`;
|
|
162
|
+
serviceRef: Hex;
|
|
163
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
|
|
164
|
+
pathHash: Hex;
|
|
165
|
+
nonce: Hex;
|
|
166
|
+
expiry: bigint;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
/** Signs an `InvocationCapability` with the buyer's own wallet. */
|
|
170
|
+
export declare function signInvocationCapability(capability: InvocationCapability, domain: InvocationCapabilityDomainInput, account: LocalAccount): Promise<Hex>;
|
|
171
|
+
/** Recovers the EIP-712 signer of a capability. Throws on a malformed signature. */
|
|
172
|
+
export declare function recoverInvocationCapabilitySigner(capability: InvocationCapability, domain: InvocationCapabilityDomainInput, signature: Hex): Promise<Address>;
|
|
173
|
+
/** Cryptographically random 32-byte single-use nonce. */
|
|
174
|
+
export declare function generateCapabilityNonce(): Hex;
|
|
175
|
+
export declare class InvalidCapabilityHeaderError extends Error {
|
|
176
|
+
constructor(message: string, options?: {
|
|
177
|
+
readonly cause?: unknown;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/** Encodes a signed capability as an opaque, header-safe (base64url) string. */
|
|
181
|
+
export declare function encodeCapabilityHeader(signed: SignedInvocationCapability): string;
|
|
182
|
+
/** Decodes and schema-validates a capability header value. Fails closed on any malformation. */
|
|
183
|
+
export declare function decodeCapabilityHeader(headerValue: string): SignedInvocationCapability;
|
|
184
|
+
//# sourceMappingURL=invocation-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocation-capability.d.ts","sourceRoot":"","sources":["../../src/capability/invocation-capability.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmCxB,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,yEAQ3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,0BAA0B;IACrC,iFAAiF;;IAEjF,wDAAwD;;IAExD,uEAAuE;;IAEvE,yDAAyD;;IAEzD,wEAAwE;;IAExE,oCAAoC;;IAEpC,kEAAkE;;;;;;;;;;;;;;;;;;EAElE,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,gCAAgC;IAjB3C,iFAAiF;;IAEjF,wDAAwD;;IAExD,uEAAuE;;IAEvE,yDAAyD;;IAEzD,wEAAwE;;IAExE,oCAAoC;;IAEpC,kEAAkE;;;;;;;;;;;;;;;;;;;;;;EAQhE,CAAC;AACL,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAC9C,OAAO,gCAAgC,CACxC,CAAC;AAEF,mGAAmG;AACnG,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACrC;AAED,eAAO,MAAM,8BAA8B,EACzC,8BAAuC,CAAC;AAC1C,eAAO,MAAM,iCAAiC,EAAG,GAAY,CAAC;AAE9D,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;CAUhC,CAAC;AAEX,uFAAuF;AACvF,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAE5D,wBAAgB,sCAAsC,CACpD,MAAM,EAAE,+BAA+B;;;;;EAQxC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAEpD;AAED,kGAAkG;AAClG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU5D;AAeD,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAZR,GAAG;;oBAEC,GAAG;;kBAEP,GAAG;eACT,GAAG;;;EAe7B;AAED,mEAAmE;AACnE,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,+BAA+B,EACvC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,GAAG,CAAC,CAGd;AAED,oFAAoF;AACpF,wBAAsB,iCAAiC,CACrD,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,+BAA+B,EACvC,SAAS,EAAE,GAAG,GACb,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,yDAAyD;AACzD,wBAAgB,uBAAuB,IAAI,GAAG,CAE7C;AAED,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIpE;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,0BAA0B,GACjC,MAAM,CAaR;AAED,gGAAgG;AAChG,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,GAClB,0BAA0B,CA2B5B"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { getAddress, isAddress, isHex, keccak256, recoverTypedDataAddress, stringToBytes, } from "viem";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
/**
|
|
5
|
+
* `InvocationCapability` — the stream-bound EIP-712 credential that closes the
|
|
6
|
+
* "pay -> use" loop (metrik-protocol#62/#63/#64).
|
|
7
|
+
*
|
|
8
|
+
* A buyer who has opened an active, funded stream signs one of these per
|
|
9
|
+
* service call: "I, `buyer`, authorize exactly `method` `pathHash` against
|
|
10
|
+
* `serviceRef` under stream `streamId`, once, before `expiry`." The seller's
|
|
11
|
+
* gateway (`../gateway/caller-auth-gateway.ts`) recovers the signer, cross
|
|
12
|
+
* checks it against the on-chain stream, and enforces the nonce/expiry/route
|
|
13
|
+
* binding before proxying the call upstream.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately kept LOCAL to `@absol-labs/agent` for this MVP slice (per
|
|
16
|
+
* metrik-agent#64) rather than added to `@absol-labs/shared` - it is never
|
|
17
|
+
* read on-chain (no contract parses it), so there is no cross-repo byte
|
|
18
|
+
* compatibility requirement the way there is for `DeliveryReceipt`.
|
|
19
|
+
*/
|
|
20
|
+
const addressSchema = z
|
|
21
|
+
.string()
|
|
22
|
+
.refine((value) => isAddress(value), "must be an EVM address");
|
|
23
|
+
const bytes32Schema = z
|
|
24
|
+
.string()
|
|
25
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 66, "must be bytes32 hex");
|
|
26
|
+
const signatureSchema = z
|
|
27
|
+
.string()
|
|
28
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 132, "must be a 65-byte signature");
|
|
29
|
+
/** HTTP methods a capability may authorize. */
|
|
30
|
+
export const httpMethodSchema = z.enum([
|
|
31
|
+
"GET",
|
|
32
|
+
"POST",
|
|
33
|
+
"PUT",
|
|
34
|
+
"PATCH",
|
|
35
|
+
"DELETE",
|
|
36
|
+
"HEAD",
|
|
37
|
+
"OPTIONS",
|
|
38
|
+
]);
|
|
39
|
+
export const invocationCapabilitySchema = z.object({
|
|
40
|
+
/** The on-chain `StreamEscrowV2` stream this capability draws authority from. */
|
|
41
|
+
streamId: bytes32Schema,
|
|
42
|
+
/** Must equal `stream.buyer` and the EIP-712 signer. */
|
|
43
|
+
buyer: addressSchema,
|
|
44
|
+
/** Must equal the target service's `serviceRef` (and the stream's). */
|
|
45
|
+
serviceRef: bytes32Schema,
|
|
46
|
+
/** The single HTTP method this capability authorizes. */
|
|
47
|
+
method: httpMethodSchema,
|
|
48
|
+
/** `keccak256` of the exact request path this capability authorizes. */
|
|
49
|
+
pathHash: bytes32Schema,
|
|
50
|
+
/** Single-use anti-replay nonce. */
|
|
51
|
+
nonce: bytes32Schema,
|
|
52
|
+
/** Unix seconds after which the capability is no longer valid. */
|
|
53
|
+
expiry: z.number().int().positive(),
|
|
54
|
+
});
|
|
55
|
+
export const signedInvocationCapabilitySchema = invocationCapabilitySchema.extend({
|
|
56
|
+
signature: signatureSchema,
|
|
57
|
+
});
|
|
58
|
+
export const invocationCapabilityDomainName = "Metrik Invocation Capability";
|
|
59
|
+
export const invocationCapabilityDomainVersion = "1";
|
|
60
|
+
export const invocationCapabilityTypedData = {
|
|
61
|
+
InvocationCapability: [
|
|
62
|
+
{ name: "streamId", type: "bytes32" },
|
|
63
|
+
{ name: "buyer", type: "address" },
|
|
64
|
+
{ name: "serviceRef", type: "bytes32" },
|
|
65
|
+
{ name: "method", type: "string" },
|
|
66
|
+
{ name: "pathHash", type: "bytes32" },
|
|
67
|
+
{ name: "nonce", type: "bytes32" },
|
|
68
|
+
{ name: "expiry", type: "uint64" },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
/** Default single-use lifetime for a freshly-issued capability. Deliberately short. */
|
|
72
|
+
export const DEFAULT_CAPABILITY_TTL_SECONDS = 60;
|
|
73
|
+
/** Header a buyer's request carries the signed capability in. */
|
|
74
|
+
export const CAPABILITY_HEADER_NAME = "x-metrik-capability";
|
|
75
|
+
export function createInvocationCapabilityEip712Domain(domain) {
|
|
76
|
+
return {
|
|
77
|
+
name: invocationCapabilityDomainName,
|
|
78
|
+
version: invocationCapabilityDomainVersion,
|
|
79
|
+
chainId: domain.chainId,
|
|
80
|
+
verifyingContract: getAddress(domain.verifyingContract),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `keccak256` of the exact request path this capability authorizes (e.g.
|
|
85
|
+
* `/v1/infer`). Path only - no scheme/host/query/fragment - so the signer and
|
|
86
|
+
* the gateway hash the identical canonical string regardless of how each side
|
|
87
|
+
* received the full URL.
|
|
88
|
+
*/
|
|
89
|
+
export function hashInvocationPath(path) {
|
|
90
|
+
return keccak256(stringToBytes(normalizeInvocationPath(path)));
|
|
91
|
+
}
|
|
92
|
+
/** Strips query/fragment and any trailing slash (except the root) for a stable canonical form. */
|
|
93
|
+
export function normalizeInvocationPath(path) {
|
|
94
|
+
const withoutFragment = path.split("#")[0] ?? "";
|
|
95
|
+
const withoutQuery = withoutFragment.split("?")[0] ?? "";
|
|
96
|
+
if (withoutQuery.length === 0) {
|
|
97
|
+
return "/";
|
|
98
|
+
}
|
|
99
|
+
if (withoutQuery.length > 1 && withoutQuery.endsWith("/")) {
|
|
100
|
+
return withoutQuery.slice(0, -1);
|
|
101
|
+
}
|
|
102
|
+
return withoutQuery;
|
|
103
|
+
}
|
|
104
|
+
function invocationCapabilityMessage(capability) {
|
|
105
|
+
const parsed = invocationCapabilitySchema.parse(capability);
|
|
106
|
+
return {
|
|
107
|
+
streamId: parsed.streamId,
|
|
108
|
+
buyer: getAddress(parsed.buyer),
|
|
109
|
+
serviceRef: parsed.serviceRef,
|
|
110
|
+
method: parsed.method,
|
|
111
|
+
pathHash: parsed.pathHash,
|
|
112
|
+
nonce: parsed.nonce,
|
|
113
|
+
expiry: BigInt(parsed.expiry),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export function buildInvocationCapabilityTypedData(capability, domain) {
|
|
117
|
+
return {
|
|
118
|
+
domain: createInvocationCapabilityEip712Domain(domain),
|
|
119
|
+
types: invocationCapabilityTypedData,
|
|
120
|
+
primaryType: "InvocationCapability",
|
|
121
|
+
message: invocationCapabilityMessage(capability),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Signs an `InvocationCapability` with the buyer's own wallet. */
|
|
125
|
+
export async function signInvocationCapability(capability, domain, account) {
|
|
126
|
+
const typedData = buildInvocationCapabilityTypedData(capability, domain);
|
|
127
|
+
return account.signTypedData(typedData);
|
|
128
|
+
}
|
|
129
|
+
/** Recovers the EIP-712 signer of a capability. Throws on a malformed signature. */
|
|
130
|
+
export async function recoverInvocationCapabilitySigner(capability, domain, signature) {
|
|
131
|
+
const typedData = buildInvocationCapabilityTypedData(capability, domain);
|
|
132
|
+
return recoverTypedDataAddress({ ...typedData, signature });
|
|
133
|
+
}
|
|
134
|
+
/** Cryptographically random 32-byte single-use nonce. */
|
|
135
|
+
export function generateCapabilityNonce() {
|
|
136
|
+
return `0x${randomBytes(32).toString("hex")}`;
|
|
137
|
+
}
|
|
138
|
+
export class InvalidCapabilityHeaderError extends Error {
|
|
139
|
+
constructor(message, options) {
|
|
140
|
+
super(message, options);
|
|
141
|
+
this.name = "InvalidCapabilityHeaderError";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Encodes a signed capability as an opaque, header-safe (base64url) string. */
|
|
145
|
+
export function encodeCapabilityHeader(signed) {
|
|
146
|
+
const parsed = signedInvocationCapabilitySchema.parse(signed);
|
|
147
|
+
const json = JSON.stringify({
|
|
148
|
+
streamId: parsed.streamId,
|
|
149
|
+
buyer: parsed.buyer,
|
|
150
|
+
serviceRef: parsed.serviceRef,
|
|
151
|
+
method: parsed.method,
|
|
152
|
+
pathHash: parsed.pathHash,
|
|
153
|
+
nonce: parsed.nonce,
|
|
154
|
+
expiry: parsed.expiry,
|
|
155
|
+
signature: parsed.signature,
|
|
156
|
+
});
|
|
157
|
+
return Buffer.from(json, "utf8").toString("base64url");
|
|
158
|
+
}
|
|
159
|
+
/** Decodes and schema-validates a capability header value. Fails closed on any malformation. */
|
|
160
|
+
export function decodeCapabilityHeader(headerValue) {
|
|
161
|
+
let json;
|
|
162
|
+
try {
|
|
163
|
+
json = Buffer.from(headerValue, "base64url").toString("utf8");
|
|
164
|
+
}
|
|
165
|
+
catch (cause) {
|
|
166
|
+
throw new InvalidCapabilityHeaderError("malformed base64 capability header", { cause });
|
|
167
|
+
}
|
|
168
|
+
let raw;
|
|
169
|
+
try {
|
|
170
|
+
raw = JSON.parse(json);
|
|
171
|
+
}
|
|
172
|
+
catch (cause) {
|
|
173
|
+
throw new InvalidCapabilityHeaderError("malformed JSON capability header", {
|
|
174
|
+
cause,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const result = signedInvocationCapabilitySchema.safeParse(raw);
|
|
178
|
+
if (!result.success) {
|
|
179
|
+
throw new InvalidCapabilityHeaderError(`invalid capability shape: ${result.error.message}`);
|
|
180
|
+
}
|
|
181
|
+
return result.data;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=invocation-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocation-capability.js","sourceRoot":"","sources":["../../src/capability/invocation-capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EACL,UAAU,EACV,SAAS,EACT,KAAK,EACL,SAAS,EACT,uBAAuB,EACvB,aAAa,GAId,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAC;AACjE,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,EAAE;KACR,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,EAChE,qBAAqB,CACtB,CAAC;AACJ,MAAM,eAAe,GAAG,CAAC;KACtB,MAAM,EAAE;KACR,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EACjE,6BAA6B,CAC9B,CAAC;AAEJ,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACrC,KAAK;IACL,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,MAAM;IACN,SAAS;CACV,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,iFAAiF;IACjF,QAAQ,EAAE,aAAa;IACvB,wDAAwD;IACxD,KAAK,EAAE,aAAa;IACpB,uEAAuE;IACvE,UAAU,EAAE,aAAa;IACzB,yDAAyD;IACzD,MAAM,EAAE,gBAAgB;IACxB,wEAAwE;IACxE,QAAQ,EAAE,aAAa;IACvB,oCAAoC;IACpC,KAAK,EAAE,aAAa;IACpB,kEAAkE;IAClE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,0BAA0B,CAAC,MAAM,CAAC;IAChC,SAAS,EAAE,eAAe;CAC3B,CAAC,CAAC;AAYL,MAAM,CAAC,MAAM,8BAA8B,GACzC,8BAAuC,CAAC;AAC1C,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAY,CAAC;AAE9D,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,oBAAoB,EAAE;QACpB,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;QACrC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;QACvC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;QACrC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;KACnC;CACO,CAAC;AAEX,uFAAuF;AACvF,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAEjD,iEAAiE;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D,MAAM,UAAU,sCAAsC,CACpD,MAAuC;IAEvC,OAAO;QACL,IAAI,EAAE,8BAA8B;QACpC,OAAO,EAAE,iCAAiC;QAC1C,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,iBAAiB,EAAE,UAAU,CAAC,MAAM,CAAC,iBAAiB,CAAC;KAC/C,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,SAAS,CAAC,aAAa,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,2BAA2B,CAAC,UAAgC;IACnE,MAAM,MAAM,GAAG,0BAA0B,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5D,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAe;QAChC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAiB;QACpC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAe;QAChC,KAAK,EAAE,MAAM,CAAC,KAAY;QAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kCAAkC,CAChD,UAAgC,EAChC,MAAuC;IAEvC,OAAO;QACL,MAAM,EAAE,sCAAsC,CAAC,MAAM,CAAC;QACtD,KAAK,EAAE,6BAA6B;QACpC,WAAW,EAAE,sBAA+B;QAC5C,OAAO,EAAE,2BAA2B,CAAC,UAAU,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAAgC,EAChC,MAAuC,EACvC,OAAqB;IAErB,MAAM,SAAS,GAAG,kCAAkC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzE,OAAO,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;AAC1C,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,UAAgC,EAChC,MAAuC,EACvC,SAAc;IAEd,MAAM,SAAS,GAAG,kCAAkC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzE,OAAO,uBAAuB,CAAC,EAAE,GAAG,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,uBAAuB;IACrC,OAAO,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAS,CAAC;AACvD,CAAC;AAED,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACrD,YAAY,OAAe,EAAE,OAAsC;QACjE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAED,gFAAgF;AAChF,MAAM,UAAU,sBAAsB,CACpC,MAAkC;IAElC,MAAM,MAAM,GAAG,gCAAgC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACzD,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CACpC,WAAmB;IAEnB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,4BAA4B,CACpC,oCAAoC,EACpC,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,4BAA4B,CAAC,kCAAkC,EAAE;YACzE,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,gCAAgC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,4BAA4B,CACpC,6BAA6B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|