@agentsean/actions 1.0.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 (50) hide show
  1. package/LICENSE +661 -0
  2. package/dist/executor.d.ts +46 -0
  3. package/dist/executor.d.ts.map +1 -0
  4. package/dist/executor.js +150 -0
  5. package/dist/executor.js.map +1 -0
  6. package/dist/hmac.d.ts +4 -0
  7. package/dist/hmac.d.ts.map +1 -0
  8. package/dist/hmac.js +23 -0
  9. package/dist/hmac.js.map +1 -0
  10. package/dist/html.d.ts +15 -0
  11. package/dist/html.d.ts.map +1 -0
  12. package/dist/html.js +52 -0
  13. package/dist/html.js.map +1 -0
  14. package/dist/index.d.ts +12 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +12 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/kinds.d.ts +12 -0
  19. package/dist/kinds.d.ts.map +1 -0
  20. package/dist/kinds.js +135 -0
  21. package/dist/kinds.js.map +1 -0
  22. package/dist/payloads.d.ts +13 -0
  23. package/dist/payloads.d.ts.map +1 -0
  24. package/dist/payloads.js +368 -0
  25. package/dist/payloads.js.map +1 -0
  26. package/dist/persist.d.ts +65 -0
  27. package/dist/persist.d.ts.map +1 -0
  28. package/dist/persist.js +362 -0
  29. package/dist/persist.js.map +1 -0
  30. package/dist/planner.d.ts +20 -0
  31. package/dist/planner.d.ts.map +1 -0
  32. package/dist/planner.js +115 -0
  33. package/dist/planner.js.map +1 -0
  34. package/dist/scan.d.ts +19 -0
  35. package/dist/scan.d.ts.map +1 -0
  36. package/dist/scan.js +210 -0
  37. package/dist/scan.js.map +1 -0
  38. package/dist/types.d.ts +227 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +29 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/urls.d.ts +6 -0
  43. package/dist/urls.d.ts.map +1 -0
  44. package/dist/urls.js +74 -0
  45. package/dist/urls.js.map +1 -0
  46. package/dist/validator.d.ts +9 -0
  47. package/dist/validator.d.ts.map +1 -0
  48. package/dist/validator.js +398 -0
  49. package/dist/validator.js.map +1 -0
  50. package/package.json +51 -0
