@unfenced-ai/sdk 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.
Files changed (59) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +171 -0
  3. package/dist/client.d.ts +385 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +349 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/credentials.d.ts +14 -0
  8. package/dist/credentials.d.ts.map +1 -0
  9. package/dist/credentials.js +17 -0
  10. package/dist/credentials.js.map +1 -0
  11. package/dist/fetch.d.ts +16 -0
  12. package/dist/fetch.d.ts.map +1 -0
  13. package/dist/fetch.js +387 -0
  14. package/dist/fetch.js.map +1 -0
  15. package/dist/http.d.ts +76 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +250 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/index.d.ts +53 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +41 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/library.d.ts +17 -0
  24. package/dist/library.d.ts.map +1 -0
  25. package/dist/library.js +25 -0
  26. package/dist/library.js.map +1 -0
  27. package/dist/memory.d.ts +13 -0
  28. package/dist/memory.d.ts.map +1 -0
  29. package/dist/memory.js +45 -0
  30. package/dist/memory.js.map +1 -0
  31. package/dist/permissions.d.ts +29 -0
  32. package/dist/permissions.d.ts.map +1 -0
  33. package/dist/permissions.js +69 -0
  34. package/dist/permissions.js.map +1 -0
  35. package/dist/protocol.d.ts +296 -0
  36. package/dist/protocol.d.ts.map +1 -0
  37. package/dist/protocol.js +89 -0
  38. package/dist/protocol.js.map +1 -0
  39. package/dist/session.d.ts +97 -0
  40. package/dist/session.d.ts.map +1 -0
  41. package/dist/session.js +88 -0
  42. package/dist/session.js.map +1 -0
  43. package/dist/sessions.d.ts +131 -0
  44. package/dist/sessions.d.ts.map +1 -0
  45. package/dist/sessions.js +197 -0
  46. package/dist/sessions.js.map +1 -0
  47. package/dist/token-usage.d.ts +11 -0
  48. package/dist/token-usage.d.ts.map +1 -0
  49. package/dist/token-usage.js +34 -0
  50. package/dist/token-usage.js.map +1 -0
  51. package/dist/types.d.ts +1409 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/url-identity.d.ts +8 -0
  56. package/dist/url-identity.d.ts.map +1 -0
  57. package/dist/url-identity.js +18 -0
  58. package/dist/url-identity.js.map +1 -0
  59. package/package.json +44 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The live-session tier: open a page, drive it, read it, close it.
