@attest-dev/sdk 0.1.0-beta.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 +119 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +312 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +599 -0
- package/dist/mcp.js.map +1 -0
- package/package.json +38 -0
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @attest-dev/sdk/mcp — Attest credential enforcement middleware for MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* Wraps any MCP server instance and enforces Attest credential checking on
|
|
5
|
+
* every tool call before the underlying handler executes.
|
|
6
|
+
*
|
|
7
|
+
* ## Two-line integration
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
* import { withAttest } from "@attest-dev/sdk/mcp";
|
|
12
|
+
*
|
|
13
|
+
* const server = new McpServer({ name: "my-tools", version: "1.0.0" });
|
|
14
|
+
* const protectedServer = withAttest(server, {
|
|
15
|
+
* issuerUri: "https://api.attest.dev",
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Register tools exactly as before — every call is now credential-gated.
|
|
19
|
+
* protectedServer.tool("send_email", "Send an email", schema, handler);
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## How it works
|
|
23
|
+
*
|
|
24
|
+
* `withAttest` monkey-patches `server.tool()` so each registered handler is
|
|
25
|
+
* wrapped with a credential check closure. On every tool call the closure:
|
|
26
|
+
*
|
|
27
|
+
* 1. Extracts the Attest JWT from `extra.authInfo.token` (set by the MCP
|
|
28
|
+
* auth middleware) or `extra.meta?.attest_token`.
|
|
29
|
+
* 2. Verifies the JWT offline against the issuer's JWKS (cached per TTL).
|
|
30
|
+
* 3. Maps the tool name to a Attest scope string via `scopeForTool()`.
|
|
31
|
+
* 4. Confirms the credential's `att_scope` covers the required scope.
|
|
32
|
+
* 5. Calls the Attest revocation endpoint to confirm the JTI is still live.
|
|
33
|
+
* 6. If all checks pass, executes the original handler.
|
|
34
|
+
* 7. If any check fails, returns a structured `attest_violation` error.
|
|
35
|
+
* 8. Fire-and-forgets an audit event to the Attest server regardless of
|
|
36
|
+
* outcome.
|
|
37
|
+
*/
|
|
38
|
+
/** Minimal shape of the auth info injected by the MCP auth middleware. */
|
|
39
|
+
interface McpAuthInfo {
|
|
40
|
+
/** Raw bearer token string (the Attest JWT). */
|
|
41
|
+
token: string;
|
|
42
|
+
clientId?: string;
|
|
43
|
+
scopes?: string[];
|
|
44
|
+
expiresAt?: Date;
|
|
45
|
+
extra?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
/** Minimal shape of the extra argument passed to every MCP tool handler. */
|
|
48
|
+
interface McpRequestExtra {
|
|
49
|
+
/** Populated by MCP auth middleware from the Authorization header. */
|
|
50
|
+
authInfo?: McpAuthInfo | undefined;
|
|
51
|
+
/** Arbitrary metadata; agents may pass attest_token here explicitly. */
|
|
52
|
+
meta?: Record<string, unknown> | undefined;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
sessionId?: string;
|
|
55
|
+
requestId?: string | number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Minimal interface for any object that behaves like an MCP server.
|
|
59
|
+
* `withAttest` accepts anything with a `tool()` method.
|
|
60
|
+
*/
|
|
61
|
+
export interface McpServerLike {
|
|
62
|
+
tool: (...args: unknown[]) => unknown;
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
}
|
|
65
|
+
/** The decoded Attest JWT claims threaded through the MCP request. */
|
|
66
|
+
export interface AttestContext {
|
|
67
|
+
/** Unique identifier for this credential. */
|
|
68
|
+
jti: string;
|
|
69
|
+
/** Task tree ID shared across the entire delegation chain. */
|
|
70
|
+
att_tid: string;
|
|
71
|
+
/** Delegation depth (0 = root credential). */
|
|
72
|
+
att_depth: number;
|
|
73
|
+
/** Granted permission scopes in "resource:action" form. */
|
|
74
|
+
att_scope: string[];
|
|
75
|
+
/** Human user who initiated the task. */
|
|
76
|
+
att_uid: string;
|
|
77
|
+
/** Raw JWT string. */
|
|
78
|
+
token: string;
|
|
79
|
+
}
|
|
80
|
+
/** Emitted to `onViolation` when a tool call is blocked. */
|
|
81
|
+
export interface ScopeViolationEvent {
|
|
82
|
+
/** MCP tool name that was blocked. */
|
|
83
|
+
toolName: string;
|
|
84
|
+
/** Attest scope required by this tool. */
|
|
85
|
+
requiredScope: string;
|
|
86
|
+
/** Scopes actually present in the credential (empty if no credential). */
|
|
87
|
+
grantedScope: string[];
|
|
88
|
+
/** Violation category. */
|
|
89
|
+
reason: 'no_credential' | 'credential_expired' | 'credential_revoked' | 'scope_violation' | 'invalid_credential' | 'audit_failure';
|
|
90
|
+
/** Credential JTI if available. */
|
|
91
|
+
jti?: string;
|
|
92
|
+
/** Task ID if available. */
|
|
93
|
+
taskId?: string;
|
|
94
|
+
/** ISO timestamp of the violation. */
|
|
95
|
+
timestamp: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Options for `withAttest`.
|
|
99
|
+
*/
|
|
100
|
+
export interface AttestMcpOptions {
|
|
101
|
+
/**
|
|
102
|
+
* URI of the Attest server used to fetch JWKS and check revocation.
|
|
103
|
+
* @example "https://api.attest.dev"
|
|
104
|
+
*/
|
|
105
|
+
issuerUri: string;
|
|
106
|
+
/**
|
|
107
|
+
* How long (in seconds) to cache the JWKS before re-fetching.
|
|
108
|
+
* Longer values reduce latency; shorter values pick up key rotations faster.
|
|
109
|
+
* @default 3600
|
|
110
|
+
*/
|
|
111
|
+
jwksCacheTTL?: number;
|
|
112
|
+
/**
|
|
113
|
+
* When `true` (the default), tool calls without a valid Attest credential
|
|
114
|
+
* are blocked and a `attest_violation` error is returned.
|
|
115
|
+
*
|
|
116
|
+
* When `false`, violations are logged via `onViolation` but the tool call
|
|
117
|
+
* proceeds. Useful for gradual rollouts or debugging.
|
|
118
|
+
* @default true
|
|
119
|
+
*/
|
|
120
|
+
requireCredential?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Called synchronously when any scope check fails.
|
|
123
|
+
* Use this to emit metrics, write logs, or trigger alerts.
|
|
124
|
+
*/
|
|
125
|
+
onViolation?: (event: ScopeViolationEvent) => void;
|
|
126
|
+
/**
|
|
127
|
+
* Per-tool scope overrides. Keys are exact MCP tool names; values are
|
|
128
|
+
* Attest scope strings. Overrides the automatic `scopeForTool()` mapping.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* toolScopeMap: {
|
|
132
|
+
* "send_message": "gmail:send",
|
|
133
|
+
* "query_db": "postgres:read",
|
|
134
|
+
* }
|
|
135
|
+
*/
|
|
136
|
+
toolScopeMap?: Record<string, string>;
|
|
137
|
+
/**
|
|
138
|
+
* How long (in seconds) to cache revocation status for each JTI.
|
|
139
|
+
* Revocation is eventually consistent; a short TTL (5-30 s) balances
|
|
140
|
+
* latency against revocation lag. Set to 0 to disable caching.
|
|
141
|
+
* @default 10
|
|
142
|
+
*/
|
|
143
|
+
revocationCacheTTL?: number;
|
|
144
|
+
/**
|
|
145
|
+
* Called when an audit POST to the Attest server fails.
|
|
146
|
+
* Receives the underlying error and the event payload that was not delivered.
|
|
147
|
+
*
|
|
148
|
+
* Use this to implement a retry queue, write to a fallback log, or page
|
|
149
|
+
* on-call for compliance-critical deployments.
|
|
150
|
+
*
|
|
151
|
+
* If omitted, failures are surfaced via `onViolation` (with
|
|
152
|
+
* `reason: "audit_failure"`) and, if that is also unset, via
|
|
153
|
+
* `console.warn` — audit failures are never silently dropped.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* onAuditError: (err, event) => {
|
|
158
|
+
* retryQueue.push({ event, attempts: 0 });
|
|
159
|
+
* logger.error("audit delivery failed", { err, jti: event.jti });
|
|
160
|
+
* }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
onAuditError?: (error: Error, event: AuditPayload) => void;
|
|
164
|
+
}
|
|
165
|
+
/** Structured error payload returned in the tool response body on violation. */
|
|
166
|
+
export interface AttestViolationError {
|
|
167
|
+
error: 'attest_violation';
|
|
168
|
+
reason: ScopeViolationEvent['reason'];
|
|
169
|
+
detail: string;
|
|
170
|
+
jti?: string;
|
|
171
|
+
taskId?: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Optional Attest-specific options passed as the final argument to
|
|
175
|
+
* `protectedServer.tool()`. The MCP SDK never sees this object — it is
|
|
176
|
+
* extracted and consumed by the `withAttest` wrapper before forwarding
|
|
177
|
+
* the remaining arguments.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* protectedServer.tool("gh_create_issue", schema, handler, {
|
|
182
|
+
* requiredScope: "github:write",
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export interface AttestToolOptions {
|
|
187
|
+
/**
|
|
188
|
+
* Explicit Attest scope string required to call this tool.
|
|
189
|
+
* Overrides the automatic `scopeForTool()` mapping.
|
|
190
|
+
* Use this for non-standard tool names or when the auto-mapping would
|
|
191
|
+
* produce a misleading scope.
|
|
192
|
+
*
|
|
193
|
+
* @example "github:write"
|
|
194
|
+
* @example "stripe:charge"
|
|
195
|
+
*/
|
|
196
|
+
requiredScope?: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Maps an MCP tool name to a Attest scope string using a convention-based
|
|
200
|
+
* approach. The tool name is split on the first underscore: the part before
|
|
201
|
+
* becomes the action; the rest (joined with `:`) becomes the resource.
|
|
202
|
+
*
|
|
203
|
+
* Action aliases:
|
|
204
|
+
* - `create`, `update`, `write`, `put`, `patch` → `write`
|
|
205
|
+
* - `remove`, `destroy` → `delete`
|
|
206
|
+
* - `run`, `invoke` → `execute`
|
|
207
|
+
* - `get`, `fetch`, `list`, `search`, `query` → `read`
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* scopeForTool("send_email") // "email:send"
|
|
211
|
+
* scopeForTool("read_file") // "file:read"
|
|
212
|
+
* scopeForTool("delete_calendar_event") // "calendar_event:delete"
|
|
213
|
+
* scopeForTool("create_user") // "user:write"
|
|
214
|
+
* scopeForTool("run_query") // "query:execute"
|
|
215
|
+
*/
|
|
216
|
+
export declare function scopeForTool(toolName: string, overrides?: Record<string, string>): string;
|
|
217
|
+
/**
|
|
218
|
+
* The audit event payload sent to `POST /v1/audit` on the Attest server.
|
|
219
|
+
* Passed to `onAuditError` when delivery fails so the caller can retry or
|
|
220
|
+
* write to a fallback log.
|
|
221
|
+
*/
|
|
222
|
+
export interface AuditPayload {
|
|
223
|
+
event_type: 'verified' | 'revoked';
|
|
224
|
+
jti: string;
|
|
225
|
+
att_tid?: string | undefined;
|
|
226
|
+
att_uid?: string | undefined;
|
|
227
|
+
agent_id: string;
|
|
228
|
+
scope: string[];
|
|
229
|
+
meta?: Record<string, string> | undefined;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Wraps an MCP server instance and enforces Attest credential checking on
|
|
233
|
+
* every tool call.
|
|
234
|
+
*
|
|
235
|
+
* Returns the **same** server object with its `tool()` method patched in place,
|
|
236
|
+
* typed as the original server type so all other methods remain accessible.
|
|
237
|
+
*
|
|
238
|
+
* @param server Any object with a `tool()` method (e.g. `new McpServer(...)`).
|
|
239
|
+
* @param options Attest enforcement options.
|
|
240
|
+
* @returns The patched server (same reference, same type).
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
245
|
+
* import { withAttest } from "@attest-dev/sdk/mcp";
|
|
246
|
+
*
|
|
247
|
+
* const server = new McpServer({ name: "my-tools", version: "1.0.0" });
|
|
248
|
+
* const protectedServer = withAttest(server, {
|
|
249
|
+
* issuerUri: "https://api.attest.dev",
|
|
250
|
+
* });
|
|
251
|
+
*
|
|
252
|
+
* protectedServer.tool("send_email", schema, async (args, extra) => {
|
|
253
|
+
* // Only reached when the caller holds a valid credential
|
|
254
|
+
* // with "email:send" in its att_scope.
|
|
255
|
+
* return { content: [{ type: "text", text: "sent!" }] };
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
export declare function withAttest<T extends McpServerLike>(server: T, options: AttestMcpOptions): T;
|
|
260
|
+
/**
|
|
261
|
+
* Decodes the Attest JWT in `extra` and returns a typed `AttestContext`
|
|
262
|
+
* without performing any cryptographic verification.
|
|
263
|
+
*
|
|
264
|
+
* Useful inside tool handlers when you want to read the credential's claims
|
|
265
|
+
* (e.g. `att_uid`, `att_tid`) after `withAttest` has already enforced them.
|
|
266
|
+
*
|
|
267
|
+
* @returns `null` when no Attest credential is present.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* protectedServer.tool("send_email", schema, async (args, extra) => {
|
|
272
|
+
* const ctx = getAttestContext(extra);
|
|
273
|
+
* console.log("acting on behalf of", ctx?.att_uid);
|
|
274
|
+
* ...
|
|
275
|
+
* });
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
export declare function getAttestContext(extra: McpRequestExtra): AttestContext | null;
|
|
279
|
+
/**
|
|
280
|
+
* Returns the scope registry for a server that has been wrapped with
|
|
281
|
+
* `withAttest` — a map of every registered tool name to its resolved
|
|
282
|
+
* Attest scope string.
|
|
283
|
+
*
|
|
284
|
+
* Use this to build a scope discovery endpoint so credential issuers can
|
|
285
|
+
* query what scopes a server requires without out-of-band coordination.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* // Express / Hono / any HTTP framework:
|
|
290
|
+
* app.get("/.well-known/attest-scopes", (_req, res) => {
|
|
291
|
+
* res.json({ tools: getAttestScopes(protectedServer) });
|
|
292
|
+
* });
|
|
293
|
+
*
|
|
294
|
+
* // Response:
|
|
295
|
+
* // {
|
|
296
|
+
* // "tools": {
|
|
297
|
+
* // "send_email": "email:send",
|
|
298
|
+
* // "read_file": "file:read",
|
|
299
|
+
* // "gh_create_issue": "github:write"
|
|
300
|
+
* // }
|
|
301
|
+
* // }
|
|
302
|
+
* ```
|
|
303
|
+
*
|
|
304
|
+
* Tools that have not yet been registered (i.e. `server.tool()` hasn't been
|
|
305
|
+
* called for them yet) will not appear in the result. Call this after all
|
|
306
|
+
* tools are registered, not during server startup.
|
|
307
|
+
*
|
|
308
|
+
* @returns A plain `Record<string, string>` safe to serialize directly as JSON.
|
|
309
|
+
*/
|
|
310
|
+
export declare function getAttestScopes(server: McpServerLike): Record<string, string>;
|
|
311
|
+
export {};
|
|
312
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AASH,0EAA0E;AAC1E,UAAU,WAAW;IACnB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,4EAA4E;AAC5E,UAAU,eAAe;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC3C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAeD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,0BAA0B;IAC1B,MAAM,EACF,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,oBAAoB,GACpB,eAAe,CAAC;IACpB,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAEnD;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC5D;AAED,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAczF;AAyLD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC3C;AAgQD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAqB3F;AA4ED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,aAAa,GAAG,IAAI,CAiB7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7E"}
|