@prompteryx/sdk 0.4.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prompteryx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # @prompteryx/sdk
2
+
3
+ Official TypeScript SDK for [Prompteryx](https://prompteryx.com).
4
+
5
+ ```bash
6
+ npm install @prompteryx/sdk
7
+ ```
8
+
9
+ Optional peers for the most-used features:
10
+
11
+ ```bash
12
+ npm install zod playwright-core
13
+ ```
14
+
15
+ Works on Node 18+ (global `fetch`), ESM **and** CommonJS.
16
+
17
+ ## Authentication — two key families
18
+
19
+ | Key | Format | Used by | Sent as | Rate limits |
20
+ |---|---|---|---|---|
21
+ | Platform API key | `px_live_…` | everything except `cloudBrowser` | `Authorization: Bearer` | 60 req/min, 10,000 req/day |
22
+ | Cloud Browser key | `pcb_live_…` | `px.cloudBrowser.*` (sessions / fetch / search) | `x-api-key` | per-plan session quotas |
23
+
24
+ Create the `px_live_` key in Settings → API Keys, and the `pcb_live_` key in
25
+ Cloud Platform → API Keys. `px.cloudBrowser.*` throws a descriptive
26
+ `AuthError` if you call it without `cloudBrowserKey`.
27
+
28
+ ```ts
29
+ const px = new Prompteryx({
30
+ apiKey: process.env.PROMPTERYX_API_KEY!, // px_live_…
31
+ cloudBrowserKey: process.env.PROMPTERYX_CLOUD_BROWSER_KEY, // pcb_live_… (only for cloudBrowser)
32
+ })
33
+ ```
34
+
35
+ ## Two AI surfaces — Copilot and Autopilot
36
+
37
+ Prompteryx has two distinct ways AI helps you automate the browser:
38
+
39
+ - **Copilot** — you drive Playwright; copilot helps with ONE step.
40
+ You're in control, AI is the assistant.
41
+ - **Autopilot** — you give a goal in plain English; autopilot drives
42
+ the whole task end-to-end.
43
+
44
+ ```ts
45
+ import { Prompteryx } from '@prompteryx/sdk'
46
+ import { chromium } from 'playwright-core'
47
+ import { z } from 'zod'
48
+
49
+ const px = new Prompteryx({
50
+ apiKey: process.env.PROMPTERYX_API_KEY!,
51
+ cloudBrowserKey: process.env.PROMPTERYX_CLOUD_BROWSER_KEY,
52
+ })
53
+
54
+ // — Copilot: assisted single steps —
55
+ const session = await px.cloudBrowser.sessions.create({
56
+ useProxy: true, proxyLocation: 'us',
57
+ })
58
+ const browser = await chromium.connectOverCDP(session.connectUrl)
59
+ const page = browser.contexts()[0].pages()[0]
60
+ await page.goto('https://example.com/signup')
61
+
62
+ await px.copilot.do(page, 'click the Sign up button')
63
+ await px.copilot.do(page, 'fill the email field with hello@example.com')
64
+
65
+ const plan = await px.copilot.read(page, z.object({
66
+ name: z.string(),
67
+ pricePerMonth: z.number(),
68
+ features: z.array(z.string()),
69
+ }))
70
+
71
+ const actions = await px.copilot.scan(page, 'checkout flow')
72
+
73
+ // — Autopilot: hand it a goal, it runs end-to-end —
74
+ const result = await px.autopilot.run({
75
+ goal: 'Find the cheapest direct flight from London to Lisbon next Tuesday and screenshot the booking page',
76
+ maxSteps: 30, // default is 30
77
+ saveAsWorkflow: true, // ← key superpower, see below
78
+ })
79
+ // result.usage → { aiCredits, tokensIn, tokensOut, costUSD, turns }
80
+ ```
81
+
82
+ ## Action caching = saved workflows
83
+
84
+ This is Prompteryx's killer feature for cost-conscious users. Set
85
+ `saveAsWorkflow: true` on any `autopilot.run` call and the discovered
86
+ action sequence is captured as a permanent Visual Studio workflow:
87
+
88
+ ```ts
89
+ const result = await px.autopilot.run({
90
+ goal: 'Apply for the Senior Engineer role at OpenAI',
91
+ saveAsWorkflow: true,
92
+ savedWorkflowName: 'apply-openai-senior',
93
+ })
94
+
95
+ // Replay forever at ZERO AI cost:
96
+ await px.workflows.run(result.savedWorkflowId!)
97
+ ```
98
+
99
+ The saved workflow is a first-class platform object — schedulable,
100
+ editable in Visual Studio, shareable, API-triggerable. It uses
101
+ multi-option ranked selectors so it stays resilient to UI changes.
102
+
103
+ ## Structured output + final-step vision
104
+
105
+ ```ts
106
+ const result = await px.autopilot.run({
107
+ goal: 'Get the top 3 HN stories',
108
+ outputSchema: { // finalAnswer becomes JSON matching this
109
+ type: 'object',
110
+ properties: {
111
+ stories: {
112
+ type: 'array',
113
+ items: { type: 'object', properties: { title: { type: 'string' }, url: { type: 'string' } } },
114
+ },
115
+ },
116
+ },
117
+ finalStepVision: 'precision', // one extra high-quality look before answering
118
+ })
119
+ const data = JSON.parse(result.finalAnswer!)
120
+ ```
121
+
122
+ ## The SDK surface (verified against the live API)
123
+
124
+ | Resource | What it does |
125
+ |---|---|
126
+ | `px.workflows.{list,get,run,runAndWait}` | Visual Studio workflows. |
127
+ | `px.executions.{get,wait,logs,stream,getNodeOutput}` | Execution status + logs. |
128
+ | `px.cloudBrowser.sessions.{create,get,list,close}` | Cloud browser sessions for your own Playwright code (`pcb_live_` key). |
129
+ | `px.cloudBrowser.{fetch,search}` | One-shot page fetch + web search via the cloud browser, no session management (`pcb_live_` key). |
130
+ | `px.copilot.{do,read,scan}` | Assisted single-step primitives on a Playwright page. |
131
+ | `px.autopilot.{run,stream,stop}` | Autonomous multi-step agent with `saveAsWorkflow` action caching. |
132
+ | `px.schedules.{list,get,create,update,delete}` | Server-side cron schedules — the platform fires them even when no client is connected. |
133
+ | `px.profiles.{list,get,create,delete}` | Cloud and local Chrome profiles. |
134
+ | `px.subscription.get` | Plan, balances, top-ups. |
135
+
136
+ > Removed in 0.4.0 after a live-API audit: `connectHub`, `customNodes`,
137
+ > `templates`, `apiKeys`, `recordings`, `subscription.usage`, and
138
+ > `sessions.getDownloads/getRecording` — their routes don't exist on the
139
+ > live API today (or, for `apiKeys`, can never accept API-key auth). A
140
+ > smaller honest SDK beats a 404ing surface; they return when their
141
+ > endpoints ship.
142
+
143
+ ## Models
144
+
145
+ `autopilot.run({ model })` defaults to **`gemini-3.5-flash`** (recommended —
146
+ fast and cheap). The full catalog of 32 ids is exported as
147
+ `AUTOPILOT_MODELS` (type `AutopilotModel`), mirrored from the platform's
148
+ canonical list — highlights:
149
+
150
+ - `gemini-3.5-flash` (default), `gemini-3.7-flash` (newest GA Flash),
151
+ `gemini-3.6-flash`, `gemini-default` (legacy 2.5)
152
+ - `claude-sonnet-4-6`, `claude-opus-4-8` (native computer use)
153
+ - `gpt-5.6-terra`, `gpt-5.6-sol`, `gpt-5.5`
154
+ - Vision-loop variants (`claude-fable-5-vision`, `gpt-5.6-luna-vision`, …)
155
+ and experimental harness engines (`model-h`, `modelc`, …)
156
+
157
+ Unknown ids are forwarded for forward-compatibility but hard-400 with
158
+ `UNSUPPORTED_MODEL` under strict validation — stick to the catalog.
159
+
160
+ ## One-shot fetch and search
161
+
162
+ No Playwright, no session management — a real cloud Chromium loads the page
163
+ (so JS-rendered sites work) and returns token-efficient content:
164
+
165
+ ```ts
166
+ const pageData = await px.cloudBrowser.fetch({
167
+ url: 'https://example.com/pricing',
168
+ format: 'markdown', // 'text' (default) | 'markdown' | 'html' | 'links'
169
+ waitForSelector: '.pricing-table', // wait for JS-rendered content
170
+ selectors: ['.pricing-table .plan'], // deterministic CSS extraction (count + text + html)
171
+ screenshot: true, // base64 JPEG in .screenshotBase64
172
+ })
173
+
174
+ // Web search — DuckDuckGo in a real browser, structured results:
175
+ const results = await px.cloudBrowser.search({ query: 'best CDP libraries', limit: 5 })
176
+ // [{ title, url, snippet }, …]
177
+ ```
178
+
179
+ ## Local mode — use your own Chrome, zero cloud minutes
180
+
181
+ If you want to skip cloud-browser minutes entirely, you can point the SDK at
182
+ your own local Chrome via the **Prompteryx plugin + Electron runner**. Two
183
+ paths connect your own Playwright over CDP (autopilot local mode is a third —
184
+ covered just below):
185
+
186
+ **1. `px.cloudBrowser.sessions.create({ target: 'local' })`** — returns a
187
+ session whose `connectUrl` points at `http://127.0.0.1:9222`. Hand that to
188
+ Playwright and drive your own Chrome:
189
+
190
+ ```ts
191
+ const s = await px.cloudBrowser.sessions.create({ target: 'local' })
192
+ // s.connectUrl === 'http://127.0.0.1:9222'
193
+ const browser = await chromium.connectOverCDP(s.connectUrl)
194
+ // …drive it. No cloud minutes billed.
195
+ await px.cloudBrowser.sessions.close(s.id) // no-op for local sessions
196
+ ```
197
+
198
+ **2. `px.workflows.run(id, { execution: { target: 'local' } })`** — runs
199
+ a saved workflow against your local Chrome instead of the cloud browser:
200
+
201
+ ```ts
202
+ await px.workflows.run('wf_abc123', {
203
+ execution: { target: 'local', chromeProfile: 'Work' },
204
+ })
205
+ ```
206
+
207
+ **Prereqs (both paths):**
208
+ - The **Prompteryx plugin** is installed in your Chrome.
209
+ - The **Electron runner** is running on the same machine as the SDK consumer
210
+ (the SDK talks to `localhost`, so they must co-locate).
211
+ - Chrome is launched with remote debugging on port 9222. The plugin manages
212
+ this for you when it's connected.
213
+
214
+ ### Autopilot in local mode
215
+
216
+ `px.autopilot.run({ target: 'local' })` runs the autonomous agent against
217
+ your own local Chrome. Unlike the two CDP paths above, it talks **directly to
218
+ the Prompteryx desktop app** at `http://localhost:61337` (override with
219
+ `runnerUrl`):
220
+
221
+ ```ts
222
+ const result = await px.autopilot.run({
223
+ target: 'local',
224
+ goal: 'Open my Gmail and tell me how many unread emails I have',
225
+ aiVision: 'balanced', // quality preset — lower = cheaper/faster, higher = more accurate
226
+ maxSteps: 30,
227
+ })
228
+ ```
229
+
230
+ **Requirements:** the **Prompteryx desktop app** must be **running and signed
231
+ in** on this machine, and your code must run on the **same machine** (the SDK
232
+ talks to `localhost`). Zero cloud-browser minutes are billed — AI Credits
233
+ still apply — and the browser tab stays open after the run for inspection or
234
+ follow-ups. It supports the same knobs as cloud: `model`, `aiVision` preset,
235
+ `maxSteps`, `maxCredits`, `costSaving`.
236
+
237
+ > `px.autopilot.stream({ target: 'local' })` throws a clear message —
238
+ > use the blocking `autopilot.run` for local runs.
239
+
240
+ ## Streaming an autopilot run
241
+
242
+ `autopilot.stream()` runs the task over the keep-alive stream endpoint
243
+ (`/api/v1/ai-browser/execute-stream`). Note the protocol: the server sends
244
+ heartbeats while the run executes and the full result at the end — so steps
245
+ arrive together when the run finishes (not one-by-one live), ending with a
246
+ `{ step: -1, action: 'done' }` sentinel whose `result` carries the full
247
+ `AutopilotRunResult`. Prefer `run()` unless you want the keep-alive
248
+ transport for a long single-request run.
249
+
250
+ ```ts
251
+ for await (const step of px.autopilot.stream({ goal: 'Buy a ticket' })) {
252
+ if (step.action === 'done') break
253
+ console.log('Step', step.step, '→', step.action)
254
+ }
255
+ ```
256
+
257
+ ## Stopping a job
258
+
259
+ ```ts
260
+ await px.autopilot.stop(jobId) // POST { jobId, mode: 'stop' } — stops at the next step boundary
261
+ ```
262
+
263
+ ## Configuration
264
+
265
+ ```ts
266
+ new Prompteryx({
267
+ apiKey: '…', // required — px_live_…
268
+ cloudBrowserKey: '…', // optional — pcb_live_…, for px.cloudBrowser.*
269
+ baseUrl: 'https://prompteryx.com', // override for self-hosted
270
+ timeoutMs: 60_000, // default per-request timeout
271
+ maxRetries: 2, // retries for transient 5xx/network errors
272
+ defaultHeaders: { 'X-Project-Id': '…' },
273
+ fetch: customFetch, // for tests / non-default runtimes
274
+ })
275
+ ```
276
+
277
+ ## Typed error handling
278
+
279
+ ```ts
280
+ import {
281
+ AuthError, QuotaError, RateLimitError, NotFoundError,
282
+ ValidationError, ServerError, NetworkError, TimeoutError, ParseError,
283
+ } from '@prompteryx/sdk'
284
+
285
+ try {
286
+ await px.workflows.run('wf_abc')
287
+ } catch (err) {
288
+ if (err instanceof QuotaError) {
289
+ // Out of AI Credits / cloud minutes / proxy data / etc.
290
+ console.log('Exhausted resources:', err.resources)
291
+ } else if (err instanceof RateLimitError) {
292
+ // Back off — err.retryAfterSeconds is set when the server provided one
293
+ } else {
294
+ throw err
295
+ }
296
+ }
297
+ ```
298
+
299
+ ## Streaming log events
300
+
301
+ ```ts
302
+ const exec = await px.workflows.run('wf_abc')
303
+ for await (const ev of px.executions.stream(exec.executionId)) {
304
+ console.log(`[${ev.level || 'info'}]`, ev.message)
305
+ if (ev.type === 'done') break
306
+ }
307
+ ```
308
+
309
+ ## Future-proof customisation
310
+
311
+ `autopilot.run` exposes every option the in-app AI Browser Agent UI
312
+ exposes — model, AI Vision quality preset, final-step vision, structured
313
+ output, max steps, credit cap, cost-saving batching, system-prompt
314
+ override, allowed tools, viewport, proxy, recording, profile id, etc. New
315
+ options added server-side ride through the `passthrough` field without an
316
+ SDK release:
317
+
318
+ ```ts
319
+ await px.autopilot.run({
320
+ goal: '...',
321
+ model: 'gemini-3.7-flash', // catalog in AUTOPILOT_MODELS; default gemini-3.5-flash
322
+ aiVision: 'balanced', // quality preset: lower = cheaper/faster, higher = more accurate
323
+ maxCredits: 50, // hard AI-Credit cap (cost guardrail)
324
+ costSaving: true, // batch several safe actions per screenshot
325
+ allowedTools: ['click', 'extract'], // restrict capabilities for read-only tasks
326
+ systemPromptOverride: 'Always confirm before submitting any form.',
327
+ passthrough: { newServerOption: true },
328
+ })
329
+ ```
330
+
331
+ Same passthrough pattern on `workflows.run` and
332
+ `cloudBrowser.sessions.create`.
333
+
334
+ ## License
335
+
336
+ MIT
@@ -0,0 +1,272 @@
1
+ // src/errors.ts
2
+ var PrompteryxError = class extends Error {
3
+ constructor(message, opts = {}) {
4
+ super(message);
5
+ this.name = "PrompteryxError";
6
+ this.status = opts.status;
7
+ this.code = opts.code;
8
+ this.requestId = opts.requestId;
9
+ this.raw = opts.raw;
10
+ Object.setPrototypeOf(this, new.target.prototype);
11
+ }
12
+ };
13
+ var AuthError = class _AuthError extends PrompteryxError {
14
+ constructor(message, opts = {}) {
15
+ super(message, opts);
16
+ this.name = "AuthError";
17
+ Object.setPrototypeOf(this, _AuthError.prototype);
18
+ }
19
+ };
20
+ var QuotaError = class _QuotaError extends PrompteryxError {
21
+ constructor(message, opts = {}) {
22
+ super(message, opts);
23
+ this.name = "QuotaError";
24
+ this.resources = opts.resources;
25
+ Object.setPrototypeOf(this, _QuotaError.prototype);
26
+ }
27
+ };
28
+ var NotFoundError = class _NotFoundError extends PrompteryxError {
29
+ constructor(message, opts = {}) {
30
+ super(message, opts);
31
+ this.name = "NotFoundError";
32
+ Object.setPrototypeOf(this, _NotFoundError.prototype);
33
+ }
34
+ };
35
+ var ValidationError = class _ValidationError extends PrompteryxError {
36
+ constructor(message, opts = {}) {
37
+ super(message, opts);
38
+ this.name = "ValidationError";
39
+ Object.setPrototypeOf(this, _ValidationError.prototype);
40
+ }
41
+ };
42
+ var RateLimitError = class _RateLimitError extends PrompteryxError {
43
+ constructor(message, opts = {}) {
44
+ super(message, opts);
45
+ this.name = "RateLimitError";
46
+ this.retryAfterSeconds = opts.retryAfterSeconds;
47
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
48
+ }
49
+ };
50
+ var ServerError = class _ServerError extends PrompteryxError {
51
+ constructor(message, opts = {}) {
52
+ super(message, opts);
53
+ this.name = "ServerError";
54
+ Object.setPrototypeOf(this, _ServerError.prototype);
55
+ }
56
+ };
57
+ var NetworkError = class _NetworkError extends PrompteryxError {
58
+ constructor(message, opts = {}) {
59
+ super(message, opts);
60
+ this.name = "NetworkError";
61
+ Object.setPrototypeOf(this, _NetworkError.prototype);
62
+ }
63
+ };
64
+ var ParseError = class _ParseError extends PrompteryxError {
65
+ constructor(message, opts = {}) {
66
+ super(message, opts);
67
+ this.name = "ParseError";
68
+ Object.setPrototypeOf(this, _ParseError.prototype);
69
+ }
70
+ };
71
+ var TimeoutError = class _TimeoutError extends PrompteryxError {
72
+ constructor(message, opts = {}) {
73
+ super(message, opts);
74
+ this.name = "TimeoutError";
75
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
76
+ }
77
+ };
78
+
79
+ // src/page.ts
80
+ var CopilotHelpers = class {
81
+ constructor(http) {
82
+ this.http = http;
83
+ }
84
+ /**
85
+ * Execute a single natural-language action against the page.
86
+ *
87
+ * ```ts
88
+ * await px.copilot.do(page, 'click the Sign up button')
89
+ * await px.copilot.do(page, 'fill the email field with hello@example.com')
90
+ * ```
91
+ *
92
+ * Internally: snapshot the page → POST `/api/v1/copilot/do` → server
93
+ * returns a structured action plan (ranked selector + alternatives +
94
+ * value + a normalised vision point) → SDK executes locally, trying the
95
+ * ranked selectors in order, and ONLY if every selector fails, falling
96
+ * back to an AI-vision coordinate click. Costs 1 AI Credit per call.
97
+ *
98
+ * This is the key resilience advantage over a pure-LLM `act()`: the cheap,
99
+ * deterministic ranked selectors are tried first (no flakiness, no re-asking
100
+ * the model); the vision fallback is a safety net, not the default path. Pass
101
+ * `{ visionFallback: false }` to disable the fallback (selectors-only).
102
+ */
103
+ async do(page, instruction, opts = {}) {
104
+ const start = Date.now();
105
+ const snapshot = await this.snapshotPage(page);
106
+ const plan = await this.http.request("/api/v1/copilot/do", {
107
+ method: "POST",
108
+ body: { instruction, snapshot },
109
+ timeoutMs: 45e3
110
+ });
111
+ const allowVision = opts.visionFallback !== false;
112
+ try {
113
+ const usedVisionFallback = await this.runAction(page, plan, opts.timeout ?? 15e3, allowVision);
114
+ return {
115
+ success: true,
116
+ action: plan.description,
117
+ selector: plan.selector,
118
+ usedVisionFallback,
119
+ durationMs: Date.now() - start
120
+ };
121
+ } catch (err) {
122
+ return {
123
+ success: false,
124
+ action: plan.description,
125
+ selector: plan.selector,
126
+ durationMs: Date.now() - start,
127
+ error: err instanceof Error ? err.message : String(err)
128
+ };
129
+ }
130
+ }
131
+ /**
132
+ * Pull typed structured data from the page conforming to a schema.
133
+ *
134
+ * ```ts
135
+ * import { z } from 'zod'
136
+ * const product = await px.copilot.read(page, z.object({
137
+ * name: z.string(),
138
+ * pricePerMonth: z.number(),
139
+ * features: z.array(z.string()),
140
+ * }))
141
+ * // product is fully typed; ValidationError is thrown if the model
142
+ * // returns data that doesn't match the schema.
143
+ * ```
144
+ *
145
+ * Accepts a Zod schema (preferred — gives you compile-time types)
146
+ * OR a raw JSON Schema via `{ jsonSchema: ... }` if you don't want
147
+ * a `zod` peer dep.
148
+ */
149
+ async read(page, schema) {
150
+ const snapshot = await this.snapshotPage(page);
151
+ const jsonSchema = "jsonSchema" in schema ? schema.jsonSchema : this.zodToJsonSchema(schema);
152
+ const raw = await this.http.request(
153
+ "/api/v1/copilot/read",
154
+ { method: "POST", body: { snapshot, jsonSchema }, timeoutMs: 6e4 }
155
+ );
156
+ if ("parse" in schema && typeof schema.parse === "function") {
157
+ try {
158
+ return schema.parse(raw.data);
159
+ } catch (e) {
160
+ throw new ValidationError(
161
+ `Extracted data didn't match the schema: ${e instanceof Error ? e.message : String(e)}`,
162
+ { raw: raw.data }
163
+ );
164
+ }
165
+ }
166
+ return raw.data;
167
+ }
168
+ /**
169
+ * Scan the page for available actions. Returns a ranked list of
170
+ * actions a user / agent could take next, with selectors + multi-
171
+ * option fallbacks + human-readable descriptions.
172
+ *
173
+ * Useful as a pre-step to `do()` for resilient automations:
174
+ * const actions = await px.copilot.scan(page, 'sign up flow')
175
+ * const target = actions.find(a => a.description.includes('Sign up'))
176
+ * if (target) await px.copilot.do(page, target.example ?? `click ${target.description}`)
177
+ */
178
+ async scan(page, hint) {
179
+ const snapshot = await this.snapshotPage(page);
180
+ const res = await this.http.request(
181
+ "/api/v1/copilot/scan",
182
+ { method: "POST", body: { snapshot, hint }, timeoutMs: 45e3 }
183
+ );
184
+ return res.actions ?? [];
185
+ }
186
+ // ── internals ────────────────────────────────────────────────────────
187
+ async snapshotPage(page) {
188
+ const buf = await page.screenshot({ type: "jpeg", quality: 60, fullPage: false });
189
+ const screenshot = bufferToBase64(buf);
190
+ return {
191
+ url: page.url(),
192
+ title: await page.title().catch(() => ""),
193
+ screenshot
194
+ };
195
+ }
196
+ /** Execute the plan. Returns true if the AI-vision fallback was used. */
197
+ async runAction(page, plan, timeout, allowVision) {
198
+ if (plan.type === "press_key" && plan.value) {
199
+ await page.keyboard.press(plan.value);
200
+ return false;
201
+ }
202
+ if (plan.type === "goto" && plan.url) {
203
+ await page.goto(plan.url, { timeout });
204
+ return false;
205
+ }
206
+ const candidates = [plan.selector, ...plan.alternativeSelectors ?? []].filter(
207
+ (s) => typeof s === "string" && s.length > 0
208
+ );
209
+ let lastErr;
210
+ for (const sel of candidates) {
211
+ try {
212
+ switch (plan.type) {
213
+ case "click":
214
+ await page.click(sel, { timeout });
215
+ return false;
216
+ case "fill":
217
+ await page.fill(sel, plan.value ?? "", { timeout });
218
+ return false;
219
+ case "select":
220
+ await page.selectOption(sel, plan.value ?? "", { timeout });
221
+ return false;
222
+ case "hover":
223
+ await page.hover(sel, { timeout });
224
+ return false;
225
+ default:
226
+ throw new Error(`Unsupported action type: ${plan.type}`);
227
+ }
228
+ } catch (e) {
229
+ lastErr = e;
230
+ }
231
+ }
232
+ if (allowVision && plan.point && plan.type !== "select" && page.mouse) {
233
+ const vp = page.viewportSize?.() || { width: 1280, height: 800 };
234
+ const x = Math.round(plan.point.x / 1e3 * vp.width);
235
+ const y = Math.round(plan.point.y / 1e3 * vp.height);
236
+ await page.mouse.click(x, y);
237
+ if (plan.type === "fill" && plan.value) await page.keyboard.type(plan.value, { delay: 20 });
238
+ if (plan.type === "hover" && page.mouse.move) await page.mouse.move(x, y);
239
+ return true;
240
+ }
241
+ if (candidates.length === 0) throw new Error(`No selector or vision point returned for action ${plan.type}`);
242
+ throw lastErr ?? new Error(`No selector worked for ${plan.type} (and vision fallback unavailable)`);
243
+ }
244
+ zodToJsonSchema(schema) {
245
+ return { type: "object", _zodHint: String(schema) };
246
+ }
247
+ };
248
+ function bufferToBase64(buf) {
249
+ if (typeof Buffer !== "undefined" && buf instanceof Buffer) {
250
+ return buf.toString("base64");
251
+ }
252
+ let binary = "";
253
+ const bytes = buf;
254
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
255
+ const g = globalThis;
256
+ return g.btoa ? g.btoa(binary) : Buffer.from(binary, "binary").toString("base64");
257
+ }
258
+
259
+ export {
260
+ PrompteryxError,
261
+ AuthError,
262
+ QuotaError,
263
+ NotFoundError,
264
+ ValidationError,
265
+ RateLimitError,
266
+ ServerError,
267
+ NetworkError,
268
+ ParseError,
269
+ TimeoutError,
270
+ CopilotHelpers
271
+ };
272
+ //# sourceMappingURL=chunk-ODLNHNQT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/page.ts"],"sourcesContent":["/**\r\n * Typed error hierarchy for the Prompteryx SDK.\r\n *\r\n * Every error from the SDK is an instance of `PrompteryxError`. Subclass\r\n * by HTTP status family so callers can `if (err instanceof QuotaError)`\r\n * without parsing strings. Network/parse errors get their own classes\r\n * too so retries can fork on category.\r\n */\r\n\r\n/** Base class — every SDK error inherits from this. */\r\nexport class PrompteryxError extends Error {\r\n public readonly status?: number\r\n public readonly code?: string\r\n public readonly requestId?: string\r\n public readonly raw?: unknown\r\n\r\n constructor(message: string, opts: { status?: number; code?: string; requestId?: string; raw?: unknown } = {}) {\r\n super(message)\r\n this.name = 'PrompteryxError'\r\n this.status = opts.status\r\n this.code = opts.code\r\n this.requestId = opts.requestId\r\n this.raw = opts.raw\r\n // Restore prototype chain for `instanceof` checks across realms.\r\n Object.setPrototypeOf(this, new.target.prototype)\r\n }\r\n}\r\n\r\n/** 401 / 403 — bad or revoked API key, or insufficient scope. */\r\nexport class AuthError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'AuthError'\r\n Object.setPrototypeOf(this, AuthError.prototype)\r\n }\r\n}\r\n\r\n/** 402 — plan allowance exhausted (AI Credits, cloud minutes, etc.). */\r\nexport class QuotaError extends PrompteryxError {\r\n /** Which resources are exhausted, when known. */\r\n public readonly resources?: string[]\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] & { resources?: string[] } = {}) {\r\n super(message, opts)\r\n this.name = 'QuotaError'\r\n this.resources = opts.resources\r\n Object.setPrototypeOf(this, QuotaError.prototype)\r\n }\r\n}\r\n\r\n/** 404 — resource doesn't exist (workflow id, execution id, session id). */\r\nexport class NotFoundError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'NotFoundError'\r\n Object.setPrototypeOf(this, NotFoundError.prototype)\r\n }\r\n}\r\n\r\n/** 422 — request body shape was wrong. */\r\nexport class ValidationError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'ValidationError'\r\n Object.setPrototypeOf(this, ValidationError.prototype)\r\n }\r\n}\r\n\r\n/** 429 — rate-limited. Caller can retry after `retryAfterSeconds`. */\r\nexport class RateLimitError extends PrompteryxError {\r\n public readonly retryAfterSeconds?: number\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] & { retryAfterSeconds?: number } = {}) {\r\n super(message, opts)\r\n this.name = 'RateLimitError'\r\n this.retryAfterSeconds = opts.retryAfterSeconds\r\n Object.setPrototypeOf(this, RateLimitError.prototype)\r\n }\r\n}\r\n\r\n/** 5xx — server-side failure. SDK retries these by default for idempotent ops. */\r\nexport class ServerError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'ServerError'\r\n Object.setPrototypeOf(this, ServerError.prototype)\r\n }\r\n}\r\n\r\n/** Network / DNS / socket / aborted — never reached the server. */\r\nexport class NetworkError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'NetworkError'\r\n Object.setPrototypeOf(this, NetworkError.prototype)\r\n }\r\n}\r\n\r\n/** Response body parse failure (server returned a non-JSON 500 page, etc.). */\r\nexport class ParseError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'ParseError'\r\n Object.setPrototypeOf(this, ParseError.prototype)\r\n }\r\n}\r\n\r\n/** Timed-out waiting for a long-running op (execution polling, agent run). */\r\nexport class TimeoutError extends PrompteryxError {\r\n constructor(message: string, opts: ConstructorParameters<typeof PrompteryxError>[1] = {}) {\r\n super(message, opts)\r\n this.name = 'TimeoutError'\r\n Object.setPrototypeOf(this, TimeoutError.prototype)\r\n }\r\n}\r\n","/**\r\n * Copilot primitives — `do`, `read`, `scan` — operating on a Playwright\r\n * page.\r\n *\r\n * Two namespaces in the Prompteryx SDK:\r\n * • Copilot → SDK helps with ONE step you describe in plain English.\r\n * Your code drives Playwright; copilot just figures out\r\n * which selector to click / what data to pull / what's\r\n * discoverable on the current page.\r\n * • Autopilot → SDK runs an autonomous multi-step task with no per-\r\n * action involvement from you. See ./resources/autopilot.ts.\r\n *\r\n * The copilot primitives:\r\n * • `do(page, instruction)` — execute a single natural-language\r\n * action (\"click the Sign up button\", \"fill the email field with\r\n * hello@example.com\"). Returns a structured result describing\r\n * what was done.\r\n * • `read(page, schema)` — pull typed data from the page. Pass a\r\n * Zod schema or a raw JSON Schema; the SDK returns the populated\r\n * object validated against your schema.\r\n * • `scan(page, hint?)` — list discoverable actions on the page.\r\n * Useful as a pre-step to `do()` for resilient automations:\r\n * scan → pick the action whose description matches your intent →\r\n * do() against it.\r\n *\r\n * Architecturally the SDK never drives the browser server-side. The\r\n * server returns a structured plan and the SDK executes it locally\r\n * against your Playwright page, so your trace, debugger, and any\r\n * custom event handlers continue to work normally.\r\n */\r\n\r\nimport type { HttpClient } from './client'\r\nimport { ValidationError } from './errors'\r\nimport type { CopilotDoResult, DiscoveredAction } from './types'\r\n\r\n/** Minimal Page surface — anything satisfying this works (Playwright's\r\n * Page does, by structural typing, without an explicit import). */\r\nexport interface PageLike {\r\n url(): string\r\n title(): Promise<string>\r\n screenshot(opts?: { type?: 'png' | 'jpeg'; quality?: number; fullPage?: boolean }): Promise<Buffer | Uint8Array>\r\n evaluate<T>(fn: (...args: unknown[]) => T): Promise<T>\r\n click(selector: string, opts?: { timeout?: number }): Promise<void>\r\n fill(selector: string, value: string, opts?: { timeout?: number }): Promise<void>\r\n selectOption(selector: string, value: string | string[], opts?: { timeout?: number }): Promise<unknown>\r\n goto(url: string, opts?: { timeout?: number }): Promise<unknown>\r\n hover(selector: string, opts?: { timeout?: number }): Promise<void>\r\n keyboard: { press(key: string): Promise<void>; type(text: string, opts?: { delay?: number }): Promise<void> }\r\n // Used by the AI-vision fallback (when all ranked selectors fail). Playwright's\r\n // Page provides both; optional so a minimal page can still satisfy PageLike.\r\n mouse?: { click(x: number, y: number): Promise<void>; move?(x: number, y: number): Promise<void> }\r\n viewportSize?(): { width: number; height: number } | null\r\n locator(selector: string): {\r\n first(): { isVisible(opts?: { timeout?: number }): Promise<boolean>; textContent(opts?: { timeout?: number }): Promise<string | null> }\r\n }\r\n}\r\n\r\ninterface ZodLikeSchema<T> {\r\n parse(input: unknown): T\r\n _def?: unknown\r\n}\r\n\r\nexport class CopilotHelpers {\r\n constructor(private readonly http: HttpClient) {}\r\n\r\n /**\r\n * Execute a single natural-language action against the page.\r\n *\r\n * ```ts\r\n * await px.copilot.do(page, 'click the Sign up button')\r\n * await px.copilot.do(page, 'fill the email field with hello@example.com')\r\n * ```\r\n *\r\n * Internally: snapshot the page → POST `/api/v1/copilot/do` → server\r\n * returns a structured action plan (ranked selector + alternatives +\r\n * value + a normalised vision point) → SDK executes locally, trying the\r\n * ranked selectors in order, and ONLY if every selector fails, falling\r\n * back to an AI-vision coordinate click. Costs 1 AI Credit per call.\r\n *\r\n * This is the key resilience advantage over a pure-LLM `act()`: the cheap,\r\n * deterministic ranked selectors are tried first (no flakiness, no re-asking\r\n * the model); the vision fallback is a safety net, not the default path. Pass\r\n * `{ visionFallback: false }` to disable the fallback (selectors-only).\r\n */\r\n async do(page: PageLike, instruction: string, opts: { timeout?: number; visionFallback?: boolean } = {}): Promise<CopilotDoResult> {\r\n const start = Date.now()\r\n const snapshot = await this.snapshotPage(page)\r\n const plan = await this.http.request<{\r\n type: 'click' | 'fill' | 'select' | 'hover' | 'press_key' | 'goto'\r\n selector?: string\r\n alternativeSelectors?: string[]\r\n point?: { x: number; y: number }\r\n value?: string\r\n url?: string\r\n description: string\r\n }>('/api/v1/copilot/do', {\r\n method: 'POST',\r\n body: { instruction, snapshot },\r\n timeoutMs: 45_000,\r\n })\r\n const allowVision = opts.visionFallback !== false\r\n try {\r\n const usedVisionFallback = await this.runAction(page, plan, opts.timeout ?? 15_000, allowVision)\r\n return {\r\n success: true,\r\n action: plan.description,\r\n selector: plan.selector,\r\n usedVisionFallback,\r\n durationMs: Date.now() - start,\r\n }\r\n } catch (err) {\r\n return {\r\n success: false,\r\n action: plan.description,\r\n selector: plan.selector,\r\n durationMs: Date.now() - start,\r\n error: err instanceof Error ? err.message : String(err),\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Pull typed structured data from the page conforming to a schema.\r\n *\r\n * ```ts\r\n * import { z } from 'zod'\r\n * const product = await px.copilot.read(page, z.object({\r\n * name: z.string(),\r\n * pricePerMonth: z.number(),\r\n * features: z.array(z.string()),\r\n * }))\r\n * // product is fully typed; ValidationError is thrown if the model\r\n * // returns data that doesn't match the schema.\r\n * ```\r\n *\r\n * Accepts a Zod schema (preferred — gives you compile-time types)\r\n * OR a raw JSON Schema via `{ jsonSchema: ... }` if you don't want\r\n * a `zod` peer dep.\r\n */\r\n async read<T>(page: PageLike, schema: ZodLikeSchema<T> | { jsonSchema: unknown }): Promise<T> {\r\n const snapshot = await this.snapshotPage(page)\r\n const jsonSchema = 'jsonSchema' in schema\r\n ? (schema as { jsonSchema: unknown }).jsonSchema\r\n : this.zodToJsonSchema(schema as ZodLikeSchema<T>)\r\n const raw = await this.http.request<{ data: unknown }>(\r\n '/api/v1/copilot/read',\r\n { method: 'POST', body: { snapshot, jsonSchema }, timeoutMs: 60_000 },\r\n )\r\n if ('parse' in (schema as object) && typeof (schema as ZodLikeSchema<T>).parse === 'function') {\r\n try {\r\n return (schema as ZodLikeSchema<T>).parse(raw.data)\r\n } catch (e) {\r\n throw new ValidationError(\r\n `Extracted data didn't match the schema: ${e instanceof Error ? e.message : String(e)}`,\r\n { raw: raw.data },\r\n )\r\n }\r\n }\r\n return raw.data as T\r\n }\r\n\r\n /**\r\n * Scan the page for available actions. Returns a ranked list of\r\n * actions a user / agent could take next, with selectors + multi-\r\n * option fallbacks + human-readable descriptions.\r\n *\r\n * Useful as a pre-step to `do()` for resilient automations:\r\n * const actions = await px.copilot.scan(page, 'sign up flow')\r\n * const target = actions.find(a => a.description.includes('Sign up'))\r\n * if (target) await px.copilot.do(page, target.example ?? `click ${target.description}`)\r\n */\r\n async scan(page: PageLike, hint?: string): Promise<DiscoveredAction[]> {\r\n const snapshot = await this.snapshotPage(page)\r\n const res = await this.http.request<{ actions: DiscoveredAction[] }>(\r\n '/api/v1/copilot/scan',\r\n { method: 'POST', body: { snapshot, hint }, timeoutMs: 45_000 },\r\n )\r\n return res.actions ?? []\r\n }\r\n\r\n // ── internals ────────────────────────────────────────────────────────\r\n\r\n private async snapshotPage(page: PageLike): Promise<{ url: string; title: string; screenshot: string }> {\r\n const buf = await page.screenshot({ type: 'jpeg', quality: 60, fullPage: false })\r\n const screenshot = bufferToBase64(buf)\r\n return {\r\n url: page.url(),\r\n title: await page.title().catch(() => ''),\r\n screenshot,\r\n }\r\n }\r\n\r\n /** Execute the plan. Returns true if the AI-vision fallback was used. */\r\n private async runAction(\r\n page: PageLike,\r\n plan: {\r\n type: 'click' | 'fill' | 'select' | 'hover' | 'press_key' | 'goto'\r\n selector?: string\r\n alternativeSelectors?: string[]\r\n point?: { x: number; y: number }\r\n value?: string\r\n url?: string\r\n },\r\n timeout: number,\r\n allowVision: boolean,\r\n ): Promise<boolean> {\r\n if (plan.type === 'press_key' && plan.value) {\r\n await page.keyboard.press(plan.value)\r\n return false\r\n }\r\n if (plan.type === 'goto' && plan.url) {\r\n await page.goto(plan.url, { timeout })\r\n return false\r\n }\r\n const candidates = [plan.selector, ...(plan.alternativeSelectors ?? [])].filter(\r\n (s): s is string => typeof s === 'string' && s.length > 0,\r\n )\r\n // 1) Try the ranked selectors in order (cheap + deterministic).\r\n let lastErr: unknown\r\n for (const sel of candidates) {\r\n try {\r\n switch (plan.type) {\r\n case 'click': await page.click(sel, { timeout }); return false\r\n case 'fill': await page.fill(sel, plan.value ?? '', { timeout }); return false\r\n case 'select': await page.selectOption(sel, plan.value ?? '', { timeout }); return false\r\n case 'hover': await page.hover(sel, { timeout }); return false\r\n default: throw new Error(`Unsupported action type: ${plan.type}`)\r\n }\r\n } catch (e) {\r\n lastErr = e\r\n }\r\n }\r\n // 2) AI-vision fallback: every selector failed → click the model's\r\n // normalised point. <select> can't be operated by a coordinate, so it's\r\n // selectors-only. Requires page.mouse (Playwright provides it).\r\n if (allowVision && plan.point && plan.type !== 'select' && page.mouse) {\r\n const vp = page.viewportSize?.() || { width: 1280, height: 800 }\r\n const x = Math.round((plan.point.x / 1000) * vp.width)\r\n const y = Math.round((plan.point.y / 1000) * vp.height)\r\n await page.mouse.click(x, y)\r\n if (plan.type === 'fill' && plan.value) await page.keyboard.type(plan.value, { delay: 20 })\r\n // hover via mouse.move when available, else the click above is close enough.\r\n if (plan.type === 'hover' && page.mouse.move) await page.mouse.move(x, y)\r\n return true\r\n }\r\n if (candidates.length === 0) throw new Error(`No selector or vision point returned for action ${plan.type}`)\r\n throw lastErr ?? new Error(`No selector worked for ${plan.type} (and vision fallback unavailable)`)\r\n }\r\n\r\n private zodToJsonSchema<T>(schema: ZodLikeSchema<T>): unknown {\r\n return { type: 'object', _zodHint: String(schema) }\r\n }\r\n}\r\n\r\nfunction bufferToBase64(buf: Buffer | Uint8Array): string {\r\n if (typeof Buffer !== 'undefined' && buf instanceof Buffer) {\r\n return buf.toString('base64')\r\n }\r\n let binary = ''\r\n const bytes = buf as Uint8Array\r\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i])\r\n const g = globalThis as any\r\n return g.btoa ? g.btoa(binary) : Buffer.from(binary, 'binary').toString('base64')\r\n}\r\n"],"mappings":";AAUO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAMzC,YAAY,SAAiB,OAA8E,CAAC,GAAG;AAC7G,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,YAAY,KAAK;AACtB,SAAK,MAAM,KAAK;AAEhB,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,YAAN,MAAM,mBAAkB,gBAAgB;AAAA,EAC7C,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAU,SAAS;AAAA,EACjD;AACF;AAGO,IAAM,aAAN,MAAM,oBAAmB,gBAAgB;AAAA,EAG9C,YAAY,SAAiB,OAAoF,CAAC,GAAG;AACnH,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,SAAK,YAAY,KAAK;AACtB,WAAO,eAAe,MAAM,YAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,gBAAN,MAAM,uBAAsB,gBAAgB;AAAA,EACjD,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,eAAc,SAAS;AAAA,EACrD;AACF;AAGO,IAAM,kBAAN,MAAM,yBAAwB,gBAAgB;AAAA,EACnD,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,iBAAgB,SAAS;AAAA,EACvD;AACF;AAGO,IAAM,iBAAN,MAAM,wBAAuB,gBAAgB;AAAA,EAElD,YAAY,SAAiB,OAA0F,CAAC,GAAG;AACzH,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,SAAK,oBAAoB,KAAK;AAC9B,WAAO,eAAe,MAAM,gBAAe,SAAS;AAAA,EACtD;AACF;AAGO,IAAM,cAAN,MAAM,qBAAoB,gBAAgB;AAAA,EAC/C,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,aAAY,SAAS;AAAA,EACnD;AACF;AAGO,IAAM,eAAN,MAAM,sBAAqB,gBAAgB;AAAA,EAChD,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAa,SAAS;AAAA,EACpD;AACF;AAGO,IAAM,aAAN,MAAM,oBAAmB,gBAAgB;AAAA,EAC9C,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,YAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,eAAN,MAAM,sBAAqB,gBAAgB;AAAA,EAChD,YAAY,SAAiB,OAAyD,CAAC,GAAG;AACxF,UAAM,SAAS,IAAI;AACnB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,cAAa,SAAS;AAAA,EACpD;AACF;;;AClDO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,MAAkB;AAAlB;AAAA,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBhD,MAAM,GAAG,MAAgB,aAAqB,OAAuD,CAAC,GAA6B;AACjI,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,WAAW,MAAM,KAAK,aAAa,IAAI;AAC7C,UAAM,OAAO,MAAM,KAAK,KAAK,QAQ1B,sBAAsB;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM,EAAE,aAAa,SAAS;AAAA,MAC9B,WAAW;AAAA,IACb,CAAC;AACD,UAAM,cAAc,KAAK,mBAAmB;AAC5C,QAAI;AACF,YAAM,qBAAqB,MAAM,KAAK,UAAU,MAAM,MAAM,KAAK,WAAW,MAAQ,WAAW;AAC/F,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf;AAAA,QACA,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,KAAQ,MAAgB,QAAgE;AAC5F,UAAM,WAAW,MAAM,KAAK,aAAa,IAAI;AAC7C,UAAM,aAAa,gBAAgB,SAC9B,OAAmC,aACpC,KAAK,gBAAgB,MAA0B;AACnD,UAAM,MAAM,MAAM,KAAK,KAAK;AAAA,MAC1B;AAAA,MACA,EAAE,QAAQ,QAAQ,MAAM,EAAE,UAAU,WAAW,GAAG,WAAW,IAAO;AAAA,IACtE;AACA,QAAI,WAAY,UAAqB,OAAQ,OAA4B,UAAU,YAAY;AAC7F,UAAI;AACF,eAAQ,OAA4B,MAAM,IAAI,IAAI;AAAA,MACpD,SAAS,GAAG;AACV,cAAM,IAAI;AAAA,UACR,2CAA2C,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,UACrF,EAAE,KAAK,IAAI,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,KAAK,MAAgB,MAA4C;AACrE,UAAM,WAAW,MAAM,KAAK,aAAa,IAAI;AAC7C,UAAM,MAAM,MAAM,KAAK,KAAK;AAAA,MAC1B;AAAA,MACA,EAAE,QAAQ,QAAQ,MAAM,EAAE,UAAU,KAAK,GAAG,WAAW,KAAO;AAAA,IAChE;AACA,WAAO,IAAI,WAAW,CAAC;AAAA,EACzB;AAAA;AAAA,EAIA,MAAc,aAAa,MAA6E;AACtG,UAAM,MAAM,MAAM,KAAK,WAAW,EAAE,MAAM,QAAQ,SAAS,IAAI,UAAU,MAAM,CAAC;AAChF,UAAM,aAAa,eAAe,GAAG;AACrC,WAAO;AAAA,MACL,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,UACZ,MACA,MAQA,SACA,aACkB;AAClB,QAAI,KAAK,SAAS,eAAe,KAAK,OAAO;AAC3C,YAAM,KAAK,SAAS,MAAM,KAAK,KAAK;AACpC,aAAO;AAAA,IACT;AACA,QAAI,KAAK,SAAS,UAAU,KAAK,KAAK;AACpC,YAAM,KAAK,KAAK,KAAK,KAAK,EAAE,QAAQ,CAAC;AACrC,aAAO;AAAA,IACT;AACA,UAAM,aAAa,CAAC,KAAK,UAAU,GAAI,KAAK,wBAAwB,CAAC,CAAE,EAAE;AAAA,MACvE,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS;AAAA,IAC1D;AAEA,QAAI;AACJ,eAAW,OAAO,YAAY;AAC5B,UAAI;AACF,gBAAQ,KAAK,MAAM;AAAA,UACjB,KAAK;AAAS,kBAAM,KAAK,MAAM,KAAK,EAAE,QAAQ,CAAC;AAAG,mBAAO;AAAA,UACzD,KAAK;AAAQ,kBAAM,KAAK,KAAK,KAAK,KAAK,SAAS,IAAI,EAAE,QAAQ,CAAC;AAAG,mBAAO;AAAA,UACzE,KAAK;AAAU,kBAAM,KAAK,aAAa,KAAK,KAAK,SAAS,IAAI,EAAE,QAAQ,CAAC;AAAG,mBAAO;AAAA,UACnF,KAAK;AAAS,kBAAM,KAAK,MAAM,KAAK,EAAE,QAAQ,CAAC;AAAG,mBAAO;AAAA,UACzD;AAAS,kBAAM,IAAI,MAAM,4BAA4B,KAAK,IAAI,EAAE;AAAA,QAClE;AAAA,MACF,SAAS,GAAG;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AAIA,QAAI,eAAe,KAAK,SAAS,KAAK,SAAS,YAAY,KAAK,OAAO;AACrE,YAAM,KAAK,KAAK,eAAe,KAAK,EAAE,OAAO,MAAM,QAAQ,IAAI;AAC/D,YAAM,IAAI,KAAK,MAAO,KAAK,MAAM,IAAI,MAAQ,GAAG,KAAK;AACrD,YAAM,IAAI,KAAK,MAAO,KAAK,MAAM,IAAI,MAAQ,GAAG,MAAM;AACtD,YAAM,KAAK,MAAM,MAAM,GAAG,CAAC;AAC3B,UAAI,KAAK,SAAS,UAAU,KAAK,MAAO,OAAM,KAAK,SAAS,KAAK,KAAK,OAAO,EAAE,OAAO,GAAG,CAAC;AAE1F,UAAI,KAAK,SAAS,WAAW,KAAK,MAAM,KAAM,OAAM,KAAK,MAAM,KAAK,GAAG,CAAC;AACxE,aAAO;AAAA,IACT;AACA,QAAI,WAAW,WAAW,EAAG,OAAM,IAAI,MAAM,mDAAmD,KAAK,IAAI,EAAE;AAC3G,UAAM,WAAW,IAAI,MAAM,0BAA0B,KAAK,IAAI,oCAAoC;AAAA,EACpG;AAAA,EAEQ,gBAAmB,QAAmC;AAC5D,WAAO,EAAE,MAAM,UAAU,UAAU,OAAO,MAAM,EAAE;AAAA,EACpD;AACF;AAEA,SAAS,eAAe,KAAkC;AACxD,MAAI,OAAO,WAAW,eAAe,eAAe,QAAQ;AAC1D,WAAO,IAAI,SAAS,QAAQ;AAAA,EAC9B;AACA,MAAI,SAAS;AACb,QAAM,QAAQ;AACd,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,WAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAC7E,QAAM,IAAI;AACV,SAAO,EAAE,OAAO,EAAE,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,QAAQ,EAAE,SAAS,QAAQ;AAClF;","names":[]}