3
+ *
4
+ * These are the bodies of the `Unfenced` methods under its `// ---- live
5
+ * sessions` heading, moved out and re-shaped from methods to functions that
6
+ * take a `Transport`. `open` and `switchAccount` also take the `client`, because
7
+ * they hand it to the `Session` they build. `this.request` became `http.request`
8
+ * and nothing else changed — the request paths, shapes, and comments are as they
9
+ * were.
10
+ */
11
+ import { Transport } from "./http.js";
12
+ import { Session } from "./session.js";
13
+ import type { Unfenced } from "./client.js";
14
+ import type { ExtractedDoc } from "./protocol.js";
15
+ import type { Action, ActResult, ActExpectation, AtAction, DownloadInfo, Format, FormField, FormFillResult, OnAction, PageSnapshot, PendingApproval, ProviderSessionReason, ReadPage, SeenPage, SessionInfo } from "./types.js";
16
+ /**
17
+ * A session id, safe to splice into a path.
18
+ *
19
+ * Every call below builds `/session/<id>/...` by interpolation, and the id
20
+ * reaches the client from whatever asked for it — an MCP tool argument written
21
+ * by a model, a CLI flag, a caller's own storage. An id carrying `/`, `?`, `#`
22
+ * or `..` therefore chooses a DIFFERENT route on the worker while looking like
23
+ * a session that does not exist: `../../permissions` reads the allowlist,
24
+ * `x?account=other` appends a parameter the caller never passed. Nothing on
25
+ * this path validated it.
26
+ *
27
+ * Deliberately a shape check and not a format check. Ids are 8 hex characters
28
+ * today (`randomUUID().slice(0, 8)`), but pinning that here would make a
29
+ * perfectly reasonable change to the generator break every client; what has to
30
+ * hold is only that the value is ONE path segment. `encodeURIComponent` is
31
+ * belt to the braces — it would neutralise the traversal on its own, and the
32
+ * throw is what makes a bad id readable rather than a 404 from the worker.
33
+ *
34
+ * Every caller below is `async` so this arrives as a REJECTION. A promise-
35
+ * returning method that throws synchronously is caught by `await` and missed by
36
+ * `.catch()`, which is a difference no caller should have to know about.
37
+ */
38
+ export declare function sessionPath(id: string): string;
39
+ export declare function open(client: Unfenced, http: Transport, url: string, options?: {
40
+ headless?: boolean;
41
+ profile?: "ephemeral" | "agent";
42
+ proxy?: string;
43
+ account?: string;
44
+ intent?: "read" | "act";
45
+ settleMs?: number;
46
+ }): Promise<Session & {
47
+ providerSession?: ProviderSessionReason;
48
+ reusedExistingSession?: boolean;
49
+ navigated?: boolean;
50
+ note?: string;
51
+ }>;
52
+ export declare function switchAccount(client: Unfenced, http: Transport, sessionId: string, account: string): Promise<{
53
+ session: Session;
54
+ restoredSession: boolean;
55
+ requestedAccount?: string;
56
+ observedIdentity?: string;
57
+ }>;
58
+ export declare function pendingApprovals(http: Transport): Promise<{
59
+ interrupts: PendingApproval[];
60
+ clearAt: string;
61
+ }>;
62
+ export declare function whoami(http: Transport, sessionId: string): Promise<{
63
+ identity: string | null;
64
+ source: string | null;
65
+ }>;
66
+ export declare function liveSessions(http: Transport): Promise<SessionInfo[]>;
67
+ export declare function observe(http: Transport, id: string, opts?: {
68
+ match?: string;
69
+ maxControls?: number;
70
+ maxLinks?: number;
71
+ excerptChars?: number;
72
+ media?: boolean;
73
+ }): Promise<PageSnapshot>;
74
+ /**
75
+ * Read the current page and the server's last provider-session continuity
76
+ * decision. Unlike `observe`, this keeps the small status envelope so a caller
77
+ * reconnecting after a network interruption can distinguish a carried login
78
+ * from a session that needs re-authentication.
79
+ */
80
+ export declare function refresh(http: Transport, id: string, opts?: {
81
+ match?: string;
82
+ }): Promise<{
83
+ page: PageSnapshot;
84
+ providerSession?: ProviderSessionReason;
85
+ }>;
86
+ export declare function read(http: Transport, id: string, opts?: {
87
+ match?: string;
88
+ maxControls?: number;
89
+ maxLinks?: number;
90
+ excerptChars?: number;
91
+ media?: boolean;
92
+ }): Promise<ReadPage>;
93
+ export declare function act(http: Transport, id: string, action: Action | OnAction | AtAction, opts?: {
94
+ acceptDialog?: boolean;
95
+ dialogText?: string;
96
+ brief?: boolean;
97
+ see?: boolean;
98
+ expect?: ActExpectation;
99
+ }): Promise<ActResult>;
100
+ export declare function extract(http: Transport, id: string, format?: Format, maxWords?: number): Promise<{
101
+ doc: ExtractedDoc;
102
+ content: string;
103
+ url: string;
104
+ contentTruncated?: boolean;
105
+ /** Present when something was withheld: how many words the page held. */
106
+ totalWords?: number;
107
+ }>;
108
+ export declare function screenshot(http: Transport, id: string): Promise<string>;
109
+ export declare function see(http: Transport, id: string, opts?: {
110
+ links?: boolean;
111
+ maxMarks?: number;
112
+ }): Promise<SeenPage>;
113
+ export declare function fill(http: Transport, id: string, fields: readonly FormField[]): Promise<FormFillResult>;
114
+ export declare function park(http: Transport, id: string, minutes?: number, reason?: string): Promise<{
115
+ ok: boolean;
116
+ until?: string;
117
+ }>;
118
+ export declare function downloads(http: Transport, id: string): Promise<{
119
+ downloads: DownloadInfo[];
120
+ }>;
121
+ export declare function readDownload(http: Transport, id: string, filename: string): Promise<{
122
+ filename: string;
123
+ bytes: number;
124
+ content: string;
125
+ doc: ExtractedDoc;
126
+ }>;
127
+ export declare function closeSession(http: Transport, id: string): Promise<{
128
+ alreadyClosed?: boolean;
129
+ wasOpen?: boolean;
130
+ }>;
131
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The live-session tier: open a page, drive it, read it, close it.
3
+ *
4
+ * These are the bodies of the `Unfenced` methods under its `// ---- live
5
+ * sessions` heading, moved out and re-shaped from methods to functions that
6
+ * take a `Transport`. `open` and `switchAccount` also take the `client`, because
7
+ * they hand it to the `Session` they build. `this.request` became `http.request`
8
+ * and nothing else changed — the request paths, shapes, and comments are as they
9
+ * were.
10
+ */
11
+ import { UnfencedError } from "./http.js";
12
+ import { Session } from "./session.js";
13
+ /**
14
+ * A session id, safe to splice into a path.
15
+ *
16
+ * Every call below builds `/session/<id>/...` by interpolation, and the id
17
+ * reaches the client from whatever asked for it — an MCP tool argument written
18
+ * by a model, a CLI flag, a caller's own storage. An id carrying `/`, `?`, `#`
19
+ * or `..` therefore chooses a DIFFERENT route on the worker while looking like
20
+ * a session that does not exist: `../../permissions` reads the allowlist,
21
+ * `x?account=other` appends a parameter the caller never passed. Nothing on
22
+ * this path validated it.
23
+ *
24
+ * Deliberately a shape check and not a format check. Ids are 8 hex characters
25
+ * today (`randomUUID().slice(0, 8)`), but pinning that here would make a
26
+ * perfectly reasonable change to the generator break every client; what has to
27
+ * hold is only that the value is ONE path segment. `encodeURIComponent` is
28
+ * belt to the braces — it would neutralise the traversal on its own, and the
29
+ * throw is what makes a bad id readable rather than a 404 from the worker.
30
+ *
31
+ * Every caller below is `async` so this arrives as a REJECTION. A promise-
32
+ * returning method that throws synchronously is caught by `await` and missed by
33
+ * `.catch()`, which is a difference no caller should have to know about.
34
+ */
35
+ export function sessionPath(id) {
36
+ // A standalone dot segment is normalized by URL parsers even when encoded.
37
+ // Embedded dots remain valid, but these two values cannot identify a session.
38
+ if (id === "." || id === ".." || !/^[A-Za-z0-9._~-]{1,128}$/.test(id)) {
39
+ throw new UnfencedError(400, `not a session id: ${JSON.stringify(id.slice(0, 40))}`, {
40
+ code: "bad-session-id",
41
+ remedy: "a session id is the `id` open_page returned - one path segment of letters, digits, dot, dash, underscore or tilde. Open a page and use the id it gives you",
42
+ });
43
+ }
44
+ return encodeURIComponent(id);
45
+ }
46
+ export async function open(client, http, url, options = {}) {
47
+ const { id, page, providerSession, reusedExistingSession, navigated, note } = await http.request("POST", "/session", { url, ...options });
48
+ // Attached rather than returned separately, so every existing caller keeps
49
+ // working unchanged and a caller that wants to know WHY a page opened
50
+ // without the account's provider session can ask.
51
+ // Attached the same way `providerSession` is, and for the same reason: the
52
+ // server has always said when it handed back a page you already had, and
53
+ // this destructuring is where that sentence used to stop. An agent asking
54
+ // for a second page on a site got the first one with nothing saying so.
55
+ return Object.assign(new Session(client, id, page), {
56
+ providerSession,
57
+ ...(reusedExistingSession ? { reusedExistingSession, navigated } : {}),
58
+ // Outside the reuse branch: an ordinary open can carry one too, and it was
59
+ // being dropped for every caller who had not reused a session.
60
+ ...(note ? { note } : {}),
61
+ });
62
+ }
63
+ export async function switchAccount(client, http, sessionId, account) {
64
+ // `restoredSession` says whether this account had a STORED session to restore. When
65
+ // false the reopened page is logged out and the resulting identity is whatever a
66
+ // fresh OAuth resolves — NOT a guarantee it is `account`. `observedIdentity` is who
67
+ // the page is ACTUALLY signed in as, when the site is one the server can read —
68
+ // the truth to check against the requested name and across switches.
69
+ const { id, page, restoredSession, requestedAccount, observedIdentity } = await http.request("POST", `/session/${sessionPath(sessionId)}/switch`, { account });
70
+ return {
71
+ session: new Session(client, id, page),
72
+ restoredSession: Boolean(restoredSession),
73
+ requestedAccount,
74
+ observedIdentity,
75
+ };
76
+ }
77
+ export function pendingApprovals(http) {
78
+ return http.request("GET", "/interrupts");
79
+ }
80
+ export async function whoami(http, sessionId) {
81
+ return http.request("GET", `/session/${sessionPath(sessionId)}/whoami`);
82
+ }
83
+ export function liveSessions(http) {
84
+ return http.request("GET", "/session").then((r) => r.sessions);
85
+ }
86
+ export async function observe(http, id, opts) {
87
+ return http
88
+ .request("POST", `/session/${sessionPath(id)}/observe`, opts ?? {})
89
+ .then((r) => r.page);
90
+ }
91
+ /**
92
+ * Read the current page and the server's last provider-session continuity
93
+ * decision. Unlike `observe`, this keeps the small status envelope so a caller
94
+ * reconnecting after a network interruption can distinguish a carried login
95
+ * from a session that needs re-authentication.
96
+ */
97
+ export function refresh(http, id, opts) {
98
+ return http.request("GET", `/session/${sessionPath(id)}${opts?.match ? `?match=${encodeURIComponent(opts.match)}` : ""}`);
99
+ }
100
+ export async function read(http, id, opts) {
101
+ return http.request("POST", `/session/${sessionPath(id)}/observe`, opts ?? {});
102
+ }
103
+ export async function act(http, id, action, opts) {
104
+ // Merged into the body, because the wire is one object — the server reads
105
+ // `acceptDialog`/`dialogText` off it and hands them to the engine as call
106
+ // options rather than as part of the action.
107
+ return http.request("POST", `/session/${sessionPath(id)}/act`, {
108
+ ...onTheWire(action),
109
+ ...opts,
110
+ });
111
+ }
112
+ /**
113
+ * The one place an action's field names are translated for the wire.
114
+ *
115
+ * A multi-select carried its options as an ARRAY under the SINGULAR key —
116
+ * `{kind:"select", ref, value:["Olive","Caper"]}` — and the server's parser
117
+ * reads the PLURAL key for arrays: `const many = b["values"]; if
118
+ * (Array.isArray(many)) …`, falling through otherwise to `String(b["value"])`,
119
+ * which is `"Olive,Caper"`. That string matches no option on any page, so
120
+ * multi-select failed on every call and failed in a way that looked like the
121
+ * site's fault. Reproduced end to end against the two real functions.
122
+ *
123
+ * Done HERE rather than in the MCP fold because this function is what writes
124
+ * the body: every caller — the connector, a typed SDK user, a script — goes
125
+ * through it, and a fix in one caller leaves the others broken. The action
126
+ * types keep `value: string | string[]`, which is what the idea actually is;
127
+ * the wire's two spellings are a wire detail and stop here.
128
+ */
129
+ function onTheWire(action) {
130
+ const body = { ...action };
131
+ if (body["kind"] === "select" && Array.isArray(body["value"])) {
132
+ body["values"] = body["value"];
133
+ delete body["value"];
134
+ }
135
+ return body;
136
+ }
137
+ export async function extract(http, id, format = "markdown", maxWords) {
138
+ // `contentTruncated` is on the wire and was not in this type, so every typed
139
+ // caller lost it: the route sets it when `capExtractInput` cut the DOM at the
140
+ // 1 MB ceiling, with the comment "a cut page that claims to be whole is worse
141
+ // than a cut page", and a client that cannot see the field cannot pass it on.
142
+ //
143
+ // `maxWords` and `totalWords` are the SECOND ceiling, which had no parameter
144
+ // and no report: the route defaults to core's 25,000-word budget, so a long
145
+ // logged-in listing came back cut with `contentTruncated` set for a reason
146
+ // every description of this call attributed to the megabyte cut instead — and
147
+ // its only documented remedy, a smaller DOM, does nothing about it.
148
+ //
149
+ // Absent means the route's own default, which is what every existing caller
150
+ // already gets — adding the parameter changed nobody's result, only what a
151
+ // careful caller is able to ask for.
152
+ const budget = maxWords === undefined ? "" : `&maxWords=${encodeURIComponent(String(maxWords))}`;
153
+ return http.request("GET", `/session/${sessionPath(id)}/extract?format=${format}${budget}`);
154
+ }
155
+ export async function screenshot(http, id) {
156
+ return http
157
+ .request("GET", `/session/${sessionPath(id)}/screenshot`)
158
+ .then((r) => r.image);
159
+ }
160
+ export async function see(http, id, opts = {}) {
161
+ const query = new URLSearchParams({ marks: "1" });
162
+ if (opts.links)
163
+ query.set("links", "1");
164
+ if (opts.maxMarks !== undefined)
165
+ query.set("maxMarks", String(opts.maxMarks));
166
+ return http.request("GET", `/session/${sessionPath(id)}/screenshot?${query.toString()}`);
167
+ }
168
+ export async function fill(http, id, fields) {
169
+ return http.request("POST", `/session/${sessionPath(id)}/fill`, { fields });
170
+ }
171
+ export async function park(http, id, minutes, reason) {
172
+ return http.request("POST", `/session/${sessionPath(id)}/park`, {
173
+ ...(minutes !== undefined ? { minutes } : {}),
174
+ ...(reason !== undefined ? { reason } : {}),
175
+ });
176
+ }
177
+ export async function downloads(http, id) {
178
+ return http.request("GET", `/session/${sessionPath(id)}/downloads`);
179
+ }
180
+ export async function readDownload(http, id, filename) {
181
+ return http.request("GET", `/session/${sessionPath(id)}/downloads/${encodeURIComponent(filename)}`);
182
+ }
183
+ export async function closeSession(http, id) {
184
+ // The response type has to name every field that is read below, or the
185
+ // passthrough silently drops it again — which is the whole defect here.
186
+ return http
187
+ .request("DELETE", `/session/${sessionPath(id)}`)
188
+ .then((r) => ({
189
+ // Passed through rather than rebuilt. This mapped only `alreadyClosed`,
190
+ // so `wasOpen` — added so a mistyped id stops reading as success — was
191
+ // dropped here and the MCP tool reported `wasOpen: true` unconditionally.
192
+ // Its false branch was dead the day it shipped.
193
+ ...(r.alreadyClosed ? { alreadyClosed: true } : {}),
194
+ ...(typeof r.wasOpen === "boolean" ? { wasOpen: r.wasOpen } : {}),
195
+ }));
196
+ }
197
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** Text returned by a tool/API, not provider-billed model usage. */
2
+ export interface TokenUsage {
3
+ category: "fetch" | "session" | "other";
4
+ operation: string;
5
+ textChars: number;
6
+ imageCount: number;
7
+ sessionId?: string;
8
+ }
9
+ /** Count MCP text once; structuredContent duplicates text in our tool replies. */
10
+ export declare function toolTokenUsage(operation: string, input: unknown, result: unknown): TokenUsage;
11
+ //# sourceMappingURL=token-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-usage.d.ts","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,CA4B7F"}
@@ -0,0 +1,34 @@
1
+ /** Count MCP text once; structuredContent duplicates text in our tool replies. */
2
+ export function toolTokenUsage(operation, input, result) {
3
+ const args = input;
4
+ let sessionId = typeof args?.sessionId === "string" ? args.sessionId : undefined;
5
+ let textChars = 0;
6
+ let imageCount = 0;
7
+ const blocks = result?.content;
8
+ if (Array.isArray(blocks)) {
9
+ for (const block of blocks) {
10
+ if (block?.type === "text" && typeof block.text === "string") {
11
+ textChars += block.text.length;
12
+ if (!sessionId) {
13
+ try {
14
+ const payload = JSON.parse(block.text);
15
+ if (typeof payload?.sessionId === "string")
16
+ sessionId = payload.sessionId;
17
+ }
18
+ catch {
19
+ /* prose is also a valid text block */
20
+ }
21
+ }
22
+ }
23
+ else if (block?.type === "image")
24
+ imageCount++;
25
+ }
26
+ }
27
+ const category = sessionId || operation === "open_page" || operation === "switch_account"
28
+ ? "session"
29
+ : /^(fetch_page|fetch_batch|get_page_links)$/.test(operation)
30
+ ? "fetch"
31
+ : "other";
32
+ return { category, operation, textChars, imageCount, ...(sessionId ? { sessionId } : {}) };
33
+ }
34
+ //# sourceMappingURL=token-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-usage.js","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"AASA,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,KAAc,EAAE,MAAe;IAC/E,MAAM,IAAI,GAAG,KAAuC,CAAC;IACrD,IAAI,SAAS,GAAG,OAAO,IAAI,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,MAAM,GAAI,MAAuC,EAAE,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7D,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;gBAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;wBAClE,IAAI,OAAO,OAAO,EAAE,SAAS,KAAK,QAAQ;4BAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;oBAC5E,CAAC;oBAAC,MAAM,CAAC;wBACP,sCAAsC;oBACxC,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,IAAI,KAAK,EAAE,IAAI,KAAK,OAAO;gBAAE,UAAU,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GACZ,SAAS,IAAI,SAAS,KAAK,WAAW,IAAI,SAAS,KAAK,gBAAgB;QACtE,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,2CAA2C,CAAC,IAAI,CAAC,SAAS,CAAC;YAC3D,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,OAAO,CAAC;IAChB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7F,CAAC"}