robotrock 0.1.0 → 0.3.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/README.md CHANGED
@@ -10,77 +10,234 @@ npm install robotrock
10
10
  bun add robotrock
11
11
  ```
12
12
 
13
+ ## Agent skill (Cursor, Claude Code, etc.)
14
+
15
+ Install the RobotRock agent skill from [skills.sh](https://skills.sh/quintenb/robotrock-skills/robotrock) to teach coding agents how to integrate the SDK:
16
+
17
+ ```bash
18
+ npx skills add quintenb/robotrock-skills --skill robotrock
19
+ ```
20
+
13
21
  For Trigger.dev, also install `@trigger.dev/sdk`.
14
22
 
15
23
  ## Quick Start
16
24
 
25
+ Create a shared client in its own module (for example `lib/robotrock.ts`), then import it wherever you create tasks.
26
+
17
27
  ```typescript
18
- import { RobotRock, askHuman } from "robotrock";
28
+ // lib/robotrock.ts
29
+ import { createClient } from "robotrock";
30
+
31
+ export const robotrock = createClient({
32
+ app: "budgeting-service",
33
+ webhook: {
34
+ url: "https://your-app.com/api/robotrock/webhook",
35
+ headers: {
36
+ // place your headers here
37
+ },
38
+ },
39
+ });
40
+ ```
19
41
 
20
- // Or create a client explicitly
21
- const client = new RobotRock({ apiKey: process.env.ROBOTROCK_API_KEY! });
42
+ ```typescript
43
+ import { robotrock } from "./lib/robotrock";
22
44
 