@@ -0,0 +1,362 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { and, eq, gte } from "drizzle-orm";
3
+ import { actions, adapterConnections, changes, changeSnapshots, costLedger, entitySightings, pages, sites, twoKeyApprovals, urlAllowlist, } from "@agentsean/db";
4
+ import { BLAST } from "./types.js";
5
+ export function envelopeOf(action) {
6
+ return {
7
+ payload: action.payload,
8
+ rationale: action.rationale,
9
+ findingIds: action.findingIds,
10
+ estimatedImpact: action.estimatedImpact,
11
+ kind: action.kind,
12
+ target: action.target,
13
+ };
14
+ }
15
+ export function actionFromRow(row) {
16
+ if (!row.payload)
17
+ return null;
18
+ let env;
19
+ try {
20
+ env = JSON.parse(row.payload);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ const tier = Number(row.tier.replace(/^T/, ""));
26
+ if (tier !== 0 && tier !== 1 && tier !== 2 && tier !== 3 && tier !== 4)
27
+ return null;
28
+ return {
29
+ id: row.id,
30
+ siteId: row.siteId,
31
+ kind: env.kind,
32
+ tier,
33
+ target: env.target,
34
+ payload: env.payload,
35
+ rationale: env.rationale,
36
+ findingIds: env.findingIds,
37
+ estimatedImpact: env.estimatedImpact,
38
+ };
39
+ }
40
+ export function saveAction(db, action, state, extra) {
41
+ const now = new Date().toISOString();
42
+ const existing = db.select().from(actions).where(eq(actions.id, action.id)).get();
43
+ const values = {
44
+ siteId: action.siteId,
45
+ pageId: action.target.pageId,
46
+ findingId: action.findingIds[0] ?? null,
47
+ actionType: action.kind,
48
+ targetRef: action.target.url,
49
+ payload: JSON.stringify(envelopeOf(action)),
50
+ risk: `T${action.tier}`,
51
+ tier: `T${action.tier}`,
52
+ state,
53
+ approvedBy: extra?.approvedBy ?? existing?.approvedBy ?? null,
54
+ approvedAt: extra?.approvedBy ? now : (existing?.approvedAt ?? null),
55
+ appliedAt: state === "applied" ? now : (existing?.appliedAt ?? null),
56
+ error: extra?.error ?? null,
57
+ };
58
+ if (existing) {
59
+ db.update(actions).set(values).where(eq(actions.id, action.id)).run();
60
+ return;
61
+ }
62
+ db.insert(actions)
63
+ .values({
64
+ id: action.id,
65
+ createdAt: now,
66
+ ...values,
67
+ })
68
+ .run();
69
+ }
70
+ export function recordChange(db, action, applied) {
71
+ const now = new Date().toISOString();
72
+ const changeId = applied.id || randomUUID();
73
+ db.insert(changes)
74
+ .values({
75
+ id: changeId,
76
+ actionId: action.id,
77
+ siteId: action.siteId,
78
+ appliedAt: now,
79
+ actor: "sean",
80
+ summary: applied.summary,
81
+ revertible: 1,
82
+ revertedAt: null,
83
+ })
84
+ .run();
85
+ db.insert(changeSnapshots)
86
+ .values({
87
+ id: randomUUID(),
88
+ changeId,
89
+ kind: "before",
90
+ targetRef: applied.targetRef,
91
+ body: applied.before,
92
+ contentType: "text/plain",
93
+ capturedAt: now,
94
+ })
95
+ .run();
96
+ db.insert(changeSnapshots)
97
+ .values({
98
+ id: randomUUID(),
99
+ changeId,
100
+ kind: "after",
101
+ targetRef: applied.targetRef,
102
+ body: applied.after,
103
+ contentType: "text/plain",
104
+ capturedAt: now,
105
+ })
106
+ .run();
107
+ const meta = {
108
+ branch: applied.branch ?? null,
109
+ commitSha: applied.commitSha ?? null,
110
+ prUrl: applied.prUrl ?? null,
111
+ };
112
+ db.insert(changeSnapshots)
113
+ .values({
114
+ id: randomUUID(),
115
+ changeId,
116
+ kind: "meta",
117
+ targetRef: applied.targetRef,
118
+ body: JSON.stringify(meta),
119
+ contentType: "application/json",
120
+ capturedAt: now,
121
+ })
122
+ .run();
123
+ return { changeId };
124
+ }
125
+ export function loadChange(db, changeId) {
126
+ const row = db.select().from(changes).where(eq(changes.id, changeId)).get();
127
+ if (!row)
128
+ return null;
129
+ const snaps = db
130
+ .select()
131
+ .from(changeSnapshots)
132
+ .where(eq(changeSnapshots.changeId, changeId))
133
+ .all();
134
+ const before = snaps.find((s) => s.kind === "before");
135
+ const after = snaps.find((s) => s.kind === "after");
136
+ const metaRow = snaps.find((s) => s.kind === "meta");
137
+ let meta = {};
138
+ if (metaRow) {
139
+ try {
140
+ meta = JSON.parse(metaRow.body);
141
+ }
142
+ catch {
143
+ meta = {};
144
+ }
145
+ }
146
+ const result = {
147
+ id: row.id,
148
+ actionId: row.actionId,
149
+ siteId: row.siteId,
150
+ targetRef: before?.targetRef ?? after?.targetRef ?? "",
151
+ before: before?.body ?? "",
152
+ after: after?.body ?? "",
153
+ summary: row.summary,
154
+ };
155
+ if (meta.branch)
156
+ result.branch = meta.branch;
157
+ if (meta.commitSha)
158
+ result.commitSha = meta.commitSha;
159
+ if (meta.prUrl)
160
+ result.prUrl = meta.prUrl;
161
+ return result;
162
+ }
163
+ export function markReverted(db, changeId) {
164
+ db.update(changes)
165
+ .set({ revertedAt: new Date().toISOString() })
166
+ .where(eq(changes.id, changeId))
167
+ .run();
168
+ }
169
+ export function loadSitePolicy(db, siteId) {
170
+ const row = db.select().from(sites).where(eq(sites.id, siteId)).get();
171
+ if (!row)
172
+ return null;
173
+ let globs = [];
174
+ try {
175
+ const parsed = JSON.parse(row.neverTouchGlobs);
176
+ if (Array.isArray(parsed) && parsed.every((g) => typeof g === "string"))
177
+ globs = parsed;
178
+ }
179
+ catch {
180
+ globs = [];
181
+ }
182
+ return {
183
+ id: row.id,
184
+ origin: row.origin,
185
+ autonomyMode: row.autonomyMode,
186
+ observeUntil: row.observeUntil,
187
+ ymylCategory: row.ymylCategory,
188
+ killswitch: row.killswitch,
189
+ neverTouchGlobs: globs,
190
+ createdAt: row.createdAt,
191
+ };
192
+ }
193
+ export function loadPages(db, siteId) {
194
+ return db
195
+ .select()
196
+ .from(pages)
197
+ .where(eq(pages.siteId, siteId))
198
+ .all()
199
+ .map((p) => ({
200
+ id: p.id,
201
+ url: p.url,
202
+ title: p.title,
203
+ metaDescription: p.metaDescription,
204
+ h1: p.h1,
205
+ }));
206
+ }
207
+ export function loadAllowlist(db, siteId) {
208
+ return db
209
+ .select()
210
+ .from(urlAllowlist)
211
+ .where(eq(urlAllowlist.siteId, siteId))
212
+ .all()
213
+ .map((r) => r.url);
214
+ }
215
+ export function loadEntities(db, siteId) {
216
+ return db
217
+ .select()
218
+ .from(entitySightings)
219
+ .where(eq(entitySightings.siteId, siteId))
220
+ .all()
221
+ .map((r) => ({
222
+ entity: r.entity,
223
+ source: r.source,
224
+ }));
225
+ }
226
+ export function loadTwoKey(db, actionId) {
227
+ return db
228
+ .select()
229
+ .from(twoKeyApprovals)
230
+ .where(eq(twoKeyApprovals.actionId, actionId))
231
+ .all()
232
+ .map((r) => ({ actor: r.actor, hmac: r.hmac }));
233
+ }
234
+ export function addTwoKey(db, actionId, actor, hmac) {
235
+ db.insert(twoKeyApprovals)
236
+ .values({
237
+ id: randomUUID(),
238
+ actionId,
239
+ actor,
240
+ hmac,
241
+ createdAt: new Date().toISOString(),
242
+ })
243
+ .run();
244
+ }
245
+ export function recordEntity(db, siteId, entity, entityKind, source) {
246
+ const existing = db
247
+ .select()
248
+ .from(entitySightings)
249
+ .where(eq(entitySightings.siteId, siteId))
250
+ .all()
251
+ .find((r) => r.entity === entity);
252
+ if (existing)
253
+ return;
254
+ db.insert(entitySightings)
255
+ .values({
256
+ id: randomUUID(),
257
+ siteId,
258
+ entity,
259
+ entityKind,
260
+ source,
261
+ firstSeenAt: new Date().toISOString(),
262
+ })
263
+ .run();
264
+ }
265
+ export function upsertAdapterConnection(db, siteId, kind, config) {
266
+ const now = new Date().toISOString();
267
+ const existing = db
268
+ .select()
269
+ .from(adapterConnections)
270
+ .where(and(eq(adapterConnections.siteId, siteId), eq(adapterConnections.kind, kind)))
271
+ .get();
272
+ if (existing) {
273
+ db.update(adapterConnections)
274
+ .set({ config: JSON.stringify(config), updatedAt: now })
275
+ .where(eq(adapterConnections.id, existing.id))
276
+ .run();
277
+ return;
278
+ }
279
+ db.insert(adapterConnections)
280
+ .values({
281
+ id: randomUUID(),
282
+ siteId,
283
+ kind,
284
+ config: JSON.stringify(config),
285
+ createdAt: now,
286
+ updatedAt: now,
287
+ })
288
+ .run();
289
+ }
290
+ export function loadAdapterConnection(db, siteId, kind) {
291
+ const row = db
292
+ .select()
293
+ .from(adapterConnections)
294
+ .where(and(eq(adapterConnections.siteId, siteId), eq(adapterConnections.kind, kind)))
295
+ .get();
296
+ if (!row)
297
+ return null;
298
+ try {
299
+ const parsed = JSON.parse(row.config);
300
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
301
+ return parsed;
302
+ }
303
+ }
304
+ catch {
305
+ return null;
306
+ }
307
+ return null;
308
+ }
309
+ export function listAdapterConnections(db, siteId) {
310
+ return db
311
+ .select()
312
+ .from(adapterConnections)
313
+ .where(eq(adapterConnections.siteId, siteId))
314
+ .all()
315
+ .map((row) => {
316
+ let config = {};
317
+ try {
318
+ const parsed = JSON.parse(row.config);
319
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
320
+ config = parsed;
321
+ }
322
+ }
323
+ catch {
324
+ config = {};
325
+ }
326
+ return { kind: row.kind, config };
327
+ });
328
+ }
329
+ export function upsertGitConnection(db, siteId, config) {
330
+ upsertAdapterConnection(db, siteId, "git", config);
331
+ }
332
+ export function loadGitConnection(db, siteId) {
333
+ return loadAdapterConnection(db, siteId, "git");
334
+ }
335
+ export function countsForLedger(db, siteId, now) {
336
+ const hour = new Date(now.getTime() - 60 * 60 * 1000).toISOString();
337
+ const day = now.toISOString().slice(0, 10) + "T00:00:00.000Z";
338
+ const applied = db
339
+ .select()
340
+ .from(actions)
341
+ .where(and(eq(actions.siteId, siteId), eq(actions.state, "applied")))
342
+ .all();
343
+ const appliedThisHour = applied.filter((a) => (a.appliedAt ?? "") >= hour).length;
344
+ const appliedThisDay = applied.filter((a) => (a.appliedAt ?? "") >= day).length;
345
+ const newPagesToday = applied.filter((a) => a.actionType === "create_page" && (a.appliedAt ?? "") >= day).length;
346
+ const contentRefreshToday = applied.filter((a) => a.actionType === "refresh_content" && (a.appliedAt ?? "") >= day).length;
347
+ const costs = db
348
+ .select()
349
+ .from(costLedger)
350
+ .where(and(eq(costLedger.siteId, siteId), gte(costLedger.ts, day)))
351
+ .all();
352
+ const spentUsdToday = costs.reduce((s, r) => s + (r.costUsd ?? 0), 0);
353
+ return {
354
+ appliedThisHour,
355
+ appliedThisDay,
356
+ newPagesToday,
357
+ contentRefreshToday,
358
+ spentUsdToday,
359
+ };
360
+ }
361
+ export { BLAST };
362
+ //# sourceMappingURL=persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.js","sourceRoot":"","sources":["../src/persist.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type Action, type PageRow } from "./types.js";
2
+ export type FindingRow = {
3
+ id: string;
4
+ siteId: string;
5
+ pageId: string | null;
6
+ ruleId: string;
7
+ status: string;
8
+ };
9
+ /**
10
+ * Deterministic title. Never reads JSON-LD headline or Readability metadata —
11
+ * those are attacker-controlled. Prefer a clean H1, else slug + site name.
12
+ */
13
+ export declare function proposeTitle(page: PageRow, origin: string): string;
14
+ export declare function planTitleActions(opts: {
15
+ siteId: string;
16
+ origin: string;
17
+ pages: PageRow[];
18
+ findings: FindingRow[];
19
+ }): Action[];
20
+ //# sourceMappingURL=planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AASlE,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAwCF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBlE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB,GAAG,MAAM,EAAE,CA4BX"}
@@ -0,0 +1,115 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { KIND_TIER } from "./kinds.js";
3
+ import { bannedHits, invisibleHits } from "./scan.js";
4
+ import { TITLE_MAX } from "./types.js";
5
+ const TITLE_RULES = new Set([
6
+ "ONP.TITLE_MISSING",
7
+ "ONP.TITLE_TOO_SHORT",
8
+ "ONP.TITLE_TOO_LONG",
9
+ "ONP.TITLE_PIXEL_OVER",
10
+ ]);
11
+ function hostnameToName(origin) {
12
+ try {
13
+ const host = new URL(origin).hostname.replace(/^www\./, "");
14
+ const head = host.split(".")[0] ?? host;
15
+ return head.charAt(0).toUpperCase() + head.slice(1);
16
+ }
17
+ catch {
18
+ return "Site";
19
+ }
20
+ }
21
+ function slugTitle(url) {
22
+ try {
23
+ const u = new URL(url);
24
+ const parts = u.pathname.split("/").filter(Boolean);
25
+ const last = parts[parts.length - 1] ?? "";
26
+ const cleaned = last
27
+ .replace(/\.[a-z0-9]+$/i, "")
28
+ .replace(/[-_]+/g, " ")
29
+ .trim();
30
+ if (!cleaned)
31
+ return "";
32
+ return cleaned
33
+ .split(" ")
34
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
35
+ .join(" ");
36
+ }
37
+ catch {
38
+ return "";
39
+ }
40
+ }
41
+ function usable(text) {
42
+ const t = text.replace(/\s+/g, " ").trim();
43
+ if (t.length < 8 || t.length > TITLE_MAX)
44
+ return false;
45
+ if (bannedHits(t).length)
46
+ return false;
47
+ if (invisibleHits(t).count)
48
+ return false;
49
+ if (/https?:\/\//i.test(t) || /</.test(t))
50
+ return false;
51
+ return true;
52
+ }
53
+ /**
54
+ * Deterministic title. Never reads JSON-LD headline or Readability metadata —
55
+ * those are attacker-controlled. Prefer a clean H1, else slug + site name.
56
+ */
57
+ export function proposeTitle(page, origin) {
58
+ const site = hostnameToName(origin);
59
+ const h1 = page.h1?.replace(/\s+/g, " ").trim() ?? "";
60
+ if (usable(h1) && h1.length >= 20 && h1.length <= 60) {
61
+ return h1.length >= 30 ? h1 : `${h1} | ${site}`.slice(0, TITLE_MAX);
62
+ }
63
+ const slug = slugTitle(page.url);
64
+ let title = slug ? `${slug} | ${site}` : `${site} official site`;
65
+ if (h1 && usable(h1))
66
+ title = `${h1} | ${site}`;
67
+ if (title.length < 30)
68
+ title = `${title} — ${site} official site`;
69
+ if (title.length > 60) {
70
+ title = title
71
+ .slice(0, 57)
72
+ .replace(/\s+\S*$/, "")
73
+ .trimEnd();
74
+ }
75
+ if (title.length < 30)
76
+ title = `${site} official site home page`;
77
+ if (!usable(title))
78
+ title = `${site} official site home page`;
79
+ return title.slice(0, TITLE_MAX);
80
+ }
81
+ export function planTitleActions(opts) {
82
+ const byId = new Map(opts.pages.map((p) => [p.id, p]));
83
+ const out = [];
84
+ for (const f of opts.findings) {
85
+ if (f.status !== "open")
86
+ continue;
87
+ if (!TITLE_RULES.has(f.ruleId))
88
+ continue;
89
+ if (!f.pageId)
90
+ continue;
91
+ const page = byId.get(f.pageId);
92
+ if (!page)
93
+ continue;
94
+ const title = proposeTitle(page, opts.origin);
95
+ if (page.title && page.title === title)
96
+ continue;
97
+ out.push({
98
+ id: randomUUID(),
99
+ siteId: opts.siteId,
100
+ kind: "rewrite_title",
101
+ tier: KIND_TIER.rewrite_title,
102
+ target: { pageId: page.id, url: page.url },
103
+ payload: { title },
104
+ rationale: [
105
+ `Finding ${f.ruleId} on ${page.url}.`,
106
+ `Proposed title from the page H1 or URL slug, never from JSON-LD or og: tags.`,
107
+ `Default impact metric is clicks.`,
108
+ ],
109
+ findingIds: [f.id],
110
+ estimatedImpact: { metric: "clicks", estimate: 0, confidence: 0.2 },
111
+ });
112
+ }
113
+ return out;
114
+ }
115
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"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"}
package/dist/scan.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Output-side scanners. Applied to Action text, never to crawled input.
3
+ * Classifiers score ~60% on benign SEO trigger words — that is why we scan
4
+ * what we would write, not what we read.
5
+ */
6
+ export declare const BANNED_PATTERNS: {
7
+ id: string;
8
+ re: RegExp;
9
+ }[];
10
+ export declare function foldHomoglyphs(text: string): string;
11
+ export declare function invisibleHits(text: string): {
12
+ count: number;
13
+ kinds: string[];
14
+ };
15
+ export declare function stripInvisible(text: string): string;
16
+ export declare function bannedHits(text: string): string[];
17
+ export declare function encodedPayloadHits(text: string): string[];
18
+ export declare function collectActionText(value: unknown): string;
19
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA6CH,eAAO,MAAM,eAAe,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAyBvD,CAAC;AAeF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAW9E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQjD;AAyCD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAwCzD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUxD"}