@absol-labs/agent 0.1.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/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/discovery/registry.d.ts +68 -0
- package/dist/discovery/registry.d.ts.map +1 -0
- package/dist/discovery/registry.js +148 -0
- package/dist/discovery/registry.js.map +1 -0
- package/dist/frameworks/agentkit.d.ts +158 -0
- package/dist/frameworks/agentkit.d.ts.map +1 -0
- package/dist/frameworks/agentkit.js +339 -0
- package/dist/frameworks/agentkit.js.map +1 -0
- package/dist/frameworks/crewai.d.ts +19 -0
- package/dist/frameworks/crewai.d.ts.map +1 -0
- package/dist/frameworks/crewai.js +73 -0
- package/dist/frameworks/crewai.js.map +1 -0
- package/dist/frameworks/eliza.d.ts +393 -0
- package/dist/frameworks/eliza.d.ts.map +1 -0
- package/dist/frameworks/eliza.js +542 -0
- package/dist/frameworks/eliza.js.map +1 -0
- package/dist/frameworks/langchain.d.ts +31 -0
- package/dist/frameworks/langchain.d.ts.map +1 -0
- package/dist/frameworks/langchain.js +206 -0
- package/dist/frameworks/langchain.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/mandates/env.d.ts +51 -0
- package/dist/mandates/env.d.ts.map +1 -0
- package/dist/mandates/env.js +148 -0
- package/dist/mandates/env.js.map +1 -0
- package/dist/mandates/mandate.d.ts +214 -0
- package/dist/mandates/mandate.d.ts.map +1 -0
- package/dist/mandates/mandate.js +172 -0
- package/dist/mandates/mandate.js.map +1 -0
- package/dist/mcp/http-server.d.ts +2 -0
- package/dist/mcp/http-server.d.ts.map +1 -0
- package/dist/mcp/http-server.js +32 -0
- package/dist/mcp/http-server.js.map +1 -0
- package/dist/mcp/http.d.ts +114 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +428 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/server.d.ts +119 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +679 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +6 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/sdk/client.d.ts +78 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +99 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/wallet/provider.d.ts +51 -0
- package/dist/wallet/provider.d.ts.map +1 -0
- package/dist/wallet/provider.js +107 -0
- package/dist/wallet/provider.js.map +1 -0
- package/dist/x402/facilitator.d.ts +315 -0
- package/dist/x402/facilitator.d.ts.map +1 -0
- package/dist/x402/facilitator.js +194 -0
- package/dist/x402/facilitator.js.map +1 -0
- package/dist/zktls/reclaim.d.ts +137 -0
- package/dist/zktls/reclaim.d.ts.map +1 -0
- package/dist/zktls/reclaim.js +245 -0
- package/dist/zktls/reclaim.js.map +1 -0
- package/package.json +86 -0
- package/src/discovery/registry.ts +255 -0
- package/src/frameworks/agentkit.ts +537 -0
- package/src/frameworks/crewai.ts +102 -0
- package/src/frameworks/eliza.ts +776 -0
- package/src/frameworks/langchain.ts +325 -0
- package/src/index.ts +179 -0
- package/src/mandates/env.ts +222 -0
- package/src/mandates/mandate.ts +280 -0
- package/src/mcp/http-server.ts +38 -0
- package/src/mcp/http.ts +620 -0
- package/src/mcp/server.ts +1006 -0
- package/src/mcp/stdio.ts +8 -0
- package/src/sdk/client.ts +222 -0
- package/src/wallet/provider.ts +197 -0
- package/src/x402/facilitator.ts +336 -0
- package/src/zktls/reclaim.ts +424 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const spendMandateSchema: z.ZodObject<{
|
|
3
|
+
/** Max deposit for any single stream. */
|
|
4
|
+
maxPerStreamUsdc: z.ZodBigInt;
|
|
5
|
+
/** Cumulative cap across all streams opened under this mandate. */
|
|
6
|
+
maxTotalUsdc: z.ZodBigInt;
|
|
7
|
+
/** Max ratePerSecond an agent may agree to. */
|
|
8
|
+
maxRatePerSecondUsdc: z.ZodBigInt;
|
|
9
|
+
/** Max stream duration in seconds. */
|
|
10
|
+
maxDurationSeconds: z.ZodNumber;
|
|
11
|
+
/** Allowed operator addresses; empty/undefined = any operator. */
|
|
12
|
+
allowedOperators: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, `0x${string}`, string>, "many">>;
|
|
13
|
+
/** Mandate expiry, unix seconds. */
|
|
14
|
+
expiresAt: z.ZodNumber;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
maxPerStreamUsdc: bigint;
|
|
17
|
+
maxTotalUsdc: bigint;
|
|
18
|
+
maxRatePerSecondUsdc: bigint;
|
|
19
|
+
maxDurationSeconds: number;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
allowedOperators?: `0x${string}`[] | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
maxPerStreamUsdc: bigint;
|
|
24
|
+
maxTotalUsdc: bigint;
|
|
25
|
+
maxRatePerSecondUsdc: bigint;
|
|
26
|
+
maxDurationSeconds: number;
|
|
27
|
+
expiresAt: number;
|
|
28
|
+
allowedOperators?: string[] | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
export type SpendMandate = z.infer<typeof spendMandateSchema>;
|
|
31
|
+
export declare const signedSpendMandateSchema: z.ZodObject<{
|
|
32
|
+
mandateId: z.ZodEffects<z.ZodString, string, string>;
|
|
33
|
+
owner: z.ZodEffects<z.ZodString, `0x${string}`, string>;
|
|
34
|
+
chainId: z.ZodNumber;
|
|
35
|
+
issuedAt: z.ZodNumber;
|
|
36
|
+
mandate: z.ZodObject<{
|
|
37
|
+
/** Max deposit for any single stream. */
|
|
38
|
+
maxPerStreamUsdc: z.ZodBigInt;
|
|
39
|
+
/** Cumulative cap across all streams opened under this mandate. */
|
|
40
|
+
maxTotalUsdc: z.ZodBigInt;
|
|
41
|
+
/** Max ratePerSecond an agent may agree to. */
|
|
42
|
+
maxRatePerSecondUsdc: z.ZodBigInt;
|
|
43
|
+
/** Max stream duration in seconds. */
|
|
44
|
+
maxDurationSeconds: z.ZodNumber;
|
|
45
|
+
/** Allowed operator addresses; empty/undefined = any operator. */
|
|
46
|
+
allowedOperators: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, `0x${string}`, string>, "many">>;
|
|
47
|
+
/** Mandate expiry, unix seconds. */
|
|
48
|
+
expiresAt: z.ZodNumber;
|
|
49
|
+
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
maxPerStreamUsdc: bigint;
|
|
51
|
+
maxTotalUsdc: bigint;
|
|
52
|
+
maxRatePerSecondUsdc: bigint;
|
|
53
|
+
maxDurationSeconds: number;
|
|
54
|
+
expiresAt: number;
|
|
55
|
+
allowedOperators?: `0x${string}`[] | undefined;
|
|
56
|
+
}, {
|
|
57
|
+
maxPerStreamUsdc: bigint;
|
|
58
|
+
maxTotalUsdc: bigint;
|
|
59
|
+
maxRatePerSecondUsdc: bigint;
|
|
60
|
+
maxDurationSeconds: number;
|
|
61
|
+
expiresAt: number;
|
|
62
|
+
allowedOperators?: string[] | undefined;
|
|
63
|
+
}>;
|
|
64
|
+
signature: z.ZodEffects<z.ZodString, string, string>;
|
|
65
|
+
}, "strip", z.ZodTypeAny, {
|
|
66
|
+
mandateId: string;
|
|
67
|
+
owner: `0x${string}`;
|
|
68
|
+
chainId: number;
|
|
69
|
+
issuedAt: number;
|
|
70
|
+
mandate: {
|
|
71
|
+
maxPerStreamUsdc: bigint;
|
|
72
|
+
maxTotalUsdc: bigint;
|
|
73
|
+
maxRatePerSecondUsdc: bigint;
|
|
74
|
+
maxDurationSeconds: number;
|
|
75
|
+
expiresAt: number;
|
|
76
|
+
allowedOperators?: `0x${string}`[] | undefined;
|
|
77
|
+
};
|
|
78
|
+
signature: string;
|
|
79
|
+
}, {
|
|
80
|
+
mandateId: string;
|
|
81
|
+
owner: string;
|
|
82
|
+
chainId: number;
|
|
83
|
+
issuedAt: number;
|
|
84
|
+
mandate: {
|
|
85
|
+
maxPerStreamUsdc: bigint;
|
|
86
|
+
maxTotalUsdc: bigint;
|
|
87
|
+
maxRatePerSecondUsdc: bigint;
|
|
88
|
+
maxDurationSeconds: number;
|
|
89
|
+
expiresAt: number;
|
|
90
|
+
allowedOperators?: string[] | undefined;
|
|
91
|
+
};
|
|
92
|
+
signature: string;
|
|
93
|
+
}>;
|
|
94
|
+
export type SignedSpendMandate = z.infer<typeof signedSpendMandateSchema>;
|
|
95
|
+
export interface HireRequest {
|
|
96
|
+
operator: `0x${string}`;
|
|
97
|
+
budgetUsdc: bigint;
|
|
98
|
+
ratePerSecondUsdc: bigint;
|
|
99
|
+
maxDurationSeconds: number;
|
|
100
|
+
}
|
|
101
|
+
export interface MandateDecision {
|
|
102
|
+
allowed: boolean;
|
|
103
|
+
/** Machine-readable denial reason; undefined when allowed. */
|
|
104
|
+
reason?: "invalid-signature" | "mandate-revoked" | "mandate-expired" | "exceeds-per-stream-cap" | "exceeds-total-cap" | "exceeds-rate-cap" | "exceeds-duration-cap" | "operator-not-allowed";
|
|
105
|
+
}
|
|
106
|
+
export interface MandateVerificationContext {
|
|
107
|
+
readonly nowSeconds: number;
|
|
108
|
+
readonly revokedMandateIds?: Iterable<`0x${string}`>;
|
|
109
|
+
}
|
|
110
|
+
export declare const spendMandateDomain: {
|
|
111
|
+
readonly name: "MetrikAgentMandate";
|
|
112
|
+
readonly version: "1";
|
|
113
|
+
};
|
|
114
|
+
export declare const spendMandateTypedData: {
|
|
115
|
+
readonly SpendMandate: readonly [{
|
|
116
|
+
readonly name: "mandateId";
|
|
117
|
+
readonly type: "bytes32";
|
|
118
|
+
}, {
|
|
119
|
+
readonly name: "owner";
|
|
120
|
+
readonly type: "address";
|
|
121
|
+
}, {
|
|
122
|
+
readonly name: "issuedAt";
|
|
123
|
+
readonly type: "uint64";
|
|
124
|
+
}, {
|
|
125
|
+
readonly name: "expiresAt";
|
|
126
|
+
readonly type: "uint64";
|
|
127
|
+
}, {
|
|
128
|
+
readonly name: "maxPerStreamUsdc";
|
|
129
|
+
readonly type: "uint256";
|
|
130
|
+
}, {
|
|
131
|
+
readonly name: "maxTotalUsdc";
|
|
132
|
+
readonly type: "uint256";
|
|
133
|
+
}, {
|
|
134
|
+
readonly name: "maxRatePerSecondUsdc";
|
|
135
|
+
readonly type: "uint256";
|
|
136
|
+
}, {
|
|
137
|
+
readonly name: "maxDurationSeconds";
|
|
138
|
+
readonly type: "uint64";
|
|
139
|
+
}, {
|
|
140
|
+
readonly name: "allowedOperatorsHash";
|
|
141
|
+
readonly type: "bytes32";
|
|
142
|
+
}];
|
|
143
|
+
};
|
|
144
|
+
export declare function createSpendMandateTypedData(input: Omit<SignedSpendMandate, "signature">): {
|
|
145
|
+
domain: {
|
|
146
|
+
chainId: number;
|
|
147
|
+
name: "MetrikAgentMandate";
|
|
148
|
+
version: "1";
|
|
149
|
+
};
|
|
150
|
+
primaryType: "SpendMandate";
|
|
151
|
+
types: {
|
|
152
|
+
readonly SpendMandate: readonly [{
|
|
153
|
+
readonly name: "mandateId";
|
|
154
|
+
readonly type: "bytes32";
|
|
155
|
+
}, {
|
|
156
|
+
readonly name: "owner";
|
|
157
|
+
readonly type: "address";
|
|
158
|
+
}, {
|
|
159
|
+
readonly name: "issuedAt";
|
|
160
|
+
readonly type: "uint64";
|
|
161
|
+
}, {
|
|
162
|
+
readonly name: "expiresAt";
|
|
163
|
+
readonly type: "uint64";
|
|
164
|
+
}, {
|
|
165
|
+
readonly name: "maxPerStreamUsdc";
|
|
166
|
+
readonly type: "uint256";
|
|
167
|
+
}, {
|
|
168
|
+
readonly name: "maxTotalUsdc";
|
|
169
|
+
readonly type: "uint256";
|
|
170
|
+
}, {
|
|
171
|
+
readonly name: "maxRatePerSecondUsdc";
|
|
172
|
+
readonly type: "uint256";
|
|
173
|
+
}, {
|
|
174
|
+
readonly name: "maxDurationSeconds";
|
|
175
|
+
readonly type: "uint64";
|
|
176
|
+
}, {
|
|
177
|
+
readonly name: "allowedOperatorsHash";
|
|
178
|
+
readonly type: "bytes32";
|
|
179
|
+
}];
|
|
180
|
+
};
|
|
181
|
+
message: {
|
|
182
|
+
mandateId: `0x${string}`;
|
|
183
|
+
owner: `0x${string}`;
|
|
184
|
+
issuedAt: bigint;
|
|
185
|
+
expiresAt: bigint;
|
|
186
|
+
maxPerStreamUsdc: bigint;
|
|
187
|
+
maxTotalUsdc: bigint;
|
|
188
|
+
maxRatePerSecondUsdc: bigint;
|
|
189
|
+
maxDurationSeconds: bigint;
|
|
190
|
+
allowedOperatorsHash: `0x${string}`;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
export declare function verifySignedMandate(signedMandate: SignedSpendMandate, context: MandateVerificationContext): Promise<MandateDecision>;
|
|
194
|
+
/**
|
|
195
|
+
* Signature-only authorization for BUYER-RECOVERY actions (close / reclaim).
|
|
196
|
+
*
|
|
197
|
+
* Close and reclaim only ever return unspent funds to the buyer or stop further
|
|
198
|
+
* payment — they never move funds to a seller. Gating them on mandate expiry or
|
|
199
|
+
* revocation would let a lapsed/revoked mandate strand the buyer's OWN funds in
|
|
200
|
+
* escrow, defeating the auto-refund guarantee. So these actions verify only that
|
|
201
|
+
* the mandate is authentically owner-signed (proving buyer intent); they do NOT
|
|
202
|
+
* check expiry, revocation, or spend caps. Fail-closed on a bad signature.
|
|
203
|
+
*/
|
|
204
|
+
export declare function verifyMandateOwnerSignature(signedMandate: SignedSpendMandate): Promise<MandateDecision>;
|
|
205
|
+
/**
|
|
206
|
+
* Full mandate authorization check. A mandate must be owner-signed, unrevoked,
|
|
207
|
+
* and unexpired before the policy engine runs. Fail-closed: any breach denies.
|
|
208
|
+
*/
|
|
209
|
+
export declare function checkMandate(signedMandate: SignedSpendMandate, request: HireRequest, spentSoFarUsdc: bigint, context: MandateVerificationContext): Promise<MandateDecision>;
|
|
210
|
+
/**
|
|
211
|
+
* Pure policy check over an already-verified mandate.
|
|
212
|
+
*/
|
|
213
|
+
export declare function checkMandatePolicy(mandate: SpendMandate, request: HireRequest, spentSoFarUsdc: bigint, nowSeconds: number): MandateDecision;
|
|
214
|
+
//# sourceMappingURL=mandate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandate.d.ts","sourceRoot":"","sources":["../../src/mandates/mandate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAwBxB,eAAO,MAAM,kBAAkB;IAC7B,yCAAyC;;IAEzC,mEAAmE;;IAEnE,+CAA+C;;IAE/C,sCAAsC;;IAEtC,kEAAkE;;IAElE,oCAAoC;;;;;;;;;;;;;;;;EAEpC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,wBAAwB;;;;;;QAhBnC,yCAAyC;;QAEzC,mEAAmE;;QAEnE,+CAA+C;;QAE/C,sCAAsC;;QAEtC,kEAAkE;;QAElE,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAapC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,MAAM,CAAC,EACH,mBAAmB,GACnB,iBAAiB,GACjB,iBAAiB,GACjB,wBAAwB,GACxB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,sBAAsB,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;CACtD;AAED,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYxB,CAAC;AAIX,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAcT,KAAK,MAAM,EAAE;;;;;;;;;;EAajD;AAED,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,kBAAkB,EACjC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,kBAAkB,GAChC,OAAO,CAAC,eAAe,CAAC,CAe1B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,kBAAkB,EACjC,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAY1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GACjB,eAAe,CAyBjB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { encodeAbiParameters, getAddress, isAddress, isHex, keccak256, recoverTypedDataAddress, } from "viem";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Spend mandate — the guardrail that lets an AI agent transact autonomously while
|
|
5
|
+
* its owner stays in control. A signed mandate caps how much, how fast, for how
|
|
6
|
+
* long, and with whom an agent may open verified payment streams.
|
|
7
|
+
*/
|
|
8
|
+
const addressSchema = z
|
|
9
|
+
.string()
|
|
10
|
+
.refine((value) => isAddress(value), "must be an EVM address");
|
|
11
|
+
const bytes32Schema = z
|
|
12
|
+
.string()
|
|
13
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 66, "must be bytes32 hex");
|
|
14
|
+
const signatureSchema = z
|
|
15
|
+
.string()
|
|
16
|
+
.refine((value) => isHex(value, { strict: true }) && value.length === 132, "must be a 65-byte signature");
|
|
17
|
+
export const spendMandateSchema = z.object({
|
|
18
|
+
/** Max deposit for any single stream. */
|
|
19
|
+
maxPerStreamUsdc: z.bigint().positive(),
|
|
20
|
+
/** Cumulative cap across all streams opened under this mandate. */
|
|
21
|
+
maxTotalUsdc: z.bigint().positive(),
|
|
22
|
+
/** Max ratePerSecond an agent may agree to. */
|
|
23
|
+
maxRatePerSecondUsdc: z.bigint().positive(),
|
|
24
|
+
/** Max stream duration in seconds. */
|
|
25
|
+
maxDurationSeconds: z.number().int().positive(),
|
|
26
|
+
/** Allowed operator addresses; empty/undefined = any operator. */
|
|
27
|
+
allowedOperators: z.array(addressSchema).optional(),
|
|
28
|
+
/** Mandate expiry, unix seconds. */
|
|
29
|
+
expiresAt: z.number().int().positive(),
|
|
30
|
+
});
|
|
31
|
+
export const signedSpendMandateSchema = z.object({
|
|
32
|
+
mandateId: bytes32Schema,
|
|
33
|
+
owner: addressSchema,
|
|
34
|
+
chainId: z.number().int().positive(),
|
|
35
|
+
issuedAt: z.number().int().nonnegative(),
|
|
36
|
+
mandate: spendMandateSchema,
|
|
37
|
+
signature: signatureSchema,
|
|
38
|
+
});
|
|
39
|
+
export const spendMandateDomain = {
|
|
40
|
+
name: "MetrikAgentMandate",
|
|
41
|
+
version: "1",
|
|
42
|
+
};
|
|
43
|
+
export const spendMandateTypedData = {
|
|
44
|
+
SpendMandate: [
|
|
45
|
+
{ name: "mandateId", type: "bytes32" },
|
|
46
|
+
{ name: "owner", type: "address" },
|
|
47
|
+
{ name: "issuedAt", type: "uint64" },
|
|
48
|
+
{ name: "expiresAt", type: "uint64" },
|
|
49
|
+
{ name: "maxPerStreamUsdc", type: "uint256" },
|
|
50
|
+
{ name: "maxTotalUsdc", type: "uint256" },
|
|
51
|
+
{ name: "maxRatePerSecondUsdc", type: "uint256" },
|
|
52
|
+
{ name: "maxDurationSeconds", type: "uint64" },
|
|
53
|
+
{ name: "allowedOperatorsHash", type: "bytes32" },
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
const ALLOW = { allowed: true };
|
|
57
|
+
export function createSpendMandateTypedData(input) {
|
|
58
|
+
const parsed = signedSpendMandateSchema
|
|
59
|
+
.omit({ signature: true })
|
|
60
|
+
.parse(input);
|
|
61
|
+
return {
|
|
62
|
+
domain: {
|
|
63
|
+
...spendMandateDomain,
|
|
64
|
+
chainId: parsed.chainId,
|
|
65
|
+
},
|
|
66
|
+
primaryType: "SpendMandate",
|
|
67
|
+
types: spendMandateTypedData,
|
|
68
|
+
message: {
|
|
69
|
+
mandateId: parsed.mandateId,
|
|
70
|
+
owner: getAddress(parsed.owner),
|
|
71
|
+
issuedAt: BigInt(parsed.issuedAt),
|
|
72
|
+
expiresAt: BigInt(parsed.mandate.expiresAt),
|
|
73
|
+
maxPerStreamUsdc: parsed.mandate.maxPerStreamUsdc,
|
|
74
|
+
maxTotalUsdc: parsed.mandate.maxTotalUsdc,
|
|
75
|
+
maxRatePerSecondUsdc: parsed.mandate.maxRatePerSecondUsdc,
|
|
76
|
+
maxDurationSeconds: BigInt(parsed.mandate.maxDurationSeconds),
|
|
77
|
+
allowedOperatorsHash: hashAllowedOperators(parsed.mandate.allowedOperators),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export async function verifySignedMandate(signedMandate, context) {
|
|
82
|
+
const parsedMandate = signedSpendMandateSchema.parse(signedMandate);
|
|
83
|
+
const recovered = await recoverTypedDataAddress({
|
|
84
|
+
...createSpendMandateTypedData(parsedMandate),
|
|
85
|
+
signature: parsedMandate.signature,
|
|
86
|
+
}).catch(() => null);
|
|
87
|
+
if (recovered === null ||
|
|
88
|
+
recovered.toLowerCase() !== parsedMandate.owner.toLowerCase()) {
|
|
89
|
+
return { allowed: false, reason: "invalid-signature" };
|
|
90
|
+
}
|
|
91
|
+
if (isRevoked(parsedMandate.mandateId, context.revokedMandateIds)) {
|
|
92
|
+
return { allowed: false, reason: "mandate-revoked" };
|
|
93
|
+
}
|
|
94
|
+
if (context.nowSeconds >= parsedMandate.mandate.expiresAt) {
|
|
95
|
+
return { allowed: false, reason: "mandate-expired" };
|
|
96
|
+
}
|
|
97
|
+
return ALLOW;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Signature-only authorization for BUYER-RECOVERY actions (close / reclaim).
|
|
101
|
+
*
|
|
102
|
+
* Close and reclaim only ever return unspent funds to the buyer or stop further
|
|
103
|
+
* payment — they never move funds to a seller. Gating them on mandate expiry or
|
|
104
|
+
* revocation would let a lapsed/revoked mandate strand the buyer's OWN funds in
|
|
105
|
+
* escrow, defeating the auto-refund guarantee. So these actions verify only that
|
|
106
|
+
* the mandate is authentically owner-signed (proving buyer intent); they do NOT
|
|
107
|
+
* check expiry, revocation, or spend caps. Fail-closed on a bad signature.
|
|
108
|
+
*/
|
|
109
|
+
export async function verifyMandateOwnerSignature(signedMandate) {
|
|
110
|
+
const parsed = signedSpendMandateSchema.parse(signedMandate);
|
|
111
|
+
const recovered = await recoverTypedDataAddress({
|
|
112
|
+
...createSpendMandateTypedData(parsed),
|
|
113
|
+
signature: parsed.signature,
|
|
114
|
+
}).catch(() => null);
|
|
115
|
+
if (recovered === null ||
|
|
116
|
+
recovered.toLowerCase() !== parsed.owner.toLowerCase()) {
|
|
117
|
+
return { allowed: false, reason: "invalid-signature" };
|
|
118
|
+
}
|
|
119
|
+
return ALLOW;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Full mandate authorization check. A mandate must be owner-signed, unrevoked,
|
|
123
|
+
* and unexpired before the policy engine runs. Fail-closed: any breach denies.
|
|
124
|
+
*/
|
|
125
|
+
export async function checkMandate(signedMandate, request, spentSoFarUsdc, context) {
|
|
126
|
+
const signatureDecision = await verifySignedMandate(signedMandate, context);
|
|
127
|
+
if (!signatureDecision.allowed) {
|
|
128
|
+
return signatureDecision;
|
|
129
|
+
}
|
|
130
|
+
return checkMandatePolicy(signedMandate.mandate, request, spentSoFarUsdc, context.nowSeconds);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Pure policy check over an already-verified mandate.
|
|
134
|
+
*/
|
|
135
|
+
export function checkMandatePolicy(mandate, request, spentSoFarUsdc, nowSeconds) {
|
|
136
|
+
if (nowSeconds >= mandate.expiresAt)
|
|
137
|
+
return { allowed: false, reason: "mandate-expired" };
|
|
138
|
+
if (request.budgetUsdc > mandate.maxPerStreamUsdc) {
|
|
139
|
+
return { allowed: false, reason: "exceeds-per-stream-cap" };
|
|
140
|
+
}
|
|
141
|
+
if (spentSoFarUsdc + request.budgetUsdc > mandate.maxTotalUsdc) {
|
|
142
|
+
return { allowed: false, reason: "exceeds-total-cap" };
|
|
143
|
+
}
|
|
144
|
+
if (request.ratePerSecondUsdc > mandate.maxRatePerSecondUsdc) {
|
|
145
|
+
return { allowed: false, reason: "exceeds-rate-cap" };
|
|
146
|
+
}
|
|
147
|
+
if (request.maxDurationSeconds > mandate.maxDurationSeconds) {
|
|
148
|
+
return { allowed: false, reason: "exceeds-duration-cap" };
|
|
149
|
+
}
|
|
150
|
+
if (mandate.allowedOperators &&
|
|
151
|
+
mandate.allowedOperators.length > 0 &&
|
|
152
|
+
!mandate.allowedOperators.some((operator) => operator.toLowerCase() === request.operator.toLowerCase())) {
|
|
153
|
+
return { allowed: false, reason: "operator-not-allowed" };
|
|
154
|
+
}
|
|
155
|
+
return ALLOW;
|
|
156
|
+
}
|
|
157
|
+
function hashAllowedOperators(operators) {
|
|
158
|
+
const normalized = (operators ?? []).map((operator) => getAddress(operator));
|
|
159
|
+
return keccak256(encodeAbiParameters([{ type: "address[]" }], [normalized]));
|
|
160
|
+
}
|
|
161
|
+
function isRevoked(mandateId, revokedMandateIds) {
|
|
162
|
+
if (revokedMandateIds === undefined) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
for (const revokedMandateId of revokedMandateIds) {
|
|
166
|
+
if (revokedMandateId.toLowerCase() === mandateId.toLowerCase()) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=mandate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandate.js","sourceRoot":"","sources":["../../src/mandates/mandate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../../src/mcp/http-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { startHostedMcpHttpServerFromEnv } from "./http.js";
|
|
2
|
+
/**
|
|
3
|
+
* Runnable entrypoint for the hosted, multi-tenant MCP server over Streamable
|
|
4
|
+
* HTTP. Reads all configuration from the environment (see
|
|
5
|
+
* {@link parseHostedMcpServerConfig}) and refuses to start when no tenant
|
|
6
|
+
* tokens are configured (fail-closed).
|
|
7
|
+
*
|
|
8
|
+
* Start with: `pnpm mcp:http` (or `node dist/mcp/http-server.js`).
|
|
9
|
+
*/
|
|
10
|
+
async function main() {
|
|
11
|
+
const server = await startHostedMcpHttpServerFromEnv();
|
|
12
|
+
const path = process.env.METRIK_MCP_PATH ?? "/mcp";
|
|
13
|
+
const host = process.env.METRIK_MCP_HOST ?? "0.0.0.0";
|
|
14
|
+
// Config only — never log tokens or wallet secrets.
|
|
15
|
+
console.error(`[metrik-agent-mcp-http] listening on http://${host}:${server.boundPort}${path}`);
|
|
16
|
+
const shutdown = (signal) => {
|
|
17
|
+
console.error(`[metrik-agent-mcp-http] ${signal} received — shutting down`);
|
|
18
|
+
server
|
|
19
|
+
.close()
|
|
20
|
+
.then(() => process.exit(0))
|
|
21
|
+
.catch(() => process.exit(1));
|
|
22
|
+
};
|
|
23
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
24
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
25
|
+
}
|
|
26
|
+
main().catch((error) => {
|
|
27
|
+
console.error(error instanceof Error
|
|
28
|
+
? error.message
|
|
29
|
+
: "failed to start hosted MCP server");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=http-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/mcp/http-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,WAAW,CAAC;AAE5D;;;;;;;GAOG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,+BAA+B,EAAE,CAAC;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;IACtD,oDAAoD;IACpD,OAAO,CAAC,KAAK,CACX,+CAA+C,IAAI,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,CACjF,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAE;QAClC,OAAO,CAAC,KAAK,CAAC,2BAA2B,MAAM,2BAA2B,CAAC,CAAC;QAC5E,MAAM;aACH,KAAK,EAAE;aACP,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC3B,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CACX,KAAK,YAAY,KAAK;QACpB,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,mCAAmC,CACxC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import { type VerifiedStreamMcpServerOptions } from "./server.js";
|
|
3
|
+
/**
|
|
4
|
+
* A single authenticated tenant: a bearer token plus the wallet/mandate-scoped
|
|
5
|
+
* MCP server options used to build that tenant's server instances.
|
|
6
|
+
*/
|
|
7
|
+
export interface HostedMcpTenant {
|
|
8
|
+
/** Stable, human-readable tenant identifier (used only for routing/logging). */
|
|
9
|
+
readonly id: string;
|
|
10
|
+
/** The tenant's secret bearer token. Never logged. */
|
|
11
|
+
readonly token: string;
|
|
12
|
+
/**
|
|
13
|
+
* The tenant's isolated MCP server options (its own agent client + registry).
|
|
14
|
+
* A fresh `McpServer` is built from these for each of the tenant's sessions.
|
|
15
|
+
*/
|
|
16
|
+
readonly options: VerifiedStreamMcpServerOptions;
|
|
17
|
+
}
|
|
18
|
+
/** Resolves a presented bearer token to a tenant, or `null` when invalid. */
|
|
19
|
+
export interface TenantResolver {
|
|
20
|
+
authenticate(token: string): HostedMcpTenant | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Constant-time, env-backed tenant resolver.
|
|
24
|
+
*
|
|
25
|
+
* Tokens are pre-hashed with SHA-256 (fixed 32-byte width) so every comparison
|
|
26
|
+
* uses `timingSafeEqual` on equal-length buffers, and the loop always visits
|
|
27
|
+
* every tenant with no early exit — neither the presented token's length nor
|
|
28
|
+
* which tenant matched is observable via timing.
|
|
29
|
+
*/
|
|
30
|
+
export declare class StaticTenantResolver implements TenantResolver {
|
|
31
|
+
private readonly entries;
|
|
32
|
+
constructor(tenants: readonly HostedMcpTenant[]);
|
|
33
|
+
authenticate(token: string): HostedMcpTenant | null;
|
|
34
|
+
}
|
|
35
|
+
export interface HostedMcpHttpServerOptions {
|
|
36
|
+
readonly resolver: TenantResolver;
|
|
37
|
+
/** HTTP path the MCP endpoint is served on. Default `/mcp`. */
|
|
38
|
+
readonly path?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Allowed CORS origins. `["*"]` allows any origin. When omitted, no
|
|
41
|
+
* cross-origin `Access-Control-Allow-Origin` header is emitted (same-origin
|
|
42
|
+
* / non-browser clients only).
|
|
43
|
+
*/
|
|
44
|
+
readonly allowedOrigins?: readonly string[];
|
|
45
|
+
/** Session id generator. Default: cryptographically-random UUID v4. */
|
|
46
|
+
readonly generateSessionId?: () => string;
|
|
47
|
+
}
|
|
48
|
+
export interface HostedMcpHttpServer {
|
|
49
|
+
readonly httpServer: Server;
|
|
50
|
+
/** Bind and start listening. Resolves with the actual bound address. */
|
|
51
|
+
listen(port: number, host?: string): Promise<{
|
|
52
|
+
port: number;
|
|
53
|
+
host: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Stop accepting requests and tear down all live sessions. */
|
|
56
|
+
close(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates (but does not start) a hosted multi-tenant MCP HTTP server.
|
|
60
|
+
*
|
|
61
|
+
* Use {@link HostedMcpHttpServer.listen} to bind it. The returned server is
|
|
62
|
+
* suitable for tests (bind to port 0 for an ephemeral port) and for
|
|
63
|
+
* deployment (bind to `0.0.0.0`/`PORT`).
|
|
64
|
+
*/
|
|
65
|
+
export declare function createHostedMcpHttpServer(options: HostedMcpHttpServerOptions): HostedMcpHttpServer;
|
|
66
|
+
/**
|
|
67
|
+
* Per-tenant configuration parsed from the hosted server environment.
|
|
68
|
+
*
|
|
69
|
+
* Each tenant carries the environment slice (`METRIK_AGENT_*`, wallet vars,
|
|
70
|
+
* optional Reclaim vars) that builds its OWN wallet-backed agent client, so
|
|
71
|
+
* tenants are fully fund-isolated from one another.
|
|
72
|
+
*/
|
|
73
|
+
export interface HostedMcpTenantConfig {
|
|
74
|
+
readonly id: string;
|
|
75
|
+
readonly token: string;
|
|
76
|
+
readonly env: Readonly<Record<string, string>>;
|
|
77
|
+
}
|
|
78
|
+
export interface HostedMcpServerConfig {
|
|
79
|
+
readonly host: string;
|
|
80
|
+
readonly port: number;
|
|
81
|
+
readonly path: string;
|
|
82
|
+
readonly allowedOrigins: readonly string[];
|
|
83
|
+
readonly tenants: readonly HostedMcpTenantConfig[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parses the hosted MCP server configuration from environment variables.
|
|
87
|
+
*
|
|
88
|
+
* Env contract:
|
|
89
|
+
* - `PORT` / `METRIK_MCP_PORT` — listen port (default 8080).
|
|
90
|
+
* - `METRIK_MCP_HOST` — bind host (default `0.0.0.0`).
|
|
91
|
+
* - `METRIK_MCP_PATH` — MCP endpoint path (default `/mcp`).
|
|
92
|
+
* - `METRIK_MCP_ALLOWED_ORIGINS` — comma-separated CORS origins, or `*`.
|
|
93
|
+
* - `METRIK_MCP_TENANTS` — JSON array of
|
|
94
|
+
* `{ "id": string, "token": string, "env"?: Record<string,string> }`.
|
|
95
|
+
* Each tenant's `env` is layered over the process environment and used to
|
|
96
|
+
* build that tenant's isolated, wallet-backed agent client.
|
|
97
|
+
*
|
|
98
|
+
* Throws (fail-closed) when no tenants/tokens are configured.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseHostedMcpServerConfig(env?: NodeJS.ProcessEnv): HostedMcpServerConfig;
|
|
101
|
+
/**
|
|
102
|
+
* Builds fully-isolated {@link HostedMcpTenant}s from parsed config, resolving
|
|
103
|
+
* each tenant's wallet-backed agent client from its own environment slice.
|
|
104
|
+
*/
|
|
105
|
+
export declare function createHostedMcpTenants(config: HostedMcpServerConfig, baseEnv?: NodeJS.ProcessEnv): Promise<HostedMcpTenant[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Builds and starts a hosted MCP HTTP server entirely from the environment.
|
|
108
|
+
*
|
|
109
|
+
* Fail-closed: throws before binding if no tenants/tokens are configured.
|
|
110
|
+
*/
|
|
111
|
+
export declare function startHostedMcpHttpServerFromEnv(env?: NodeJS.ProcessEnv): Promise<HostedMcpHttpServer & {
|
|
112
|
+
readonly boundPort: number;
|
|
113
|
+
}>;
|
|
114
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/mcp/http.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,MAAM,EAEZ,MAAM,WAAW,CAAC;AAOnB,OAAO,EAGL,KAAK,8BAA8B,EAEpC,MAAM,aAAa,CAAC;AA4BrB;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,8BAA8B,CAAC;CAClD;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;CACrD;AAMD;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAGpB;gBAEQ,OAAO,EAAE,SAAS,eAAe,EAAE;IA+B/C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;CAYpD;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,uEAAuE;IACvE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;CAC3C;AAQD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,+DAA+D;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,mBAAmB,CAkDrB;AA6PD;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,qBAAqB,CAoEvB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,MAAM,CAAC,UAAwB,GACvC,OAAO,CAAC,eAAe,EAAE,CAAC,CAc5B;AAED;;;;GAIG;AACH,wBAAsB,+BAA+B,CACnD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,mBAAmB,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAW/D"}
|