@happyvertical/smrt-mcp-openai 0.51.38 → 0.52.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/dist/files.js ADDED
@@ -0,0 +1,288 @@
1
+ import { fileContent, fileRead, fileWriteResult, validateFileInput, } from './file-contracts.js';
2
+ import { json, record, text, toolName } from './validation.js';
3
+ export { validateFileInput } from './file-contracts.js';
4
+ class SupersededFileRead extends Error {
5
+ }
6
+ function capability(bridge, key) {
7
+ try {
8
+ const raw = record(bridge.snapshot.rawHostCapabilities.experimental);
9
+ record(raw[key]);
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ function emptyResult(result) {
17
+ json(result);
18
+ const value = record(result);
19
+ if (value.isError === true)
20
+ throw new Error('Host denied file operation');
21
+ }
22
+ /** One initial tool-input grant, one lifetime; create a new session after reconnect/input change. */
23
+ export class OpenAiFileSession {
24
+ #options;
25
+ #input;
26
+ #extension;
27
+ #lifetime = new AbortController();
28
+ #latest;
29
+ #writing = false;
30
+ #resumeRefresh;
31
+ #subscribed = false;
32
+ #stop;
33
+ #generation = 0;
34
+ #unsubscribeLifecycle;
35
+ constructor(options) {
36
+ this.#input = validateFileInput(options.input);
37
+ toolName(options.authorityTool);
38
+ toolName(options.fallbackTool);
39
+ if (!Number.isSafeInteger(options.maxBytes) ||
40
+ options.maxBytes < 1 ||
41
+ options.maxBytes > 16384)
42
+ throw new TypeError('Expected file byte bound from 1 to 16384');
43
+ if (!options.mimeTypes.length ||
44
+ options.mimeTypes.length > 16 ||
45
+ options.mimeTypes.some((mime) => !/^[\w.+-]+\/[\w.+-]+$/.test(mime)))
46
+ throw new TypeError('Expected explicit MIME allowlist');
47
+ this.#options = { ...options, mimeTypes: [...options.mimeTypes] };
48
+ if (options.bridge.snapshot.state !== 'ready')
49
+ throw new Error('Bridge is not ready');
50
+ if (capability(options.bridge, 'openai/resource')) {
51
+ this.#extension = options.bridge.registerExtension({
52
+ id: 'openai.files.resources',
53
+ capability: { path: ['experimental', 'openai/resource'] },
54
+ methods: [
55
+ 'resources/read',
56
+ 'resources/subscribe',
57
+ 'resources/unsubscribe',
58
+ 'openai/resources/write',
59
+ ],
60
+ notifications: ['notifications/resources/updated'],
61
+ });
62
+ }
63
+ this.#unsubscribeLifecycle = options.bridge.subscribe((snapshot) => {
64
+ if (snapshot.state === 'disposed' || snapshot.cancelled)
65
+ this.dispose();
66
+ if (snapshot.toolInput !== undefined) {
67
+ try {
68
+ const input = validateFileInput(snapshot.toolInput);
69
+ if (input.file.resourceUri !== this.#input.file.resourceUri ||
70
+ input.file.name !== this.#input.file.name)
71
+ this.dispose();
72
+ }
73
+ catch {
74
+ this.dispose();
75
+ }
76
+ }
77
+ });
78
+ // subscribe delivers the current snapshot before returning its cleanup.
79
+ if (this.signal.aborted)
80
+ this.#unsubscribeLifecycle();
81
+ }
82
+ get native() {
83
+ return Boolean(this.#extension);
84
+ }
85
+ get signal() {
86
+ return this.#lifetime.signal;
87
+ }
88
+ #active() {
89
+ if (this.signal.aborted || this.#options.bridge.signal.aborted)
90
+ throw new Error('File session disposed');
91
+ }
92
+ async #authorize(operation) {
93
+ this.#active();
94
+ const result = await this.#options.bridge.callTool(this.#options.authorityTool, { ...this.#input, operation }, this.signal);
95
+ this.#active();
96
+ if (result.isError) {
97
+ this.#latest = undefined;
98
+ throw new Error('File authority denied');
99
+ }
100
+ }
101
+ async fallback(operation) {
102
+ await this.#authorize(operation);
103
+ const result = await this.#options.bridge.callTool(this.#options.fallbackTool, { ...this.#input, operation }, this.signal);
104
+ this.#active();
105
+ if (result.isError)
106
+ throw new Error('File workflow denied');
107
+ return result;
108
+ }
109
+ async read(representation) {
110
+ if (representation !== undefined &&
111
+ representation !== 'text' &&
112
+ representation !== 'blob')
113
+ throw new TypeError('Invalid representation');
114
+ if (!this.#extension)
115
+ return this.fallback('read');
116
+ const generation = ++this.#generation;
117
+ this.#latest = undefined;
118
+ await this.#authorize('read');
119
+ const result = await this.#extension.request('resources/read', {
120
+ uri: this.#input.file.resourceUri,
121
+ ...(representation
122
+ ? { _meta: { 'openai/resource': { representation } } }
123
+ : {}),
124
+ }, this.signal);
125
+ const file = fileRead(result, this.#input.file.resourceUri, this.#options.maxBytes, this.#options.mimeTypes);
126
+ await this.#authorize('read');
127
+ if (generation !== this.#generation)
128
+ throw new SupersededFileRead('Stale file read');
129
+ this.#latest = file;
130
+ return structuredClone(file);
131
+ }
132
+ async write(content) {
133
+ this.#active();
134
+ if (!this.#extension)
135
+ return this.fallback('write');
136
+ if (this.#writing)
137
+ throw new Error('File write already pending');
138
+ const previous = this.#latest;
139
+ if (!previous?.writable || !previous.etag)
140
+ throw new Error('Fresh writable ETag required');
141
+ const bounded = fileContent(content, this.#options.maxBytes);
142
+ this.#writing = true;
143
+ this.#latest = undefined;
144
+ ++this.#generation;
145
+ try {
146
+ await this.#authorize('write');
147
+ const result = fileWriteResult(await this.#extension.request('openai/resources/write', {
148
+ uri: this.#input.file.resourceUri,
149
+ ifMatch: previous.etag,
150
+ ...bounded,
151
+ }, this.signal));
152
+ this.#active();
153
+ // All outcomes require a new authorized read; unknown failures are never retried.
154
+ return result;
155
+ }
156
+ finally {
157
+ this.#writing = false;
158
+ this.#latest = undefined;
159
+ ++this.#generation;
160
+ this.#resumeRefresh?.();
161
+ }
162
+ }
163
+ async subscribe(onRead, onError) {
164
+ this.#active();
165
+ if (!this.#extension) {
166
+ await this.fallback('subscribe');
167
+ return;
168
+ }
169
+ if (this.#subscribed)
170
+ throw new Error('Already subscribed');
171
+ this.#subscribed = true;
172
+ let refreshing = false;
173
+ let dirty = false;
174
+ const refresh = async () => {
175
+ if (refreshing || this.#writing) {
176
+ dirty = true;
177
+ return;
178
+ }
179
+ refreshing = true;
180
+ try {
181
+ do {
182
+ dirty = false;
183
+ if (this.#writing) {
184
+ dirty = true;
185
+ break;
186
+ }
187
+ try {
188
+ const result = await this.read();
189
+ if (!this.signal.aborted && this.#subscribed)
190
+ onRead(result);
191
+ }
192
+ catch (error) {
193
+ if (!(error instanceof SupersededFileRead))
194
+ throw error;
195
+ dirty = true;
196
+ }
197
+ } while (dirty && !this.signal.aborted && this.#subscribed);
198
+ }
199
+ catch (error) {
200
+ if (!this.signal.aborted) {
201
+ try {
202
+ onError(error);
203
+ }
204
+ finally {
205
+ this.dispose();
206
+ }
207
+ }
208
+ }
209
+ finally {
210
+ refreshing = false;
211
+ }
212
+ };
213
+ this.#resumeRefresh = () => {
214
+ if (dirty && !this.signal.aborted && this.#subscribed)
215
+ void refresh();
216
+ };
217
+ try {
218
+ await this.#authorize('subscribe');
219
+ this.#stop = this.#extension.subscribe((method, params) => {
220
+ if (method !== 'notifications/resources/updated' || this.signal.aborted)
221
+ return;
222
+ if (params.uri !== this.#input.file.resourceUri)
223
+ return;
224
+ this.#latest = undefined;
225
+ ++this.#generation;
226
+ void refresh();
227
+ });
228
+ emptyResult(await this.#extension.request('resources/subscribe', { uri: this.#input.file.resourceUri }, this.signal));
229
+ this.#active();
230
+ }
231
+ catch (error) {
232
+ this.#stop?.();
233
+ this.#stop = undefined;
234
+ this.#subscribed = false;
235
+ throw error;
236
+ }
237
+ }
238
+ dispose() {
239
+ if (this.signal.aborted)
240
+ return;
241
+ this.#latest = undefined;
242
+ ++this.#generation;
243
+ this.#stop?.();
244
+ this.#resumeRefresh = undefined;
245
+ this.#unsubscribeLifecycle?.();
246
+ if (this.#subscribed &&
247
+ this.#extension &&
248
+ !this.#options.bridge.signal.aborted) {
249
+ // Host cleanup is best effort. Local authority and callbacks end immediately.
250
+ void this.#extension
251
+ .request('resources/unsubscribe', { uri: this.#input.file.resourceUri })
252
+ .catch(() => { });
253
+ }
254
+ // Dispatch cleanup first, then release local registration without waiting for the host.
255
+ this.#extension?.dispose();
256
+ this.#subscribed = false;
257
+ this.#lifetime.abort();
258
+ }
259
+ }
260
+ /** User action calls this after the existing provider selects and authorizes an exact path. */
261
+ export async function openOpenAiFile(options) {
262
+ if (!(await options.confirm()))
263
+ return;
264
+ const result = await options.bridge.callTool(toolName(options.resolveTool), options.arguments);
265
+ if (result.isError)
266
+ throw new Error('File opening denied');
267
+ if (!capability(options.bridge, 'openai/files'))
268
+ return result;
269
+ const path = text(record(result.structuredContent).path, 2048);
270
+ if (!options.allowedPaths.includes(path) ||
271
+ !/^(\/[^/]|[A-Za-z]:\\)/.test(path) ||
272
+ [...path].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127) ||
273
+ path.split(/[\\/]/).some((part) => part === '.' || part === '..'))
274
+ throw new TypeError('File opening path denied');
275
+ const extension = options.bridge.registerExtension({
276
+ id: 'openai.files.open',
277
+ capability: { path: ['experimental', 'openai/files'] },
278
+ methods: ['openai/files/open'],
279
+ notifications: [],
280
+ });
281
+ try {
282
+ emptyResult(await extension.request('openai/files/open', { path }));
283
+ }
284
+ finally {
285
+ extension.dispose();
286
+ }
287
+ }
288
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /** Server-only adapter: persistence/authority remain in the existing M5 workflow. */
2
+ import { type McpWorkflowContinuation } from '@happyvertical/smrt-app-mcp';
3
+ import { type OpenAiFormReply } from './forms.js';
4
+ export type FormBinding = Pick<McpWorkflowContinuation, 'recordId' | 'revision' | 'inputKey'>;
5
+ export interface BoundFormReply {
6
+ binding: FormBinding;
7
+ schemaDigest: string;
8
+ reply: OpenAiFormReply;
9
+ }
10
+ /** Bind the reply to the exact server-provided continuation; this is not authority. */
11
+ export declare function bindOpenAiFormReply(expected: FormBinding, schema: unknown, value: unknown): BoundFormReply;
12
+ /**
13
+ * Suspend/replay the existing durable task. All pre-suspension work must be
14
+ * read-only or use the application's existing idempotency API. `applyInput`
15
+ * must retain immutable review/revision and domain approval checks in its own
16
+ * transaction. A form reply must never authorize an external action.
17
+ */
18
+ export declare function continueOpenAiForm<T>(options: {
19
+ binding: McpWorkflowContinuation;
20
+ schema: unknown;
21
+ /** Re-resolve current application authority after all asynchronous selection checks. */
22
+ authorizeApply: (binding: Readonly<FormBinding>) => Promise<boolean>;
23
+ /** Required when a form contains resource pickers; resolve opaque grants server-side. */
24
+ authorizeResource?: (uri: string, binding: Readonly<FormBinding>) => Promise<boolean>;
25
+ applyInput: (content: Record<string, string | number | boolean | string[]>, binding: Readonly<FormBinding>) => Promise<T>;
26
+ }): Promise<{
27
+ action: 'accept';
28
+ value: T;
29
+ } | {
30
+ action: 'cancel' | 'decline';
31
+ }>;
32
+ /** Structural public store port; a principal/tenant-bound McpTaskStore implements it. */
33
+ export interface FormContinuationStore {
34
+ getContinuation(taskId: string): Promise<{
35
+ binding: FormBinding;
36
+ inputRequests: Record<string, unknown>;
37
+ } | null>;
38
+ updateTask(taskId: string, inputResponses: Record<string, unknown>): Promise<void>;
39
+ }
40
+ /**
41
+ * Application-form submit path. The deployment supplies its verified actor's
42
+ * tenant-bound existing store and a fresh policy callback. CAS in that store
43
+ * accepts at most one reply; submission is not proof of completion or approval.
44
+ * Worker-side validation remains mandatory even if another caller uses tasks/update.
45
+ */
46
+ export declare function submitOpenAiForm(options: {
47
+ store: FormContinuationStore;
48
+ taskId: string;
49
+ binding: FormBinding;
50
+ reply: unknown;
51
+ authorize: () => Promise<boolean>;
52
+ }): Promise<void>;
53
+ //# sourceMappingURL=forms-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms-server.d.ts","sourceRoot":"","sources":["../src/forms-server.ts"],"names":[],"mappings":"AACA,qFAAqF;AACrF,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEL,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,WAAW,GAAG,IAAI,CAC5B,uBAAuB,EACvB,UAAU,GAAG,UAAU,GAAG,UAAU,CACrC,CAAC;AACF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;CACxB;AAkCD,uFAAuF;AACvF,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,OAAO,GACb,cAAc,CAMhB;AACD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE;IACnD,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,wFAAwF;IACxF,cAAc,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,KAC3B,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,UAAU,EAAE,CACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,EAC7D,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,KAC3B,OAAO,CAAC,CAAC,CAAC,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAC,CA2C7E;AACD,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QACvC,OAAO,EAAE,WAAW,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACxC,GAAG,IAAI,CAAC,CAAC;IACV,UAAU,CACR,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AACD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACnC,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
@@ -0,0 +1,115 @@
1
+ import { createHash } from 'node:crypto';
2
+ /** Server-only adapter: persistence/authority remain in the existing M5 workflow. */
3
+ import { continueMcpWorkflow, } from '@happyvertical/smrt-app-mcp';
4
+ import { getActiveJobExecutionContext, isRunnerExecutionContext, } from '@happyvertical/smrt-jobs';
5
+ import { formResourceSelections, validateOpenAiForm, validateOpenAiFormReply, } from './forms.js';
6
+ import { json, keys, record, text } from './validation.js';
7
+ function binding(value) {
8
+ json(value);
9
+ const b = record(value);
10
+ keys(b, ['recordId', 'revision', 'inputKey']);
11
+ return {
12
+ recordId: text(b.recordId, 256),
13
+ revision: text(b.revision, 256),
14
+ inputKey: text(b.inputKey, 256),
15
+ };
16
+ }
17
+ function canonical(value) {
18
+ if (Array.isArray(value))
19
+ return value.map(canonical);
20
+ if (value && typeof value === 'object')
21
+ return Object.fromEntries(Object.entries(value)
22
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
23
+ .map(([key, item]) => [key, canonical(item)]));
24
+ return value;
25
+ }
26
+ function schemaDigest(schema) {
27
+ return createHash('sha256')
28
+ .update(JSON.stringify(canonical(validateOpenAiForm(schema))))
29
+ .digest('hex');
30
+ }
31
+ function equal(a, b) {
32
+ if (a.recordId !== b.recordId ||
33
+ a.revision !== b.revision ||
34
+ a.inputKey !== b.inputKey)
35
+ throw new Error('Form continuation binding mismatch');
36
+ }
37
+ /** Bind the reply to the exact server-provided continuation; this is not authority. */
38
+ export function bindOpenAiFormReply(expected, schema, value) {
39
+ return {
40
+ binding: binding(expected),
41
+ schemaDigest: schemaDigest(schema),
42
+ reply: validateOpenAiFormReply(schema, value),
43
+ };
44
+ }
45
+ /**
46
+ * Suspend/replay the existing durable task. All pre-suspension work must be
47
+ * read-only or use the application's existing idempotency API. `applyInput`
48
+ * must retain immutable review/revision and domain approval checks in its own
49
+ * transaction. A form reply must never authorize an external action.
50
+ */
51
+ export async function continueOpenAiForm(options) {
52
+ const context = getActiveJobExecutionContext();
53
+ if (!context || !isRunnerExecutionContext(context) || !context.task)
54
+ throw new Error('Durable form requires an active MCP task');
55
+ const task = context.task;
56
+ const form = validateOpenAiForm(options.schema);
57
+ const expected = Object.freeze(binding({
58
+ recordId: options.binding.recordId,
59
+ revision: options.binding.revision,
60
+ inputKey: options.binding.inputKey,
61
+ }));
62
+ if (Object.values(form.properties).some((f) => 'x-openai-input' in f) &&
63
+ !options.authorizeResource)
64
+ throw new TypeError('Resource pickers require live grant authorization');
65
+ return continueMcpWorkflow({
66
+ binding: options.binding,
67
+ inputSchema: form,
68
+ applyReviewed: async (raw, current) => {
69
+ json(raw);
70
+ const envelope = record(raw);
71
+ keys(envelope, ['binding', 'schemaDigest', 'reply']);
72
+ if (envelope.schemaDigest !== schemaDigest(form))
73
+ throw new Error('Form schema revision mismatch');
74
+ equal(expected, binding(envelope.binding));
75
+ equal(expected, current);
76
+ const reply = validateOpenAiFormReply(form, envelope.reply);
77
+ for (const uri of formResourceSelections(form, reply))
78
+ if (!(await options.authorizeResource?.(uri, expected)))
79
+ throw new Error('Resource access denied');
80
+ if (!(await options.authorizeApply(expected)))
81
+ throw new Error('Form access denied');
82
+ await task.assertAuthorized();
83
+ if (reply.action !== 'accept')
84
+ return reply;
85
+ return {
86
+ action: 'accept',
87
+ value: await options.applyInput(reply.content, expected),
88
+ };
89
+ },
90
+ });
91
+ }
92
+ /**
93
+ * Application-form submit path. The deployment supplies its verified actor's
94
+ * tenant-bound existing store and a fresh policy callback. CAS in that store
95
+ * accepts at most one reply; submission is not proof of completion or approval.
96
+ * Worker-side validation remains mandatory even if another caller uses tasks/update.
97
+ */
98
+ export async function submitOpenAiForm(options) {
99
+ text(options.taskId, 256);
100
+ const expected = binding(options.binding);
101
+ if (!(await options.authorize()))
102
+ throw new Error('Form access denied');
103
+ const current = await options.store.getContinuation(options.taskId);
104
+ if (!current)
105
+ throw new Error('Form is not awaiting input');
106
+ equal(expected, current.binding);
107
+ const request = record(current.inputRequests[expected.inputKey]);
108
+ const answer = bindOpenAiFormReply(expected, request.schema, options.reply);
109
+ if (!(await options.authorize()))
110
+ throw new Error('Form access denied');
111
+ await options.store.updateTask(options.taskId, {
112
+ [expected.inputKey]: answer,
113
+ });
114
+ }
115
+ //# sourceMappingURL=forms-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms-server.js","sourceRoot":"","sources":["../src/forms-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type OpenAiFormReply } from './forms.js';
2
+ /**
3
+ * Functional application-form fallback. Text is rendered inertly; no remote
4
+ * thumbnails are loaded and no credentials/native file paths are requested.
5
+ * Submit must call the authenticated owning application route, which persists
6
+ * the bound reply through submitOpenAiForm. This renderer confers no authority.
7
+ */
8
+ export declare function renderOpenAiForm(container: HTMLElement, options: {
9
+ schema: unknown;
10
+ submit: (reply: OpenAiFormReply) => Promise<void>;
11
+ }): {
12
+ dispose: () => void;
13
+ };
14
+ //# sourceMappingURL=forms-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms-view.d.ts","sourceRoot":"","sources":["../src/forms-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AACpB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE;IACP,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,GACA;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA6KzB"}