@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 +21 -0
- package/README.md +336 -0
- package/dist/chunk-ODLNHNQT.mjs +272 -0
- package/dist/chunk-ODLNHNQT.mjs.map +1 -0
- package/dist/index.d.mts +541 -0
- package/dist/index.d.ts +541 -0
- package/dist/index.js +1258 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +975 -0
- package/dist/index.mjs.map +1 -0
- package/dist/page-8LsjwpEo.d.mts +879 -0
- package/dist/page-8LsjwpEo.d.ts +879 -0
- package/dist/page.d.mts +1 -0
- package/dist/page.d.ts +1 -0
- package/dist/page.js +230 -0
- package/dist/page.js.map +1 -0
- package/dist/page.mjs +7 -0
- package/dist/page.mjs.map +1 -0
- package/package.json +86 -0
- package/src/client.ts +286 -0
- package/src/errors.ts +113 -0
- package/src/index.ts +140 -0
- package/src/models.ts +63 -0
- package/src/page.ts +264 -0
- package/src/resources/apiKeys.ts +53 -0
- package/src/resources/autopilot.ts +385 -0
- package/src/resources/cloudBrowser.ts +197 -0
- package/src/resources/connectHub.ts +93 -0
- package/src/resources/customNodes.ts +62 -0
- package/src/resources/executions.ts +84 -0
- package/src/resources/profiles.ts +48 -0
- package/src/resources/recordings.ts +32 -0
- package/src/resources/schedules.ts +49 -0
- package/src/resources/subscription.ts +27 -0
- package/src/resources/templates.ts +46 -0
- package/src/resources/workflows.ts +84 -0
- package/src/types.ts +680 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthError,
|
|
3
|
+
CopilotHelpers,
|
|
4
|
+
NetworkError,
|
|
5
|
+
NotFoundError,
|
|
6
|
+
ParseError,
|
|
7
|
+
PrompteryxError,
|
|
8
|
+
QuotaError,
|
|
9
|
+
RateLimitError,
|
|
10
|
+
ServerError,
|
|
11
|
+
TimeoutError,
|
|
12
|
+
ValidationError
|
|
13
|
+
} from "./chunk-ODLNHNQT.mjs";
|
|
14
|
+
|
|
15
|
+
// src/client.ts
|
|
16
|
+
var DEFAULT_BASE_URL = "https://prompteryx.com";
|
|
17
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
18
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
19
|
+
var HttpClient = class {
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
if (!opts.apiKey) {
|
|
22
|
+
throw new PrompteryxError("apiKey is required to construct a Prompteryx client");
|
|
23
|
+
}
|
|
24
|
+
this.apiKey = opts.apiKey;
|
|
25
|
+
this.cloudBrowserKey = opts.cloudBrowserKey;
|
|
26
|
+
this.baseUrl = (opts.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
27
|
+
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
28
|
+
this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
29
|
+
this.defaultHeaders = opts.defaultHeaders ?? {};
|
|
30
|
+
const f = opts.fetch ?? globalThis.fetch;
|
|
31
|
+
if (!f) {
|
|
32
|
+
throw new PrompteryxError(
|
|
33
|
+
"No fetch implementation found. Provide options.fetch or run on a runtime with global fetch (Node 18+, browser, Cloudflare Workers)."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
this.fetchImpl = f.bind(globalThis);
|
|
37
|
+
}
|
|
38
|
+
/** Returns the JSON-parsed body typed as T. Throws typed errors on non-2xx. */
|
|
39
|
+
async request(path, opts = {}) {
|
|
40
|
+
const res = await this.rawRequest(path, opts);
|
|
41
|
+
const text = await res.text();
|
|
42
|
+
if (!text) return void 0;
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(text);
|
|
45
|
+
} catch {
|
|
46
|
+
throw new ParseError(`Failed to parse JSON response from ${path}`, {
|
|
47
|
+
status: res.status,
|
|
48
|
+
raw: text.slice(0, 500)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Returns the raw Response. Throws typed errors on non-2xx, but does
|
|
53
|
+
* not attempt to read the body. Useful for downloading binaries. */
|
|
54
|
+
async rawRequest(path, opts = {}) {
|
|
55
|
+
const method = opts.method ?? "GET";
|
|
56
|
+
const url = this.buildUrl(path, opts.query);
|
|
57
|
+
const shouldRetry = opts.retry ?? method === "GET";
|
|
58
|
+
const maxAttempts = shouldRetry ? this.maxRetries + 1 : 1;
|
|
59
|
+
let lastErr;
|
|
60
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
61
|
+
try {
|
|
62
|
+
const res = await this.sendOnce(url, method, opts);
|
|
63
|
+
if (res.ok) return res;
|
|
64
|
+
const err = await this.errorFromResponse(res);
|
|
65
|
+
if (this.isRetryable(err) && attempt < maxAttempts - 1) {
|
|
66
|
+
await this.backoff(attempt, err);
|
|
67
|
+
lastErr = err;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
throw err;
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (e instanceof PrompteryxError) throw e;
|
|
73
|
+
const wrapped = this.wrapTransport(e);
|
|
74
|
+
if (this.isRetryable(wrapped) && attempt < maxAttempts - 1) {
|
|
75
|
+
await this.backoff(attempt, wrapped);
|
|
76
|
+
lastErr = wrapped;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
throw wrapped;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw lastErr ?? new PrompteryxError("Unknown error after retries exhausted");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stream a Server-Sent Events response line-by-line as JSON-parsed
|
|
86
|
+
* payloads. Yields each event's `data:` payload parsed as JSON. The
|
|
87
|
+
* caller is responsible for breaking the loop / cancelling the
|
|
88
|
+
* AbortSignal when done.
|
|
89
|
+
*/
|
|
90
|
+
async *streamSse(path, opts = {}) {
|
|
91
|
+
const res = await this.rawRequest(path, {
|
|
92
|
+
...opts,
|
|
93
|
+
headers: { ...opts.headers, Accept: "text/event-stream" }
|
|
94
|
+
});
|
|
95
|
+
if (!res.body) return;
|
|
96
|
+
const reader = res.body.getReader();
|
|
97
|
+
const decoder = new TextDecoder();
|
|
98
|
+
let buf = "";
|
|
99
|
+
while (true) {
|
|
100
|
+
const { value, done } = await reader.read();
|
|
101
|
+
if (done) break;
|
|
102
|
+
buf += decoder.decode(value, { stream: true });
|
|
103
|
+
let idx;
|
|
104
|
+
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
105
|
+
const raw = buf.slice(0, idx);
|
|
106
|
+
buf = buf.slice(idx + 2);
|
|
107
|
+
const dataLines = raw.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
|
|
108
|
+
if (dataLines.length === 0) continue;
|
|
109
|
+
const payload = dataLines.join("\n");
|
|
110
|
+
if (!payload) continue;
|
|
111
|
+
try {
|
|
112
|
+
yield JSON.parse(payload);
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ── internals ────────────────────────────────────────────────────────
|
|
119
|
+
async sendOnce(url, method, opts) {
|
|
120
|
+
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
121
|
+
const ctl = new AbortController();
|
|
122
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
123
|
+
if (opts.signal) {
|
|
124
|
+
if (opts.signal.aborted) ctl.abort();
|
|
125
|
+
else opts.signal.addEventListener("abort", () => ctl.abort(), { once: true });
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const headers = {
|
|
129
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
130
|
+
Accept: "application/json",
|
|
131
|
+
...this.defaultHeaders,
|
|
132
|
+
...opts.headers ?? {}
|
|
133
|
+
};
|
|
134
|
+
let body;
|
|
135
|
+
if (opts.body !== void 0) {
|
|
136
|
+
headers["Content-Type"] = "application/json";
|
|
137
|
+
body = JSON.stringify(opts.body);
|
|
138
|
+
}
|
|
139
|
+
return await this.fetchImpl(url, {
|
|
140
|
+
method,
|
|
141
|
+
headers,
|
|
142
|
+
body,
|
|
143
|
+
signal: ctl.signal
|
|
144
|
+
});
|
|
145
|
+
} finally {
|
|
146
|
+
clearTimeout(t);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
buildUrl(path, query) {
|
|
150
|
+
const base = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
151
|
+
if (!query) return base;
|
|
152
|
+
const params = new URLSearchParams();
|
|
153
|
+
for (const [k, v] of Object.entries(query)) {
|
|
154
|
+
if (v === void 0 || v === null) continue;
|
|
155
|
+
params.set(k, String(v));
|
|
156
|
+
}
|
|
157
|
+
const qs = params.toString();
|
|
158
|
+
return qs ? `${base}${base.includes("?") ? "&" : "?"}${qs}` : base;
|
|
159
|
+
}
|
|
160
|
+
async errorFromResponse(res) {
|
|
161
|
+
let parsed = null;
|
|
162
|
+
try {
|
|
163
|
+
const text = await res.text();
|
|
164
|
+
parsed = text ? JSON.parse(text) : null;
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
const message = parsed?.error?.message ?? parsed?.message ?? parsed?.error ?? `Request failed with ${res.status}`;
|
|
168
|
+
const code = parsed?.error?.code ?? parsed?.code;
|
|
169
|
+
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
170
|
+
const opts = { status: res.status, code, requestId, raw: parsed };
|
|
171
|
+
switch (res.status) {
|
|
172
|
+
case 401:
|
|
173
|
+
case 403:
|
|
174
|
+
return new AuthError(message, opts);
|
|
175
|
+
case 402:
|
|
176
|
+
return new QuotaError(message, { ...opts, resources: parsed?.error?.resources ?? parsed?.resources });
|
|
177
|
+
case 404:
|
|
178
|
+
return new NotFoundError(message, opts);
|
|
179
|
+
case 422:
|
|
180
|
+
return new ValidationError(message, opts);
|
|
181
|
+
case 429: {
|
|
182
|
+
const retryAfter = parseInt(res.headers.get("retry-after") ?? "0", 10);
|
|
183
|
+
return new RateLimitError(message, { ...opts, retryAfterSeconds: Number.isFinite(retryAfter) ? retryAfter : void 0 });
|
|
184
|
+
}
|
|
185
|
+
default:
|
|
186
|
+
if (res.status >= 500) return new ServerError(message, opts);
|
|
187
|
+
return new PrompteryxError(message, opts);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
wrapTransport(e) {
|
|
191
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
192
|
+
if (e instanceof Error && (e.name === "AbortError" || msg.includes("aborted"))) {
|
|
193
|
+
return new TimeoutError(`Request timed out: ${msg}`);
|
|
194
|
+
}
|
|
195
|
+
return new NetworkError(`Network error: ${msg}`);
|
|
196
|
+
}
|
|
197
|
+
isRetryable(err) {
|
|
198
|
+
if (err instanceof ServerError) return true;
|
|
199
|
+
if (err instanceof RateLimitError) return true;
|
|
200
|
+
if (err instanceof NetworkError) return true;
|
|
201
|
+
if (err instanceof TimeoutError) return true;
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
async backoff(attempt, err) {
|
|
205
|
+
const retryAfter = err.retryAfterSeconds;
|
|
206
|
+
if (retryAfter && retryAfter > 0) {
|
|
207
|
+
return new Promise((r) => setTimeout(r, Math.min(retryAfter, 30) * 1e3));
|
|
208
|
+
}
|
|
209
|
+
const base = 250 * Math.pow(2, attempt);
|
|
210
|
+
const jitter = Math.random() * 100;
|
|
211
|
+
return new Promise((r) => setTimeout(r, base + jitter));
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/resources/autopilot.ts
|
|
216
|
+
var AutopilotResource = class {
|
|
217
|
+
constructor(http) {
|
|
218
|
+
this.http = http;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Run the autopilot. Blocks until the task finishes (success, step
|
|
222
|
+
* limit, or error). Returns the trace plus optionally the saved
|
|
223
|
+
* workflow id.
|
|
224
|
+
*
|
|
225
|
+
* ```ts
|
|
226
|
+
* const result = await px.autopilot.run({
|
|
227
|
+
* goal: 'Apply for the Senior Engineer role at OpenAI',
|
|
228
|
+
* startUrl: 'https://openai.com/careers',
|
|
229
|
+
* maxSteps: 40,
|
|
230
|
+
* saveAsWorkflow: true, // Replay-forever, zero AI cost
|
|
231
|
+
* session: { useProxy: true, useCaptcha: true },
|
|
232
|
+
* })
|
|
233
|
+
* if (result.savedWorkflowId) {
|
|
234
|
+
* console.log('Saved as workflow:', result.savedWorkflowId)
|
|
235
|
+
* // Run it later for free:
|
|
236
|
+
* await px.workflows.run(result.savedWorkflowId)
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
async run(opts) {
|
|
241
|
+
if (opts.target === "local") {
|
|
242
|
+
return this.runLocal(opts);
|
|
243
|
+
}
|
|
244
|
+
if (opts.agents && opts.agents > 1) {
|
|
245
|
+
return this.runSwarm(opts);
|
|
246
|
+
}
|
|
247
|
+
return this.runAsync(opts);
|
|
248
|
+
}
|
|
249
|
+
/** The task params shared by the sync, swarm, and async request bodies. */
|
|
250
|
+
cloudTaskBody(opts) {
|
|
251
|
+
return {
|
|
252
|
+
instruction: opts.goal,
|
|
253
|
+
startUrl: opts.startUrl,
|
|
254
|
+
maxSteps: opts.maxSteps,
|
|
255
|
+
maxCredits: opts.maxCredits,
|
|
256
|
+
model: opts.model,
|
|
257
|
+
aiVision: opts.aiVision,
|
|
258
|
+
finalStepVision: opts.finalStepVision,
|
|
259
|
+
outputSchema: opts.outputSchema,
|
|
260
|
+
costSaving: opts.costSaving,
|
|
261
|
+
costSavingMaxBatch: opts.costSavingMaxBatch,
|
|
262
|
+
carefulBatching: opts.carefulBatching,
|
|
263
|
+
saveAsWorkflow: opts.saveAsWorkflow,
|
|
264
|
+
savedWorkflowName: opts.savedWorkflowName,
|
|
265
|
+
sessionId: opts.sessionId,
|
|
266
|
+
systemPromptOverride: opts.systemPromptOverride,
|
|
267
|
+
allowedTools: opts.allowedTools,
|
|
268
|
+
viewport: opts.viewport,
|
|
269
|
+
session: opts.session,
|
|
270
|
+
// Settings parity (2026-07-15) — mirror the AI Browser Agent settings dialog.
|
|
271
|
+
safetyConsent: opts.safetyConsent,
|
|
272
|
+
confirmUnclear: opts.confirmUnclear,
|
|
273
|
+
enableContextCompression: opts.enableContextCompression,
|
|
274
|
+
compressionThreshold: opts.compressionThreshold,
|
|
275
|
+
enableSessionReset: opts.enableSessionReset,
|
|
276
|
+
sessionResetThreshold: opts.sessionResetThreshold,
|
|
277
|
+
...opts.passthrough
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/** Multi-agent swarm — one synchronous request; the server merges all lanes. */
|
|
281
|
+
async runSwarm(opts) {
|
|
282
|
+
const env = await this.http.request("/api/v1/ai-browser/execute", {
|
|
283
|
+
method: "POST",
|
|
284
|
+
timeoutMs: opts.timeoutMs ?? 10 * 6e4,
|
|
285
|
+
body: {
|
|
286
|
+
...this.cloudTaskBody(opts),
|
|
287
|
+
agents: opts.agents,
|
|
288
|
+
collaborate: opts.collaborate,
|
|
289
|
+
attachmentContext: opts.attachmentContext,
|
|
290
|
+
maxRunCredits: opts.maxRunCredits
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
return this.mapResult(env?.data ?? env);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Async single-agent run: POST { mode:'start' } to set up the job (returns a
|
|
297
|
+
* jobId immediately), then POST { mode:'run' } in a loop — each advances the job
|
|
298
|
+
* for up to ~230s server-side and returns the current status — until the job is
|
|
299
|
+
* terminal or `opts.timeoutMs` elapses. No single request is long, so the gateway
|
|
300
|
+
* timeout is never hit. Same `AutopilotRunResult` shape as before.
|
|
301
|
+
*/
|
|
302
|
+
async runAsync(opts) {
|
|
303
|
+
const timeoutMs = opts.timeoutMs ?? 10 * 6e4;
|
|
304
|
+
const deadline = Date.now() + timeoutMs;
|
|
305
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
306
|
+
const startEnv = await this.http.request("/api/v1/ai-browser/execute", {
|
|
307
|
+
method: "POST",
|
|
308
|
+
timeoutMs: 6e4,
|
|
309
|
+
body: { ...this.cloudTaskBody(opts), mode: "start" }
|
|
310
|
+
});
|
|
311
|
+
const jobId = startEnv?.data?.jobId;
|
|
312
|
+
if (!jobId) {
|
|
313
|
+
return this.mapResult(startEnv?.data ?? startEnv);
|
|
314
|
+
}
|
|
315
|
+
let view = null;
|
|
316
|
+
while (Date.now() < deadline) {
|
|
317
|
+
const runEnv = await this.http.request("/api/v1/ai-browser/execute", {
|
|
318
|
+
method: "POST",
|
|
319
|
+
timeoutMs: 25e4,
|
|
320
|
+
retry: true,
|
|
321
|
+
body: { jobId, mode: "run", maxSteps: opts.maxSteps }
|
|
322
|
+
});
|
|
323
|
+
view = runEnv?.data ?? null;
|
|
324
|
+
if (!view || view.terminal || view.status !== "running") break;
|
|
325
|
+
await sleep(500);
|
|
326
|
+
}
|
|
327
|
+
if (view && view.status === "running") {
|
|
328
|
+
try {
|
|
329
|
+
const statusEnv = await this.http.request("/api/v1/ai-browser/execute", {
|
|
330
|
+
query: { jobId },
|
|
331
|
+
timeoutMs: 3e4
|
|
332
|
+
});
|
|
333
|
+
if (statusEnv?.data) view = statusEnv.data;
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return this.mapResult(view);
|
|
338
|
+
}
|
|
339
|
+
/** Map an execute-endpoint payload (sync result, swarm result, or async status
|
|
340
|
+
* view — they share finalAnswer / steps / usage / savedWorkflowId / status) into
|
|
341
|
+
* the public AutopilotRunResult shape. */
|
|
342
|
+
mapResult(data) {
|
|
343
|
+
const d = data || {};
|
|
344
|
+
const status = d.status;
|
|
345
|
+
const success = status === "completed" || status === "max_steps_reached";
|
|
346
|
+
const steps = Array.isArray(d.steps) ? d.steps.map((s, i) => ({
|
|
347
|
+
step: i + 1,
|
|
348
|
+
action: typeof s?.action === "string" ? s.action : s?.action?.name || "action",
|
|
349
|
+
result: s?.details ?? s?.result
|
|
350
|
+
})) : [];
|
|
351
|
+
return {
|
|
352
|
+
success,
|
|
353
|
+
finalAnswer: d.finalAnswer,
|
|
354
|
+
steps,
|
|
355
|
+
savedWorkflowId: d.savedWorkflowId,
|
|
356
|
+
usage: d.usage,
|
|
357
|
+
swarm: d.swarm
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Local Chrome autopilot — talks DIRECTLY to the Prompteryx desktop app on
|
|
362
|
+
* this machine (localhost:61337): opens your local Chrome, runs the agent
|
|
363
|
+
* loop there, polls until done. Same options as `run` (model, aiVision
|
|
364
|
+
* preset, maxSteps, maxCredits, costSaving). Credits still apply; cloud
|
|
365
|
+
* minutes do not. The tab stays open after the run for follow-ups.
|
|
366
|
+
*/
|
|
367
|
+
async runLocal(opts) {
|
|
368
|
+
const fetchImpl = globalThis.fetch;
|
|
369
|
+
if (!fetchImpl) {
|
|
370
|
+
throw new Error("[Prompteryx SDK] Local target needs a global fetch (Node 18+ / browser).");
|
|
371
|
+
}
|
|
372
|
+
const base = (opts.runnerUrl || "http://localhost:61337").replace(/\/$/, "");
|
|
373
|
+
const sessionId = `sdk-local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
374
|
+
const goal = opts.startUrl ? `First, go to ${opts.startUrl}. Then: ${opts.goal}` : opts.goal;
|
|
375
|
+
const post = async (path, body) => {
|
|
376
|
+
let r;
|
|
377
|
+
try {
|
|
378
|
+
r = await fetchImpl(`${base}${path}`, {
|
|
379
|
+
method: "POST",
|
|
380
|
+
headers: { "Content-Type": "application/json" },
|
|
381
|
+
body: JSON.stringify(body)
|
|
382
|
+
});
|
|
383
|
+
} catch (e) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
`[Prompteryx SDK] Couldn't reach the desktop app at ${base}. Make sure the Prompteryx desktop app is running and signed in on this machine. (${e.message})`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
if (!r.ok) {
|
|
389
|
+
let msg = `${path} failed (${r.status})`;
|
|
390
|
+
try {
|
|
391
|
+
const j = await r.json();
|
|
392
|
+
if (j?.error) msg = j.error;
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
throw new Error(`[Prompteryx SDK] ${msg}`);
|
|
396
|
+
}
|
|
397
|
+
return r.json().catch(() => ({}));
|
|
398
|
+
};
|
|
399
|
+
await post("/launch-gemini-browser", {
|
|
400
|
+
sessionId,
|
|
401
|
+
profileId: opts.session?.profileId,
|
|
402
|
+
enableRecording: false
|
|
403
|
+
});
|
|
404
|
+
await post("/local-agent/start", {
|
|
405
|
+
sessionId,
|
|
406
|
+
task: goal,
|
|
407
|
+
model: opts.model,
|
|
408
|
+
aiVision: opts.aiVision,
|
|
409
|
+
// preset slug — resolved app-side
|
|
410
|
+
maxTurns: opts.maxSteps,
|
|
411
|
+
maxCredits: opts.maxCredits,
|
|
412
|
+
costSaving: opts.passthrough?.costSaving === true,
|
|
413
|
+
costSavingMaxBatch: opts.passthrough?.costSavingMaxBatch
|
|
414
|
+
});
|
|
415
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 10 * 6e4);
|
|
416
|
+
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
417
|
+
let state = null;
|
|
418
|
+
while (Date.now() < deadline) {
|
|
419
|
+
await sleep(1200);
|
|
420
|
+
const r = await fetchImpl(`${base}/local-agent/state/${encodeURIComponent(sessionId)}`).catch(() => null);
|
|
421
|
+
if (!r || !r.ok) continue;
|
|
422
|
+
state = await r.json().catch(() => null);
|
|
423
|
+
if (!state || state.found === false) continue;
|
|
424
|
+
if (state.status && state.status !== "running") break;
|
|
425
|
+
}
|
|
426
|
+
const status = state?.status;
|
|
427
|
+
return {
|
|
428
|
+
success: status === "done" || status === "awaiting_input",
|
|
429
|
+
finalAnswer: state?.answer,
|
|
430
|
+
steps: (state?.steps || []).map((s, i) => ({
|
|
431
|
+
step: i + 1,
|
|
432
|
+
action: s?.name || s?.action?.name || "action",
|
|
433
|
+
result: s?.args || s?.action?.args
|
|
434
|
+
})),
|
|
435
|
+
usage: {
|
|
436
|
+
aiCredits: Math.max(0, Math.round((state?.costUSD || 0) / 0.01)),
|
|
437
|
+
tokensIn: state?.inTokens || 0,
|
|
438
|
+
tokensOut: state?.outTokens || 0,
|
|
439
|
+
costUSD: state?.costUSD || 0,
|
|
440
|
+
turns: state?.turns || 0
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Ask a running async job to stop at its next step boundary
|
|
446
|
+
* (`POST { jobId, mode: 'stop' }`). Use it to wind down a job you
|
|
447
|
+
* started via the raw API (or a run you're abandoning) instead of
|
|
448
|
+
* leaving it stepping against a dead browser session until the step
|
|
449
|
+
* cap — an abandoned job burns a model call + timeout per step.
|
|
450
|
+
*
|
|
451
|
+
* ```ts
|
|
452
|
+
* const { stopRequested, status } = await px.autopilot.stop(jobId)
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
async stop(jobId) {
|
|
456
|
+
const env = await this.http.request("/api/v1/ai-browser/execute", {
|
|
457
|
+
method: "POST",
|
|
458
|
+
timeoutMs: 3e4,
|
|
459
|
+
body: { jobId, mode: "stop" }
|
|
460
|
+
});
|
|
461
|
+
const d = env?.data ?? env ?? {};
|
|
462
|
+
return { jobId: d.jobId ?? jobId, stopRequested: d.stopRequested === true, status: d.status };
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Run the autopilot over the keep-alive stream endpoint and yield the
|
|
466
|
+
* step trace. Ends with a `{ step: -1, action: 'done' }` sentinel whose
|
|
467
|
+
* `result` field carries the full `AutopilotRunResult`.
|
|
468
|
+
*
|
|
469
|
+
* ```ts
|
|
470
|
+
* for await (const step of px.autopilot.stream({ goal: 'Buy a ticket' })) {
|
|
471
|
+
* console.log('Step', step.step, '→', step.action)
|
|
472
|
+
* if (step.action === 'done') break
|
|
473
|
+
* }
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* PROTOCOL (matches /api/v1/ai-browser/execute-stream — it is NOT SSE):
|
|
477
|
+
* the server emits a 1-space heartbeat every 15s while the run executes,
|
|
478
|
+
* then the complete execute-route JSON as the final chunk, i.e. the body
|
|
479
|
+
* is `<heartbeats>\n<json>`. The heartbeats exist to defeat the ~300s
|
|
480
|
+
* infra idle timeout on long synchronous runs; per-step live events are
|
|
481
|
+
* not available on this route, so steps arrive together when the run
|
|
482
|
+
* finishes. Prefer `run()` unless you specifically want the keep-alive
|
|
483
|
+
* transport for a long single-request run.
|
|
484
|
+
*/
|
|
485
|
+
async *stream(opts) {
|
|
486
|
+
if (opts.target === "local") {
|
|
487
|
+
throw new Error(
|
|
488
|
+
'[Prompteryx SDK] Streaming is cloud-only. For local Chrome use autopilot.run({ target: "local" }) \u2014 its result contains all steps.'
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
const res = await this.http.rawRequest("/api/v1/ai-browser/execute-stream", {
|
|
492
|
+
method: "POST",
|
|
493
|
+
timeoutMs: opts.timeoutMs ?? 15 * 6e4,
|
|
494
|
+
body: this.cloudTaskBody(opts),
|
|
495
|
+
signal: opts.signal
|
|
496
|
+
});
|
|
497
|
+
let text = "";
|
|
498
|
+
if (res.body) {
|
|
499
|
+
const reader = res.body.getReader();
|
|
500
|
+
const decoder = new TextDecoder();
|
|
501
|
+
while (true) {
|
|
502
|
+
const { value, done } = await reader.read();
|
|
503
|
+
if (done) break;
|
|
504
|
+
if (value) text += decoder.decode(value, { stream: true });
|
|
505
|
+
}
|
|
506
|
+
text += decoder.decode();
|
|
507
|
+
} else {
|
|
508
|
+
text = await res.text();
|
|
509
|
+
}
|
|
510
|
+
const payload = text.trim();
|
|
511
|
+
let parsed;
|
|
512
|
+
try {
|
|
513
|
+
parsed = JSON.parse(payload);
|
|
514
|
+
} catch {
|
|
515
|
+
throw new ParseError("execute-stream returned a non-JSON payload", {
|
|
516
|
+
raw: payload.slice(0, 500)
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (parsed && parsed.success === false) {
|
|
520
|
+
const msg = parsed?.error?.message ?? parsed?.error ?? "AI Browser Agent run failed";
|
|
521
|
+
throw new PrompteryxError(String(msg), { code: parsed?.error?.code, raw: parsed });
|
|
522
|
+
}
|
|
523
|
+
const result = this.mapResult(parsed?.data ?? parsed);
|
|
524
|
+
for (const step of result.steps) yield step;
|
|
525
|
+
yield { step: -1, action: "done", result };
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// src/resources/cloudBrowser.ts
|
|
530
|
+
var MISSING_CB_KEY_MESSAGE = "px.cloudBrowser.* uses a Cloud Browser API key (pcb_live_\u2026), which is a separate key family from the platform px_live_\u2026 key. Create one under Cloud Platform \u2192 API Keys and pass it as new Prompteryx({ apiKey, cloudBrowserKey }).";
|
|
531
|
+
var SessionsResource = class {
|
|
532
|
+
constructor(http) {
|
|
533
|
+
this.http = http;
|
|
534
|
+
}
|
|
535
|
+
/** Headers for the pcb_live_ key family (throws a clear error if absent). */
|
|
536
|
+
cbAuth() {
|
|
537
|
+
const key = this.http.cloudBrowserKey;
|
|
538
|
+
if (!key) throw new AuthError(MISSING_CB_KEY_MESSAGE);
|
|
539
|
+
return { "x-api-key": key };
|
|
540
|
+
}
|
|
541
|
+
cbRequest(path, opts = {}) {
|
|
542
|
+
return this.http.request(path, {
|
|
543
|
+
...opts,
|
|
544
|
+
headers: { ...this.cbAuth(), ...opts.headers ?? {} }
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/** Create a new browser session.
|
|
548
|
+
*
|
|
549
|
+
* Cloud (default):
|
|
550
|
+
* ```ts
|
|
551
|
+
* const s = await px.cloudBrowser.sessions.create()
|
|
552
|
+
* // s.connectUrl → Prompteryx Cloud CDP. Bills cloud-browser minutes.
|
|
553
|
+
* ```
|
|
554
|
+
*
|
|
555
|
+
* Local — uses YOUR machine's Chrome via the Prompteryx plugin +
|
|
556
|
+
* Electron runner. ZERO cloud-browser minutes. Requires the plugin
|
|
557
|
+
* to be running on the same machine as the SDK consumer; the call
|
|
558
|
+
* short-circuits to localhost and never reaches the API.
|
|
559
|
+
* ```ts
|
|
560
|
+
* const s = await px.cloudBrowser.sessions.create({ target: 'local' })
|
|
561
|
+
* // s.connectUrl → http://localhost:9222 (your Chrome's debug port)
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* When `target: 'local'` is set, cloud-only fields (recordSession,
|
|
565
|
+
* proxy, profileId) are ignored — you're driving your own Chrome
|
|
566
|
+
* with whatever cookies/extensions you've already installed.
|
|
567
|
+
*/
|
|
568
|
+
async create(opts = {}) {
|
|
569
|
+
if (opts.target === "local") {
|
|
570
|
+
const localCdp = "http://127.0.0.1:9222";
|
|
571
|
+
const localId = `local_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
572
|
+
return {
|
|
573
|
+
id: localId,
|
|
574
|
+
connectUrl: localCdp,
|
|
575
|
+
status: "active",
|
|
576
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
577
|
+
recordSession: false
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
return this.cbRequest("/api/v1/cloud-browser/sessions", {
|
|
581
|
+
method: "POST",
|
|
582
|
+
body: {
|
|
583
|
+
recordSession: opts.recordSession,
|
|
584
|
+
captureDownloads: opts.captureDownloads,
|
|
585
|
+
extensions: opts.extensions,
|
|
586
|
+
proxy: opts.useProxy,
|
|
587
|
+
country: opts.proxyLocation,
|
|
588
|
+
sessionTimeoutMinutes: opts.sessionTimeoutMinutes,
|
|
589
|
+
profileId: opts.profileId,
|
|
590
|
+
persistContext: opts.persistContext,
|
|
591
|
+
viewport: opts.viewport,
|
|
592
|
+
...opts.passthrough || {}
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
/** Retrieve a session's history record (status/duration/recording flag).
|
|
597
|
+
* Note: this is durable history, not a live handle — it has no
|
|
598
|
+
* `connectUrl`. Keep the `create()` response for connecting. */
|
|
599
|
+
async get(sessionId) {
|
|
600
|
+
return this.cbRequest(
|
|
601
|
+
`/api/v1/cloud-browser/sessions/${encodeURIComponent(sessionId)}`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
/** List recent sessions for your account, newest first. */
|
|
605
|
+
async list(opts = {}) {
|
|
606
|
+
const res = await this.cbRequest(
|
|
607
|
+
"/api/v1/cloud-browser/sessions",
|
|
608
|
+
{ query: { limit: opts.limit } }
|
|
609
|
+
);
|
|
610
|
+
return res.sessions ?? [];
|
|
611
|
+
}
|
|
612
|
+
/** Close a session, finalising the recording (if any) + releasing the
|
|
613
|
+
* cloud-browser slot. Idempotent. No-op for local sessions
|
|
614
|
+
* (target: 'local') — those don't have a slot to release. */
|
|
615
|
+
async close(sessionId) {
|
|
616
|
+
if (sessionId.startsWith("local_")) return { ok: true };
|
|
617
|
+
return this.cbRequest(
|
|
618
|
+
`/api/v1/cloud-browser/sessions/${encodeURIComponent(sessionId)}`,
|
|
619
|
+
{ method: "DELETE" }
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
var CloudBrowserResource = class {
|
|
624
|
+
constructor(http) {
|
|
625
|
+
this.http = http;
|
|
626
|
+
this.sessions = new SessionsResource(http);
|
|
627
|
+
}
|
|
628
|
+
cbAuth() {
|
|
629
|
+
const key = this.http.cloudBrowserKey;
|
|
630
|
+
if (!key) throw new AuthError(MISSING_CB_KEY_MESSAGE);
|
|
631
|
+
return { "x-api-key": key };
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* One-shot fetch through the cloud browser. Spins up a short-lived
|
|
635
|
+
* session, loads the page in real Chromium (so JS-rendered sites work),
|
|
636
|
+
* extracts the content, and tears down. Use this when you only need ONE
|
|
637
|
+
* page and don't want to manage Playwright yourself.
|
|
638
|
+
*
|
|
639
|
+
* ```ts
|
|
640
|
+
* const page = await px.cloudBrowser.fetch({
|
|
641
|
+
* url: 'https://example.com/pricing',
|
|
642
|
+
* format: 'markdown', // 'text' (default) | 'markdown' | 'html' | 'links'
|
|
643
|
+
* waitForSelector: '.pricing-table', // for JS-rendered content
|
|
644
|
+
* selectors: ['.pricing-table .plan'], // deterministic CSS extraction
|
|
645
|
+
* })
|
|
646
|
+
* // page.content, page.extracted, page.title, page.finalUrl …
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
async fetch(opts) {
|
|
650
|
+
return this.http.request("/api/v1/cloud-browser/fetch", {
|
|
651
|
+
method: "POST",
|
|
652
|
+
body: opts,
|
|
653
|
+
headers: this.cbAuth(),
|
|
654
|
+
timeoutMs: Math.max(9e4, (opts.timeoutMs ?? 3e4) + 3e4)
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Search the web through the cloud browser and get structured results
|
|
659
|
+
* (title/url/snippet). Runs the query against DuckDuckGo's server-rendered
|
|
660
|
+
* HTML endpoint in a real browser — there is no engine choice today.
|
|
661
|
+
*/
|
|
662
|
+
async search(opts) {
|
|
663
|
+
const res = await this.http.request(
|
|
664
|
+
"/api/v1/cloud-browser/search",
|
|
665
|
+
{ method: "POST", body: opts, headers: this.cbAuth(), timeoutMs: 9e4 }
|
|
666
|
+
);
|
|
667
|
+
return res.results ?? [];
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
// src/resources/executions.ts
|
|
672
|
+
var TERMINAL_STATES = [
|
|
673
|
+
"completed",
|
|
674
|
+
"failed",
|
|
675
|
+
"cancelled",
|
|
676
|
+
"timed_out"
|
|
677
|
+
];
|
|
678
|
+
var ExecutionsResource = class {
|
|
679
|
+
constructor(http) {
|
|
680
|
+
this.http = http;
|
|
681
|
+
}
|
|
682
|
+
/** Get current execution record. */
|
|
683
|
+
async get(executionId) {
|
|
684
|
+
return this.http.request(`/api/v1/executions/${encodeURIComponent(executionId)}`);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Block until the execution reaches a terminal state. Polls every
|
|
688
|
+
* `pollIntervalMs` (default 2s) until it's `completed`/`failed`/
|
|
689
|
+
* `cancelled`/`timed_out`, OR until `timeoutMs` elapses (default 5min).
|
|
690
|
+
*
|
|
691
|
+
* Throws `TimeoutError` on timeout; otherwise returns the final record.
|
|
692
|
+
*/
|
|
693
|
+
async wait(executionId, opts = {}) {
|
|
694
|
+
const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
|
|
695
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 2e3;
|
|
696
|
+
const deadline = Date.now() + timeoutMs;
|
|
697
|
+
while (true) {
|
|
698
|
+
if (opts.signal?.aborted) throw new TimeoutError(`Wait cancelled for ${executionId}`);
|
|
699
|
+
const exec = await this.get(executionId);
|
|
700
|
+
if (TERMINAL_STATES.includes(exec.status)) return exec;
|
|
701
|
+
if (Date.now() > deadline) {
|
|
702
|
+
throw new TimeoutError(`Execution ${executionId} did not finish within ${timeoutMs}ms`);
|
|
703
|
+
}
|
|
704
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Get the logs for a finished (or in-progress) execution as a one-shot
|
|
709
|
+
* fetch. For real-time streaming use `stream(executionId)` instead.
|
|
710
|
+
*/
|
|
711
|
+
async logs(executionId) {
|
|
712
|
+
const res = await this.http.request(
|
|
713
|
+
`/api/v1/executions/${encodeURIComponent(executionId)}/logs`
|
|
714
|
+
);
|
|
715
|
+
return res.logs ?? [];
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Stream log events as they arrive. Async iterable:
|
|
719
|
+
*
|
|
720
|
+
* for await (const ev of px.executions.stream(execId)) {
|
|
721
|
+
* console.log(ev.message)
|
|
722
|
+
* if (ev.type === 'done') break
|
|
723
|
+
* }
|
|
724
|
+
*/
|
|
725
|
+
async *stream(executionId, opts = {}) {
|
|
726
|
+
yield* this.http.streamSse(
|
|
727
|
+
`/api/v1/executions/${encodeURIComponent(executionId)}/logs/stream`,
|
|
728
|
+
{ signal: opts.signal }
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
/** Get a single node's output from a finished execution. */
|
|
732
|
+
async getNodeOutput(executionId, nodeId) {
|
|
733
|
+
return this.http.request(
|
|
734
|
+
`/api/v1/executions/${encodeURIComponent(executionId)}/nodes/${encodeURIComponent(nodeId)}/output`
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// src/resources/profiles.ts
|
|
740
|
+
var ProfilesResource = class {
|
|
741
|
+
constructor(http) {
|
|
742
|
+
this.http = http;
|
|
743
|
+
}
|
|
744
|
+
async list(opts = {}) {
|
|
745
|
+
const res = await this.http.request(
|
|
746
|
+
"/api/v1/profiles",
|
|
747
|
+
{ query: { kind: opts.kind } }
|
|
748
|
+
);
|
|
749
|
+
return res.profiles ?? [];
|
|
750
|
+
}
|
|
751
|
+
async get(profileId) {
|
|
752
|
+
return this.http.request(`/api/v1/profiles/${encodeURIComponent(profileId)}`);
|
|
753
|
+
}
|
|
754
|
+
/** Create a new CLOUD profile. Local profiles are managed by the
|
|
755
|
+
* plugin and cannot be created via the API. */
|
|
756
|
+
async create(opts) {
|
|
757
|
+
return this.http.request("/api/v1/profiles", {
|
|
758
|
+
method: "POST",
|
|
759
|
+
body: { name: opts.name, kind: "cloud" }
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
async delete(profileId) {
|
|
763
|
+
return this.http.request(`/api/v1/profiles/${encodeURIComponent(profileId)}`, {
|
|
764
|
+
method: "DELETE"
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/resources/schedules.ts
|
|
770
|
+
var SchedulesResource = class {
|
|
771
|
+
constructor(http) {
|
|
772
|
+
this.http = http;
|
|
773
|
+
}
|
|
774
|
+
async list(opts = {}) {
|
|
775
|
+
const res = await this.http.request(
|
|
776
|
+
"/api/v1/schedules",
|
|
777
|
+
{ query: { active: opts.active, workflowId: opts.workflowId } }
|
|
778
|
+
);
|
|
779
|
+
return res.schedules ?? [];
|
|
780
|
+
}
|
|
781
|
+
async get(scheduleId) {
|
|
782
|
+
return this.http.request(`/api/v1/schedules/${encodeURIComponent(scheduleId)}`);
|
|
783
|
+
}
|
|
784
|
+
/** Create a new schedule. Returns the created record. */
|
|
785
|
+
async create(opts) {
|
|
786
|
+
return this.http.request("/api/v1/schedules", {
|
|
787
|
+
method: "POST",
|
|
788
|
+
body: opts
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
/** Pause / resume / change cron / timezone. */
|
|
792
|
+
async update(scheduleId, patch) {
|
|
793
|
+
return this.http.request(`/api/v1/schedules/${encodeURIComponent(scheduleId)}`, {
|
|
794
|
+
method: "PATCH",
|
|
795
|
+
body: patch
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
async delete(scheduleId) {
|
|
799
|
+
return this.http.request(`/api/v1/schedules/${encodeURIComponent(scheduleId)}`, {
|
|
800
|
+
method: "DELETE"
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// src/resources/subscription.ts
|
|
806
|
+
var SubscriptionResource = class {
|
|
807
|
+
constructor(http) {
|
|
808
|
+
this.http = http;
|
|
809
|
+
}
|
|
810
|
+
/** Get the current plan + balances + usage. */
|
|
811
|
+
async get() {
|
|
812
|
+
return this.http.request("/api/v1/subscription");
|
|
813
|
+
}
|
|
814
|
+
// NOT SHIPPED (2026-09-03, v0.4.0): usage() was removed — the route it
|
|
815
|
+
// targeted (/api/v1/subscription/usage) does not exist on the live API
|
|
816
|
+
// (the only usage route is /api/v1/usage, with a different shape).
|
|
817
|
+
// Re-add once a real time-series endpoint ships.
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/resources/workflows.ts
|
|
821
|
+
var WorkflowsResource = class {
|
|
822
|
+
constructor(http) {
|
|
823
|
+
this.http = http;
|
|
824
|
+
}
|
|
825
|
+
async list(opts = {}) {
|
|
826
|
+
const res = await this.http.request(
|
|
827
|
+
"/api/v1/workflows",
|
|
828
|
+
{ query: { limit: opts.limit, q: opts.search, tag: opts.tag } }
|
|
829
|
+
);
|
|
830
|
+
return res.workflows ?? [];
|
|
831
|
+
}
|
|
832
|
+
async get(workflowId) {
|
|
833
|
+
return this.http.request(`/api/v1/workflows/${encodeURIComponent(workflowId)}`);
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Trigger a workflow. Returns immediately with `executionId`. Use
|
|
837
|
+
* `px.executions.wait(id)` to block until completion or
|
|
838
|
+
* `px.executions.stream(id)` to follow log events live.
|
|
839
|
+
*
|
|
840
|
+
* The `execution` options override the workflow's saved settings
|
|
841
|
+
* for this one run — you don't have to edit the workflow in VS to
|
|
842
|
+
* change the proxy, region, profile, etc.
|
|
843
|
+
*/
|
|
844
|
+
async run(workflowId, opts = {}) {
|
|
845
|
+
return this.http.request(
|
|
846
|
+
`/api/v1/workflows/${encodeURIComponent(workflowId)}/execute`,
|
|
847
|
+
{
|
|
848
|
+
method: "POST",
|
|
849
|
+
body: {
|
|
850
|
+
variables: opts.input,
|
|
851
|
+
executionOptions: opts.execution
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Run + block. Returns the final ExecutionRecord. Throws
|
|
858
|
+
* `TimeoutError` if the run takes longer than `timeoutMs`
|
|
859
|
+
* (default 5 minutes).
|
|
860
|
+
*/
|
|
861
|
+
async runAndWait(workflowId, opts = {}) {
|
|
862
|
+
const started = await this.run(workflowId, opts);
|
|
863
|
+
return this.waitInternal(started.executionId, opts.timeoutMs ?? 5 * 6e4, opts.pollIntervalMs ?? 2e3);
|
|
864
|
+
}
|
|
865
|
+
async waitInternal(executionId, timeoutMs, pollIntervalMs) {
|
|
866
|
+
const deadline = Date.now() + timeoutMs;
|
|
867
|
+
while (true) {
|
|
868
|
+
const exec = await this.http.request(
|
|
869
|
+
`/api/v1/executions/${encodeURIComponent(executionId)}`
|
|
870
|
+
);
|
|
871
|
+
if (["completed", "failed", "cancelled", "timed_out"].includes(exec.status)) return exec;
|
|
872
|
+
if (Date.now() > deadline) {
|
|
873
|
+
throw new TimeoutError(`Execution ${executionId} did not finish within ${timeoutMs}ms`);
|
|
874
|
+
}
|
|
875
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// src/models.ts
|
|
881
|
+
var AUTOPILOT_MODELS = [
|
|
882
|
+
// ── Standard (Gemini native Computer Use) ────────────────────────────────
|
|
883
|
+
"gemini-3.5-flash",
|
|
884
|
+
// Recommended — fast and cheap. THE DEFAULT.
|
|
885
|
+
"gemini-3.7-flash",
|
|
886
|
+
// Newest GA Flash — Google-recommended for computer use
|
|
887
|
+
"gemini-3.6-flash",
|
|
888
|
+
// Newest Flash (Computer Use preview)
|
|
889
|
+
"gemini-default",
|
|
890
|
+
// Gemini 2.5 Computer Use (legacy)
|
|
891
|
+
// ── Experimental (Gemini) ────────────────────────────────────────────────
|
|
892
|
+
"gemini-3-flash-preview",
|
|
893
|
+
"gemini-3.5-flash-lite",
|
|
894
|
+
// Harness aliases — resolved server-side to an underlying brain + prompt.
|
|
895
|
+
"model-a",
|
|
896
|
+
"model-a1",
|
|
897
|
+
"model-b",
|
|
898
|
+
"model-j",
|
|
899
|
+
"model-k",
|
|
900
|
+
"model-k37",
|
|
901
|
+
// ── Anthropic / OpenAI native Computer Use ───────────────────────────────
|
|
902
|
+
"claude-sonnet-4-6",
|
|
903
|
+
"claude-opus-4-8",
|
|
904
|
+
"gpt-5.6-terra",
|
|
905
|
+
"gpt-5.6-sol",
|
|
906
|
+
"gpt-5.5",
|
|
907
|
+
// ── Generic Vision Loop (standard chat models on screenshots) ────────────
|
|
908
|
+
"claude-fable-5-vision",
|
|
909
|
+
"claude-opus-5-vision",
|
|
910
|
+
"claude-sonnet-5-vision",
|
|
911
|
+
"claude-sonnet-4-6-vision",
|
|
912
|
+
"gpt-5.6-luna-vision",
|
|
913
|
+
"gpt-5.4-vision",
|
|
914
|
+
"gpt-4o-vision",
|
|
915
|
+
"kimi-k3-vision",
|
|
916
|
+
// ── Experimental server-side harness engines ─────────────────────────────
|
|
917
|
+
"modelc",
|
|
918
|
+
"model-d",
|
|
919
|
+
"model-d1",
|
|
920
|
+
"model-e",
|
|
921
|
+
"model-f",
|
|
922
|
+
"model-h",
|
|
923
|
+
"model-i"
|
|
924
|
+
];
|
|
925
|
+
var DEFAULT_AUTOPILOT_MODEL = "gemini-3.5-flash";
|
|
926
|
+
|
|
927
|
+
// src/index.ts
|
|
928
|
+
var Copilot = class {
|
|
929
|
+
constructor(helpers) {
|
|
930
|
+
this.helpers = helpers;
|
|
931
|
+
}
|
|
932
|
+
/** Execute a natural-language action on a connected Playwright page. */
|
|
933
|
+
do(page, instruction, opts) {
|
|
934
|
+
return this.helpers.do(page, instruction, opts);
|
|
935
|
+
}
|
|
936
|
+
/** Pull typed data from the page (Zod schema or raw JSON Schema). */
|
|
937
|
+
read(page, schema) {
|
|
938
|
+
return this.helpers.read(page, schema);
|
|
939
|
+
}
|
|
940
|
+
/** Discover available actions on the page; useful pre-`do` step. */
|
|
941
|
+
scan(page, hint) {
|
|
942
|
+
return this.helpers.scan(page, hint);
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
var Prompteryx = class {
|
|
946
|
+
constructor(opts) {
|
|
947
|
+
this.http = new HttpClient(opts);
|
|
948
|
+
this.workflows = new WorkflowsResource(this.http);
|
|
949
|
+
this.executions = new ExecutionsResource(this.http);
|
|
950
|
+
this.cloudBrowser = new CloudBrowserResource(this.http);
|
|
951
|
+
this.autopilot = new AutopilotResource(this.http);
|
|
952
|
+
this.copilot = new Copilot(new CopilotHelpers(this.http));
|
|
953
|
+
this.schedules = new SchedulesResource(this.http);
|
|
954
|
+
this.profiles = new ProfilesResource(this.http);
|
|
955
|
+
this.subscription = new SubscriptionResource(this.http);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
var src_default = Prompteryx;
|
|
959
|
+
export {
|
|
960
|
+
AUTOPILOT_MODELS,
|
|
961
|
+
AuthError,
|
|
962
|
+
DEFAULT_AUTOPILOT_MODEL,
|
|
963
|
+
NetworkError,
|
|
964
|
+
NotFoundError,
|
|
965
|
+
ParseError,
|
|
966
|
+
Prompteryx,
|
|
967
|
+
PrompteryxError,
|
|
968
|
+
QuotaError,
|
|
969
|
+
RateLimitError,
|
|
970
|
+
ServerError,
|
|
971
|
+
TimeoutError,
|
|
972
|
+
ValidationError,
|
|
973
|
+
src_default as default
|
|
974
|
+
};
|
|
975
|
+
//# sourceMappingURL=index.mjs.map
|