23
- const response = await client.createTask({
45
+ const response = await robotrock.sendToHuman({
24
46
  type: "budget-approval",
25
47
  name: "Q1 Budget Approval",
48
+ description: "Please review and approve the Q1 budget",
26
49
  actions: [
27
50
  { id: "approve", title: "Approve" },
28
51
  { id: "reject", title: "Reject" },
29
52
  ],
53
+ idempotencyKey: "budget-q1-approval-v1",
30
54
  });
55
+
56
+ console.log("Task created:", response.task.taskId);
31
57
  ```
32
58
 
33
- ## Block until a human responds
59
+ Set `ROBOTROCK_API_KEY` in your environment (create a named key in the RobotRock dashboard under Settings → API Keys). The client reads it when you omit `apiKey` on `createClient`.
60
+
61
+ ## Inbox routing
62
+
63
+ Set `app` on `createClient` to control dashboard inbox grouping for every task from that client.
64
+
65
+ When `app` is omitted on the client, the API uses your API key **name** as the inbox bucket.
66
+
67
+ ## Client webhook
68
+
69
+ Configure a single webhook on the client. It is applied to every action when you call `sendToHuman`:
34
70
 
35
71
  ```typescript
36
- import { askHuman } from "robotrock";
37
-
38
- const result = await askHuman({
39
- task: {
40
- type: "approval",
41
- name: "Review change",
42
- actions: [
43
- { id: "approve", title: "Approve" },
44
- { id: "reject", title: "Reject" },
45
- ] as const,
72
+ export const robotrock = createClient({
73
+ webhook: {
74
+ url: "https://your-app.com/api/robotrock/webhook",
75
+ headers: {
76
+ // place your headers here
77
+ }, // optional, defaults to {}
46
78
  },
47
79
  });
48
80
  ```
49
81
 
50
- Uses `ROBOTROCK_API_KEY` from the environment when no `client` is passed.
82
+ ## Automatic fallback polling
83
+
84
+ When the client has no `webhook`, `sendToHuman` polls until a human handles the task or throws `TaskExpiredError` / `TaskTimeoutError`. Configure `polling` on `createClient` (not on `sendToHuman`). Polling stops at the earlier of `polling.timeoutMs` and the task's `validUntil`. You cannot set both `webhook` and `polling` on the same client.
85
+
86
+ ```typescript
87
+ import { createClient, TaskExpiredError, TaskTimeoutError } from "robotrock";
88
+
89
+ const robotrock = createClient({
90
+ app: "my-service",
91
+ polling: {
92
+ intervalMs: 2_000,
93
+ timeoutMs: 5 * 60 * 1_000,
94
+ },
95
+ });
96
+
97
+ const actions = [
98
+ {
99
+ id: "approve",
100
+ title: "Approve",
101
+ schema: {
102
+ type: "object",
103
+ required: ["ticket"],
104
+ properties: {
105
+ ticket: { type: "string" },
106
+ },
107
+ },
108
+ },
109
+ {
110
+ id: "reject",
111
+ title: "Reject",
112
+ schema: {
113
+ type: "object",
114
+ required: ["reason"],
115
+ properties: {
116
+ reason: { type: "string" },
117
+ },
118
+ },
119
+ },
120
+ ] as const;
121
+
122
+ const result = await robotrock.sendToHuman({
123
+ type: "approval",
124
+ name: "Review change",
125
+ actions,
126
+ });
127
+
128
+ if (result.mode === "created") {
129
+ console.log("Task created:", result.task.taskId);
130
+ } else if (result.actionId === "approve") {
131
+ console.log("Approved with ticket", result.data.ticket);
132
+ } else {
133
+ console.log("Rejected because", result.data.reason);
134
+ }
135
+ ```
136
+
137
+ ## Other client methods
138
+
139
+ ```typescript
140
+ await robotrock.getTask("task_...");
141
+ await robotrock.cancelTask("task_...");
142
+ ```
143
+
144
+ ## Webhook verification helper
145
+
146
+ ```typescript
147
+ import {
148
+ verifyRobotRockWebhook,
149
+ RobotRockWebhookError,
150
+ type RobotRockWebhookPayload,
151
+ } from "robotrock";
152
+
153
+ export async function POST(request: Request) {
154
+ try {
155
+ const payload: RobotRockWebhookPayload = await verifyRobotRockWebhook(request);
156
+ console.log(payload.action.id, payload.headers["x-request-id"]);
157
+ return Response.json({ ok: true });
158
+ } catch (error) {
159
+ if (error instanceof RobotRockWebhookError) {
160
+ return Response.json({ error: error.code }, { status: 401 });
161
+ }
162
+ return Response.json({ error: "Unknown error" }, { status: 500 });
163
+ }
164
+ }
165
+ ```
51
166
 
52
167
  ## Trigger.dev
53
168
 
169
+ Install `@trigger.dev/sdk`, re-export SDK tasks from your `trigger/` directory, and call them with `triggerAndWait()`:
170
+
171
+ ```typescript
172
+ // trigger/robotrock.ts
173
+ export { sendToHumanTask, approveByHumanTask } from "robotrock/trigger";
174
+ ```
175
+
54
176
  ```typescript
55
- import { task } from "@trigger.dev/sdk/v3";
56
- import { askHuman } from "robotrock/trigger";
177
+ import { task } from "@trigger.dev/sdk";
178
+ import { approveByHumanTask } from "./robotrock";
57
179
 
58
- export const deploymentApproval = task({
59
- id: "deployment-approval",
180
+ export const gate = task({
181
+ id: "gate",
60
182
  run: async () => {
61
- const result = await askHuman({
62
- task: {
63
- type: "deployment",
64
- name: "Deploy to production",
65
- actions: [
66
- { id: "approve", title: "Deploy" },
67
- { id: "reject", title: "Cancel" },
68
- ],
69
- },
70
- timeout: "1h",
183
+ const waitResult = await approveByHumanTask.triggerAndWait({
184
+ type: "release-gate",
185
+ name: "Ship this release?",
71
186
  });
72
187
 
73
- return result.actionId === "approve";
188
+ if (!waitResult.ok) {
189
+ throw waitResult.error;
190
+ }
191
+
192
+ return waitResult.output.actionId === "approve";
193
+ },
194
+ });
195
+ ```
196
+
197
+ Set `ROBOTROCK_API_KEY` (and optionally `ROBOTROCK_APP`) in your Trigger worker environment.
198
+
199
+ ## Vercel AI SDK
200
+
201
+ Install `ai` and use the optional `robotrock/ai` entry:
202
+
203
+ ```bash
204
+ npm install ai
205
+ ```
206
+
207
+ ```typescript
208
+ import { generateText, stepCountIs } from "ai";
209
+ import { createClient } from "robotrock";
210
+ import { approveByHumanTool, createSendToHumanTool } from "robotrock/ai";
211
+
212
+ const robotrock = createClient({
213
+ app: "my-agent",
214
+ polling: { timeoutMs: 30 * 60_000 },
215
+ });
216
+
217
+ const result = await generateText({
218
+ model: "anthropic/claude-sonnet-4",
219
+ tools: {
220
+ approveByHuman: approveByHumanTool(robotrock),
74
221
  },
222
+ stopWhen: stepCountIs(10),
223
+ prompt: "Plan a release; get human approval before finalizing.",
75
224
  });
76
225
  ```
77
226
 
227
+ For AI SDK tool execution approval (approve `deleteFile` before it runs), use `createRobotRockToolApproval`, `resolveToolApprovalsViaRobotRock`, and `runWithRobotRockApprovals` from `robotrock/ai`. See the [Vercel AI integration docs](https://github.com/robotrock/robotrock/blob/main/apps/docs/content/docs/integrations/vercel-ai.mdx) in this repo.
228
+
229
+ Run long polls inside Trigger.dev or a worker — not short serverless HTTP handlers.
230
+
78
231
  ## Exports
79
232
 
80
233
  | Import | Description |
81
234
  |--------|-------------|
82
- | `robotrock` | Client, `askHuman` (poll), types, schemas |
83
- | `robotrock/trigger` | `askHuman` via Trigger waitpoint tokens |
235
+ | `robotrock` | `createClient`, `RobotRock`, env helpers, types, schemas |
236
+ | `robotrock/trigger` | `sendToHumanTask`, `approveByHumanTask` for Trigger.dev |
237
+ | `robotrock/workflow` | `sendToHumanInWorkflow`, `approveByHumanInWorkflow` for Vercel Workflow |
238
+ | `robotrock/ai` | Vercel AI SDK tools and `toolApproval` bridge (peer: `ai`) |
239
+ | `robotrock/ai/trigger` | Same API, documented for `mode: "trigger"` in Trigger.dev workers |
240
+ | `robotrock/ai/workflow` | Same API, documented for `mode: "workflow"` in Vercel Workflow |
84
241
 
85
242
  ## License
86
243
 
@@ -0,0 +1,24 @@
1
+ import { R as RobotRockToolCallInfo, F as FormatToolApprovalTaskOptions, H as HumanToolResult } from '../workflow-BYeIZgD0.js';
2
+ export { A as APPROVE_BY_HUMAN_ACTIONS, d as ApproveByHumanToolDurableOptions, c as ApproveByHumanToolOptions, r as CreateRobotRockAiToolsOptions, f as CreateSendToHumanToolDurableOptions, C as CreateSendToHumanToolOptions, B as ResolveToolApprovalsOptions, i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, D as RobotRockToolApprovalConfig, z as RobotRockToolApprovalDecision, E as RunWithRobotRockApprovalsOptions, T as ToolApprovalRequestPart, t as applyRobotRockToolApprovalToTools, g as approveByHumanForAi, b as approveByHumanInputSchema, a as approveByHumanTool, u as collectApprovalRequests, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, n as normalizeRobotRockAiContext, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals, h as sendToHumanForAi, s as sendToHumanToolInputSchema } from '../workflow-BYeIZgD0.js';
3
+ import { a as SendToHumanInput } from '../client-Dhk9qxhL.js';
4
+ import { DiscriminatedApprovalResult } from '../schemas/index.js';
5
+ import 'ai';
6
+ import 'zod';
7
+
8
+ declare const DEFAULT_APPROVE_ACTIONS: readonly [{
9
+ readonly id: "approve";
10
+ readonly title: "Approve execution";
11
+ }, {
12
+ readonly id: "deny";
13
+ readonly title: "Deny";
14
+ }];
15
+ /**
16
+ * Build a RobotRock task payload for an AI SDK tool approval request.
17
+ */
18
+ declare function defaultFormatToolApprovalTask(toolCall: RobotRockToolCallInfo, options?: FormatToolApprovalTaskOptions): SendToHumanInput;
19
+
20
+ declare function toHumanToolResult(result: DiscriminatedApprovalResult<readonly {
21
+ id: string;
22
+ }[]>): HumanToolResult;
23
+
24
+ export { DEFAULT_APPROVE_ACTIONS, FormatToolApprovalTaskOptions, HumanToolResult, RobotRockToolCallInfo, defaultFormatToolApprovalTask, toHumanToolResult };
@@ -0,0 +1,46 @@
1
+ import {
2
+ APPROVE_BY_HUMAN_ACTIONS,
3
+ DEFAULT_APPROVE_ACTIONS,
4
+ applyRobotRockToolApprovalToTools,
5
+ approveByHumanForAi,
6
+ approveByHumanInputSchema,
7
+ approveByHumanTool,
8
+ collectApprovalRequests,
9
+ createRobotRockAiTools,
10
+ createRobotRockAiTriggerContext,
11
+ createRobotRockAiWorkflowContext,
12
+ createRobotRockNeedsApproval,
13
+ createRobotRockToolApproval,
14
+ createSendToHumanTool,
15
+ defaultFormatToolApprovalTask,
16
+ normalizeRobotRockAiContext,
17
+ resolveToolApprovalsViaRobotRock,
18
+ runWithRobotRockApprovals,
19
+ sendToHumanForAi,
20
+ sendToHumanToolInputSchema,
21
+ toHumanToolResult
22
+ } from "../chunk-DSZ3GMT4.js";
23
+ import "../chunk-LXM7VS4Q.js";
24
+ export {
25
+ APPROVE_BY_HUMAN_ACTIONS,
26
+ DEFAULT_APPROVE_ACTIONS,
27
+ applyRobotRockToolApprovalToTools,
28
+ approveByHumanForAi,
29
+ approveByHumanInputSchema,
30
+ approveByHumanTool,
31
+ collectApprovalRequests,
32
+ createRobotRockAiTools,
33
+ createRobotRockAiTriggerContext,
34
+ createRobotRockAiWorkflowContext,
35
+ createRobotRockNeedsApproval,
36
+ createRobotRockToolApproval,
37
+ createSendToHumanTool,
38
+ defaultFormatToolApprovalTask,
39
+ normalizeRobotRockAiContext,
40
+ resolveToolApprovalsViaRobotRock,
41
+ runWithRobotRockApprovals,
42
+ sendToHumanForAi,
43
+ sendToHumanToolInputSchema,
44
+ toHumanToolResult
45
+ };
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,5 @@
1
+ export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-BYeIZgD0.js';
2
+ import 'ai';
3
+ import 'zod';
4
+ import '../client-Dhk9qxhL.js';
5
+ import '../schemas/index.js';
@@ -0,0 +1,24 @@
1
+ import {
2
+ applyRobotRockToolApprovalToTools,
3
+ approveByHumanTool,
4
+ createRobotRockAiTools,
5
+ createRobotRockAiTriggerContext,
6
+ createRobotRockNeedsApproval,
7
+ createRobotRockToolApproval,
8
+ createSendToHumanTool,
9
+ resolveToolApprovalsViaRobotRock,
10
+ runWithRobotRockApprovals
11
+ } from "../chunk-DSZ3GMT4.js";
12
+ import "../chunk-LXM7VS4Q.js";
13
+ export {
14
+ applyRobotRockToolApprovalToTools,
15
+ approveByHumanTool,
16
+ createRobotRockAiTools,
17
+ createRobotRockAiTriggerContext,
18
+ createRobotRockNeedsApproval,
19
+ createRobotRockToolApproval,
20
+ createSendToHumanTool,
21
+ resolveToolApprovalsViaRobotRock,
22
+ runWithRobotRockApprovals
23
+ };
24
+ //# sourceMappingURL=trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,5 @@
1
+ export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-BYeIZgD0.js';
2
+ import 'ai';
3
+ import 'zod';
4
+ import '../client-Dhk9qxhL.js';
5
+ import '../schemas/index.js';
@@ -0,0 +1,24 @@
1
+ import {
2
+ applyRobotRockToolApprovalToTools,
3
+ approveByHumanTool,
4
+ createRobotRockAiTools,
5
+ createRobotRockAiWorkflowContext,
6
+ createRobotRockNeedsApproval,
7
+ createRobotRockToolApproval,
8
+ createSendToHumanTool,
9
+ resolveToolApprovalsViaRobotRock,
10
+ runWithRobotRockApprovals
11
+ } from "../chunk-DSZ3GMT4.js";
12
+ import "../chunk-LXM7VS4Q.js";
13
+ export {
14
+ applyRobotRockToolApprovalToTools,
15
+ approveByHumanTool,
16
+ createRobotRockAiTools,
17
+ createRobotRockAiWorkflowContext,
18
+ createRobotRockNeedsApproval,
19
+ createRobotRockToolApproval,
20
+ createSendToHumanTool,
21
+ resolveToolApprovalsViaRobotRock,
22
+ runWithRobotRockApprovals
23
+ };
24
+ //# sourceMappingURL=workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,67 @@
1
+ // src/handler-webhook.ts
2
+ function isRobotRockHandlerWebhookPayload(value) {
3
+ if (typeof value !== "object" || value === null) {
4
+ return false;
5
+ }
6
+ const v = value;
7
+ if (typeof v.taskId !== "string" || typeof v.handledAt !== "string") {
8
+ return false;
9
+ }
10
+ const action = v.action;
11
+ if (typeof action !== "object" || action === null) {
12
+ return false;
13
+ }
14
+ const a = action;
15
+ return typeof a.id === "string" && "data" in a;
16
+ }
17
+
18
+ // src/wait-timing.ts
19
+ var DEFAULT_WAIT_DURATION_MS = 7 * 24 * 60 * 60 * 1e3;
20
+ function resolveWaitTiming(validUntilInput) {
21
+ const validUntilMs = validUntilInput !== void 0 ? parseValidUntilMs(validUntilInput) : Date.now() + DEFAULT_WAIT_DURATION_MS;
22
+ const durationMs = validUntilMs - Date.now();
23
+ if (durationMs <= 0) {
24
+ throw new Error("validUntil must be in the future");
25
+ }
26
+ return {
27
+ validUntil: validUntilInput ?? new Date(validUntilMs),
28
+ timeout: durationMsToTimeout(durationMs)
29
+ };
30
+ }
31
+ function parseValidUntilMs(value) {
32
+ if (value instanceof Date) {
33
+ const ms = value.getTime();
34
+ if (Number.isNaN(ms)) {
35
+ throw new Error("Invalid validUntil: Date is invalid");
36
+ }
37
+ return ms;
38
+ }
39
+ const parsed = Date.parse(value);
40
+ if (Number.isNaN(parsed)) {
41
+ throw new Error("Invalid validUntil: expected a parseable date string");
42
+ }
43
+ return parsed;
44
+ }
45
+ function durationMsToTimeout(durationMs) {
46
+ const seconds = Math.ceil(durationMs / 1e3);
47
+ if (seconds <= 0) {
48
+ throw new Error("validUntil must be in the future");
49
+ }
50
+ if (seconds >= 86400) {
51
+ return `${Math.ceil(seconds / 86400)}d`;
52
+ }
53
+ if (seconds >= 3600) {
54
+ return `${Math.ceil(seconds / 3600)}h`;
55
+ }
56
+ if (seconds >= 60) {
57
+ return `${Math.ceil(seconds / 60)}m`;
58
+ }
59
+ return `${seconds}s`;
60
+ }
61
+
62
+ export {
63
+ isRobotRockHandlerWebhookPayload,
64
+ resolveWaitTiming,
65
+ parseValidUntilMs
66
+ };
67
+ //# sourceMappingURL=chunk-D2FBSEZK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handler-webhook.ts","../src/wait-timing.ts"],"sourcesContent":["/**\n * JSON body posted when a RobotRock action handler runs (webhook or Trigger wait token).\n */\nexport interface RobotRockHandlerWebhookPayload {\n taskId: string;\n action: {\n id: string;\n title: string;\n data: unknown;\n };\n handledBy?: string;\n handledAt: string;\n handlerType: string;\n}\n\nexport function isRobotRockHandlerWebhookPayload(\n value: unknown\n): value is RobotRockHandlerWebhookPayload {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const v = value as Record<string, unknown>;\n if (typeof v.taskId !== \"string\" || typeof v.handledAt !== \"string\") {\n return false;\n }\n const action = v.action;\n if (typeof action !== \"object\" || action === null) {\n return false;\n }\n const a = action as Record<string, unknown>;\n return typeof a.id === \"string\" && \"data\" in a;\n}\n","/** Default wait when `validUntil` is omitted. */\nexport const DEFAULT_WAIT_DURATION_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport function resolveWaitTiming(validUntilInput?: Date | string): {\n validUntil: Date | string;\n timeout: string;\n} {\n const validUntilMs =\n validUntilInput !== undefined\n ? parseValidUntilMs(validUntilInput)\n : Date.now() + DEFAULT_WAIT_DURATION_MS;\n\n const durationMs = validUntilMs - Date.now();\n if (durationMs <= 0) {\n throw new Error(\"validUntil must be in the future\");\n }\n\n return {\n validUntil: validUntilInput ?? new Date(validUntilMs),\n timeout: durationMsToTimeout(durationMs),\n };\n}\n\nexport function parseValidUntilMs(value: Date | string): number {\n if (value instanceof Date) {\n const ms = value.getTime();\n if (Number.isNaN(ms)) {\n throw new Error(\"Invalid validUntil: Date is invalid\");\n }\n return ms;\n }\n\n const parsed = Date.parse(value);\n if (Number.isNaN(parsed)) {\n throw new Error(\"Invalid validUntil: expected a parseable date string\");\n }\n\n return parsed;\n}\n\n/** Duration string for Trigger.dev `wait.createToken` and Workflow `sleep`. */\nexport function durationMsToTimeout(durationMs: number): string {\n const seconds = Math.ceil(durationMs / 1000);\n if (seconds <= 0) {\n throw new Error(\"validUntil must be in the future\");\n }\n\n if (seconds >= 86_400) {\n return `${Math.ceil(seconds / 86_400)}d`;\n }\n if (seconds >= 3_600) {\n return `${Math.ceil(seconds / 3_600)}h`;\n }\n if (seconds >= 60) {\n return `${Math.ceil(seconds / 60)}m`;\n }\n\n return `${seconds}s`;\n}\n"],"mappings":";AAeO,SAAS,iCACd,OACyC;AACzC,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,cAAc,UAAU;AACnE,WAAO;AAAA,EACT;AACA,QAAM,SAAS,EAAE;AACjB,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AACV,SAAO,OAAO,EAAE,OAAO,YAAY,UAAU;AAC/C;;;AC9BO,IAAM,2BAA2B,IAAI,KAAK,KAAK,KAAK;AAEpD,SAAS,kBAAkB,iBAGhC;AACA,QAAM,eACJ,oBAAoB,SAChB,kBAAkB,eAAe,IACjC,KAAK,IAAI,IAAI;AAEnB,QAAM,aAAa,eAAe,KAAK,IAAI;AAC3C,MAAI,cAAc,GAAG;AACnB,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAEA,SAAO;AAAA,IACL,YAAY,mBAAmB,IAAI,KAAK,YAAY;AAAA,IACpD,SAAS,oBAAoB,UAAU;AAAA,EACzC;AACF;AAEO,SAAS,kBAAkB,OAA8B;AAC9D,MAAI,iBAAiB,MAAM;AACzB,UAAM,KAAK,MAAM,QAAQ;AACzB,QAAI,OAAO,MAAM,EAAE,GAAG;AACpB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,MAAI,OAAO,MAAM,MAAM,GAAG;AACxB,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,YAA4B;AAC9D,QAAM,UAAU,KAAK,KAAK,aAAa,GAAI;AAC3C,MAAI,WAAW,GAAG;AAChB,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAEA,MAAI,WAAW,OAAQ;AACrB,WAAO,GAAG,KAAK,KAAK,UAAU,KAAM,CAAC;AAAA,EACvC;AACA,MAAI,WAAW,MAAO;AACpB,WAAO,GAAG,KAAK,KAAK,UAAU,IAAK,CAAC;AAAA,EACtC;AACA,MAAI,WAAW,IAAI;AACjB,WAAO,GAAG,KAAK,KAAK,UAAU,EAAE,CAAC;AAAA,EACnC;AAEA,SAAO,GAAG,OAAO;AACnB;","names":[]}