@outcrawl/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -0
- package/dist/index.js +2493 -0
- package/dist/types/_deps/core/agent-alias.d.ts +219 -0
- package/dist/types/_deps/core/brand.d.ts +62 -0
- package/dist/types/_deps/core/certificate.d.ts +100 -0
- package/dist/types/_deps/core/cron.d.ts +116 -0
- package/dist/types/_deps/core/errors.d.ts +340 -0
- package/dist/types/_deps/core/index.d.ts +17 -0
- package/dist/types/_deps/core/money.d.ts +108 -0
- package/dist/types/_deps/core/registry.d.ts +466 -0
- package/dist/types/_deps/core/rules.d.ts +237 -0
- package/dist/types/_deps/core/secrets.d.ts +278 -0
- package/dist/types/_deps/core/types.d.ts +1677 -0
- package/dist/types/_deps/integrations/connector.d.ts +153 -0
- package/dist/types/_deps/replay/events.d.ts +1004 -0
- package/dist/types/agent.d.ts +313 -0
- package/dist/types/availability.d.ts +86 -0
- package/dist/types/browser.d.ts +107 -0
- package/dist/types/client.d.ts +128 -0
- package/dist/types/hands.d.ts +259 -0
- package/dist/types/index.d.ts +111 -0
- package/dist/types/integrations.d.ts +63 -0
- package/dist/types/monitors.d.ts +24 -0
- package/dist/types/page.d.ts +96 -0
- package/dist/types/profiles.d.ts +26 -0
- package/dist/types/result.d.ts +90 -0
- package/dist/types/rules.d.ts +41 -0
- package/dist/types/scrape.d.ts +68 -0
- package/dist/types/secrets.d.ts +36 -0
- package/dist/types/sessions.d.ts +124 -0
- package/dist/types/transport.d.ts +250 -0
- package/package.json +70 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human input — the default path, not a flag.
|
|
3
|
+
*
|
|
4
|
+
* `page.click()` lands here; `page.raw.click()` is Playwright's. That ordering
|
|
5
|
+
* is deliberate: the failure mode we care about is someone forgetting to opt
|
|
6
|
+
* in, not someone forgetting to opt out. An agent that reasons perfectly and
|
|
7
|
+
* clicks like a robot is still a robot.
|
|
8
|
+
*
|
|
9
|
+
* The constants below are the measured ones from the product surface (§6.4),
|
|
10
|
+
* taken from real human sessions rather than guessed, alongside what Playwright
|
|
11
|
+
* does by default:
|
|
12
|
+
*
|
|
13
|
+
* | | human | Playwright default |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | click landing offset | mean 8.1px, sd 15.0 | 0.0, sd 0.0 — exact bbox centre |
|
|
16
|
+
* | key flight | mean 127ms, sd 89.4 | sd 34.3 |
|
|
17
|
+
* | sub-movements per stroke | median 3 | 30, with per-sample tremor |
|
|
18
|
+
* | peak-velocity position | 0.40 | 0.50 for any eased curve |
|
|
19
|
+
* | fractional coordinates | ~100% | 0% in some stacks |
|
|
20
|
+
*
|
|
21
|
+
* Two things this cannot fix from the client — `getCoalescedEvents()` returning
|
|
22
|
+
* 1 and `PointerEvent.pressure` always 0 — are properties of where the event
|
|
23
|
+
* enters the stack, and are fixed in the browser's C++.
|
|
24
|
+
*
|
|
25
|
+
* `agenthands` (aeonmindai/agenthands v1.1.1) is the reference implementation of
|
|
26
|
+
* this interface. It is not vendored here: {@link Hands} is the seam, and a
|
|
27
|
+
* caller passes `new Outcrawl({ hands })` to swap the driver. The built-in
|
|
28
|
+
* {@link createPlaywrightHands} drives the same measured model through
|
|
29
|
+
* Playwright's mouse and keyboard so the SDK is not inert without it.
|
|
30
|
+
*
|
|
31
|
+
* `scroll` is the one method that does NOT re-derive its model here, and the
|
|
32
|
+
* asymmetry is deliberate. Pointer and keyboard timing reduce to the five
|
|
33
|
+
* constants above; a scroll is a sequence of GESTURES with a phase per event, a
|
|
34
|
+
* per-device granularity and three measured distributions behind it, and a
|
|
35
|
+
* second copy of that would be a second thing to recalibrate. So `scroll`
|
|
36
|
+
* delegates to `agenthands`, which owns the model, and supplies the
|
|
37
|
+
* `Outcrawl.dispatchWheelPath` dispatcher the session hands it. Without a
|
|
38
|
+
* dispatcher the library falls back to `mouse.wheel()` on its own, which is what
|
|
39
|
+
* a caller driving a stock browser gets.
|
|
40
|
+
*/
|
|
41
|
+
export interface BoundingBox {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
}
|
|
47
|
+
export interface RawLocator {
|
|
48
|
+
boundingBox(options?: {
|
|
49
|
+
timeout?: number;
|
|
50
|
+
}): Promise<BoundingBox | null>;
|
|
51
|
+
scrollIntoViewIfNeeded(options?: {
|
|
52
|
+
timeout?: number;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
focus(options?: {
|
|
55
|
+
timeout?: number;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
fill(value: string, options?: {
|
|
58
|
+
timeout?: number;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
selectOption(values: string[], options?: {
|
|
61
|
+
timeout?: number;
|
|
62
|
+
}): Promise<string[]>;
|
|
63
|
+
}
|
|
64
|
+
export interface RawMouse {
|
|
65
|
+
move(x: number, y: number, options?: {
|
|
66
|
+
steps?: number;
|
|
67
|
+
}): Promise<void>;
|
|
68
|
+
down(options?: {
|
|
69
|
+
button?: 'left' | 'right' | 'middle';
|
|
70
|
+
clickCount?: number;
|
|
71
|
+
}): Promise<void>;
|
|
72
|
+
up(options?: {
|
|
73
|
+
button?: 'left' | 'right' | 'middle';
|
|
74
|
+
clickCount?: number;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* `Input.dispatchMouseWheel`, one event per call. Here because it is the
|
|
78
|
+
* fallback route for {@link Hands.scroll} — the one a caller gets when the
|
|
79
|
+
* browser has no batched wheel entry point, which is every browser but ours.
|
|
80
|
+
*/
|
|
81
|
+
wheel(deltaX: number, deltaY: number): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
export interface RawKeyboard {
|
|
84
|
+
press(key: string, options?: {
|
|
85
|
+
delay?: number;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
insertText(text: string): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The slice of Playwright's `Page` this package types against.
|
|
91
|
+
*
|
|
92
|
+
* Two jobs. It is what the human-input driver calls — `mouse`, `keyboard`,
|
|
93
|
+
* `locator` — and it is the floor under `page.raw`, so the escape hatch is
|
|
94
|
+
* usable without a caller having to parameterise the page type. A caller who
|
|
95
|
+
* does supply their real `Page` gets all of it: `OutcrawlPage` is generic in
|
|
96
|
+
* the page type and never narrows it.
|
|
97
|
+
*
|
|
98
|
+
* Options are typed loosely on purpose. This is a floor, not a re-declaration
|
|
99
|
+
* of Playwright's API, and pinning every option object here would mean
|
|
100
|
+
* tracking Playwright's releases to keep an assignability check passing.
|
|
101
|
+
*/
|
|
102
|
+
export interface RawPage {
|
|
103
|
+
readonly mouse: RawMouse;
|
|
104
|
+
readonly keyboard: RawKeyboard;
|
|
105
|
+
locator(selector: string): RawLocator;
|
|
106
|
+
url(): string;
|
|
107
|
+
close(options?: {
|
|
108
|
+
runBeforeUnload?: boolean;
|
|
109
|
+
}): Promise<void>;
|
|
110
|
+
click(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
111
|
+
dblclick(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
112
|
+
fill(selector: string, value: string, options?: Record<string, unknown>): Promise<void>;
|
|
113
|
+
hover(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
114
|
+
press(selector: string, key: string, options?: Record<string, unknown>): Promise<void>;
|
|
115
|
+
type(selector: string, text: string, options?: Record<string, unknown>): Promise<void>;
|
|
116
|
+
check(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
117
|
+
uncheck(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
118
|
+
focus(selector: string, options?: Record<string, unknown>): Promise<void>;
|
|
119
|
+
selectOption(selector: string, values: unknown, options?: Record<string, unknown>): Promise<string[]>;
|
|
120
|
+
goto(url: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
121
|
+
content(): Promise<string>;
|
|
122
|
+
title(): Promise<string>;
|
|
123
|
+
waitForSelector(selector: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
124
|
+
}
|
|
125
|
+
export type WheelPhase = 'began' | 'changed' | 'stationary' | 'ended' | 'cancelled';
|
|
126
|
+
export interface WheelSample {
|
|
127
|
+
/** Milliseconds from the start of the path. Non-decreasing; the first is 0. */
|
|
128
|
+
offsetMs: number;
|
|
129
|
+
/** CSS pixels. Positive `deltaY` scrolls the content down. */
|
|
130
|
+
deltaX: number;
|
|
131
|
+
deltaY: number;
|
|
132
|
+
phase: WheelPhase;
|
|
133
|
+
/** macOS momentum, for a caller replaying a recorded gesture that had one. */
|
|
134
|
+
momentumPhase?: 'began' | 'changed' | 'ended' | 'cancelled';
|
|
135
|
+
}
|
|
136
|
+
export interface WheelPath {
|
|
137
|
+
/** Viewport-relative CSS pixels. One point for the whole path: the browser's
|
|
138
|
+
* own wheel-latching slop region is 10 CSS pixels, and a cursor that moves
|
|
139
|
+
* during a scroll is a pointer path, not a wheel one. */
|
|
140
|
+
x: number;
|
|
141
|
+
y: number;
|
|
142
|
+
path: WheelSample[];
|
|
143
|
+
granularity: 'precisePixel' | 'pixel';
|
|
144
|
+
}
|
|
145
|
+
/** Dispatches a whole gesture through a browser entry point that takes phases. */
|
|
146
|
+
export type WheelPathDispatcher = (path: WheelPath) => Promise<unknown>;
|
|
147
|
+
/**
|
|
148
|
+
* Input the browser refused to deliver, rather than input that failed.
|
|
149
|
+
*
|
|
150
|
+
* `Outcrawl.dispatchWheelPath` refuses while `Outcrawl.lock` is held, by design:
|
|
151
|
+
* the lock's `ScopedIgnoreInputEvents` drops every event before the renderer
|
|
152
|
+
* sees it, so a command that reported success would be lying. That refusal is a
|
|
153
|
+
* statement about the caller's own lock discipline and not about the page, so it
|
|
154
|
+
* arrives as itself instead of as a failed step.
|
|
155
|
+
*/
|
|
156
|
+
export declare class InputRefusedError extends Error {
|
|
157
|
+
/** The refusal as the browser worded it. */
|
|
158
|
+
readonly refusal: string;
|
|
159
|
+
constructor(what: string, refusal: string);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* One bound set of hands. Bound, not static: personas bind to the profile, so
|
|
163
|
+
* the same identity keeps the same typing pace and click character across
|
|
164
|
+
* sessions. Real typing pace varies 3.6x between people, and an identity that
|
|
165
|
+
* returns at a different speed every visit is describing someone who is not the
|
|
166
|
+
* same person.
|
|
167
|
+
*/
|
|
168
|
+
export interface Hands {
|
|
169
|
+
click(page: RawPage, selector: string): Promise<void>;
|
|
170
|
+
fill(page: RawPage, selector: string, value: string): Promise<void>;
|
|
171
|
+
type(page: RawPage, selector: string, text: string): Promise<void>;
|
|
172
|
+
hover(page: RawPage, selector: string): Promise<void>;
|
|
173
|
+
press(page: RawPage, selector: string, key: string): Promise<void>;
|
|
174
|
+
selectOption(page: RawPage, selector: string, values: readonly string[]): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* Scroll `deltaY` CSS pixels; positive scrolls down.
|
|
177
|
+
*
|
|
178
|
+
* `selector` decides only WHERE the gesture happens — the browser latches the
|
|
179
|
+
* scroll to whatever is under the cursor — so it is optional and its absence
|
|
180
|
+
* means "where the cursor already is", which is what a page scroll is.
|
|
181
|
+
*
|
|
182
|
+
* Throws {@link InputRefusedError} when the browser refused the gesture rather
|
|
183
|
+
* than failing to deliver it, which today means the session is holding
|
|
184
|
+
* `Outcrawl.lock`.
|
|
185
|
+
*/
|
|
186
|
+
scroll(page: RawPage, deltaY: number, selector?: string): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* What a set of hands is bound to when the session mints it.
|
|
190
|
+
*
|
|
191
|
+
* An object rather than a bare persona string because the persona is not the
|
|
192
|
+
* only thing a driver needs from the session: the batched wheel entry point is
|
|
193
|
+
* a property of the browser on the other end of the connection, and passing it
|
|
194
|
+
* per call would make a session-wide capability look like a per-call option.
|
|
195
|
+
*/
|
|
196
|
+
export interface HandsBinding {
|
|
197
|
+
/**
|
|
198
|
+
* Persona token minted with the session. Opaque, and never chosen by the
|
|
199
|
+
* caller — identity is not customer-visible.
|
|
200
|
+
*/
|
|
201
|
+
readonly persona: string;
|
|
202
|
+
/**
|
|
203
|
+
* The browser's batched wheel entry point, when the connection has one. Absent
|
|
204
|
+
* for any browser but ours, and a driver must still scroll without it.
|
|
205
|
+
*/
|
|
206
|
+
readonly wheelPath?: WheelPathDispatcher;
|
|
207
|
+
}
|
|
208
|
+
/** Binds hands to one session. */
|
|
209
|
+
export type HandsFactory = (binding: HandsBinding) => Hands;
|
|
210
|
+
export type Sleep = (ms: number) => Promise<void>;
|
|
211
|
+
/** What `createHands` hands back, narrowed to the one method used here. */
|
|
212
|
+
export interface AgentHandsScroller {
|
|
213
|
+
scroll(deltaY: number): Promise<void>;
|
|
214
|
+
}
|
|
215
|
+
export interface AgentHandsOptions {
|
|
216
|
+
/** Binds the gesture model to one identity, as the persona binds typing pace. */
|
|
217
|
+
persona?: string | number;
|
|
218
|
+
seed?: number;
|
|
219
|
+
/** Where the gesture happens. One point for the whole path. */
|
|
220
|
+
start?: {
|
|
221
|
+
x: number;
|
|
222
|
+
y: number;
|
|
223
|
+
};
|
|
224
|
+
wheelPath?: WheelPathDispatcher;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The slice of `agenthands` the built-in driver calls.
|
|
228
|
+
*
|
|
229
|
+
* `scroll()` reaches for `mouse.wheel` and an optional `evaluate` probe and
|
|
230
|
+
* nothing else, so the page is typed as that much. A real Playwright `Page`
|
|
231
|
+
* satisfies the library's own `PageLike` in full; {@link RawPage} is the
|
|
232
|
+
* narrower floor this package types against and does not declare the keyboard
|
|
233
|
+
* methods `PageLike` asks for, none of which a scroll touches.
|
|
234
|
+
*/
|
|
235
|
+
export interface AgentHandsModule {
|
|
236
|
+
createHands(page: {
|
|
237
|
+
readonly mouse: Pick<RawMouse, 'wheel'>;
|
|
238
|
+
}, options?: AgentHandsOptions): AgentHandsScroller;
|
|
239
|
+
}
|
|
240
|
+
export type AgentHandsLoader = () => Promise<AgentHandsModule>;
|
|
241
|
+
export interface PlaywrightHandsOptions {
|
|
242
|
+
/**
|
|
243
|
+
* Injected so timing can be collapsed in tests. Nothing else about the model
|
|
244
|
+
* changes: the same samples are drawn, they are just not waited out.
|
|
245
|
+
*/
|
|
246
|
+
sleep?: Sleep;
|
|
247
|
+
/**
|
|
248
|
+
* Loads the scroll model. Injected so the seam is testable without resolving
|
|
249
|
+
* the package, and overridable by a caller who vendors it.
|
|
250
|
+
*/
|
|
251
|
+
loadAgentHands?: AgentHandsLoader;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The built-in driver. Same measured model as `agenthands`, expressed through
|
|
255
|
+
* Playwright's mouse and keyboard, so the SDK produces human input out of the
|
|
256
|
+
* box rather than only when a caller remembers to install a second package.
|
|
257
|
+
* `scroll` is the exception and delegates — see the note at the top of this file.
|
|
258
|
+
*/
|
|
259
|
+
export declare function createPlaywrightHands(options?: PlaywrightHandsOptions): HandsFactory;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@outcrawl/sdk` — the typed client, and the Playwright integration.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { Outcrawl } from '@outcrawl/sdk';
|
|
6
|
+
* const oc = new Outcrawl({ apiKey: process.env.OUTCRAWL_KEY });
|
|
7
|
+
*
|
|
8
|
+
* const browser = await oc.browser({ profile: 'acme-user-42' });
|
|
9
|
+
* const page = await browser.newPage();
|
|
10
|
+
* await page.click('#login'); // human input, by default
|
|
11
|
+
* await page.raw.click('#login'); // Playwright's, and recorded
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The API is the truth; this is a transport over it. Every capability in
|
|
15
|
+
* `@outcrawl/core`'s registry is reachable from this client under its own
|
|
16
|
+
* dotted name, and a test in this package fails if one is not.
|
|
17
|
+
*
|
|
18
|
+
* The one thing here that is not in the API, MCP or the CLI is the live page —
|
|
19
|
+
* `page.act`, `page.observe`, `page.extract` — because it needs a CDP
|
|
20
|
+
* connection in the caller's own process. That is a difference in kind, not an
|
|
21
|
+
* omission: the other surfaces reach the same capabilities through `agent` and
|
|
22
|
+
* `scrape`, which run the loop on our side.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* `API_KEY_ENV` and `API_URL_ENV` are exported because a caller who wants to
|
|
26
|
+
* say "set OUTCRAWL_API_KEY" in their own error message should read the name
|
|
27
|
+
* off the client rather than retyping it — a retyped variable name is a
|
|
28
|
+
* customer told to set a variable nothing reads.
|
|
29
|
+
*/
|
|
30
|
+
export { API_KEY_ENV, API_URL_ENV, Outcrawl, type OutcrawlOptions } from './client.js';
|
|
31
|
+
export { Browser, IdentityOverrideError, assertNoIdentityOverride, openBrowser, resolveExit, type BrowserDeps, type BrowserOptions, } from './browser.js';
|
|
32
|
+
export { OutcrawlPage, type ActResult, type ExtractResult, type ObservedActions } from './page.js';
|
|
33
|
+
export { Session, SessionControl, Sessions, UnsupportedSessionFilterError, assertSupportedFilter, } from './sessions.js';
|
|
34
|
+
export { Integrations } from './integrations.js';
|
|
35
|
+
export { Profiles } from './profiles.js';
|
|
36
|
+
export { Rules } from './rules.js';
|
|
37
|
+
export { Secrets } from './secrets.js';
|
|
38
|
+
/**
|
|
39
|
+
* Which declared capabilities this SDK actually serves. Exported because a
|
|
40
|
+
* caller integrating against `monitors.*` deserves to discover it from the
|
|
41
|
+
* types rather than from a 503 at runtime.
|
|
42
|
+
*/
|
|
43
|
+
export { CAPABILITY_AVAILABILITY, assertAvailable, type Unavailable, } from './availability.js';
|
|
44
|
+
export { Monitors } from './monitors.js';
|
|
45
|
+
export { AgentEvents, AgentHandle, AgentJob, InvalidAgentRequestError, agent, createAgentApi, type AgentApi, type AgentEventOptions, type AgentResultsOptions, } from './agent.js';
|
|
46
|
+
export { CrawlHandle, crawl, scrape, search, type CrawlOptions, type ScrapeOptions, type SearchHits, type SearchOptions, } from './scrape.js';
|
|
47
|
+
export { deletedFrom, inlineUsage, pagedFrom, queryOf, unwrapItem, withUsage, type ClientContext, type Deleted, type Paged, type Result, } from './result.js';
|
|
48
|
+
export { HttpTransport, OutcrawlApiError, alwaysStreams, errorFromWire, resolveRequest, streamSwitch, type CdpBrowser, type CdpBrowserContext, type CdpSession, type Connection, type ConnectSpec, type ExtractResponse, type HttpTransportOptions, type ObserveResponse, type RawInputMark, type RequestSpec, type Transport, } from './transport.js';
|
|
49
|
+
export { createPlaywrightHands, InputRefusedError, type AgentHandsLoader, type AgentHandsModule, type AgentHandsOptions, type AgentHandsScroller, type BoundingBox, type Hands, type HandsBinding, type HandsFactory, type PlaywrightHandsOptions, type RawKeyboard, type RawLocator, type RawMouse, type RawPage, type Sleep, type WheelPath, type WheelPathDispatcher, type WheelPhase, type WheelSample, } from './hands.js';
|
|
50
|
+
/**
|
|
51
|
+
* Re-exported so a caller catching a leased profile does not need a second
|
|
52
|
+
* import to name the error, and so `instanceof` is against the same class the
|
|
53
|
+
* SDK threw.
|
|
54
|
+
*
|
|
55
|
+
* EVERY CLASS `errorFromWire` CAN RETURN IS HERE, and the list was audited
|
|
56
|
+
* rather than extended by taste: eight of the fifteen were missing while the
|
|
57
|
+
* transport was already handing them to customers, so the errors a caller is
|
|
58
|
+
* most likely to meet in production — over their plan's rate limit, at their
|
|
59
|
+
* concurrency ceiling, on a key with no plan — were errors they could not name
|
|
60
|
+
* or `instanceof`. `OUTCRAWL_ERROR_CODES` and `isKnownErrorCode` come with
|
|
61
|
+
* them, because branching on `code` is the documented way to handle these and
|
|
62
|
+
* a caller cannot write an exhaustive switch against a union they cannot see.
|
|
63
|
+
*/
|
|
64
|
+
export { BadRequestError, CapExceededError, CapabilityUnavailableError, ConcurrencyLimitError, ExitUnavailableError, InternalError, MethodNotAllowedError, NoCapacityError, NotFoundError, OutcrawlError, PlanRequiredError, ProfileInUseError, ProfileNotFoundError, QuotaExceededError, TenantScopeError, UnauthorizedError, isOutcrawlError, isKnownErrorCode, OUTCRAWL_ERROR_CODES, CAPABILITIES, getCapability, PROFILE_KEYS, type Capability, type CapabilityName, type OutcrawlErrorCode, type QuotaCeiling, } from './_deps/core/index.js';
|
|
65
|
+
/**
|
|
66
|
+
* The wire contract.
|
|
67
|
+
*
|
|
68
|
+
* Re-exported because this package is a customer's only door: `@outcrawl/core`
|
|
69
|
+
* is not on npm and is not going to be, so a type that is only nameable from
|
|
70
|
+
* there is a type nobody outside this repository can name.
|
|
71
|
+
*
|
|
72
|
+
* It was not a theoretical gap. `oc.scrape()` is declared to return
|
|
73
|
+
* `Result<ScrapeResult>` and `ScrapeResult` was not exported, so the first
|
|
74
|
+
* thing a customer does with a typed client — annotate the value they just
|
|
75
|
+
* got, or write a helper that takes it — did not compile. Same for
|
|
76
|
+
* `AgentRequest`, which `oc.agent()` takes as its only argument. A typed
|
|
77
|
+
* client whose types cannot be referenced is a client with no types.
|
|
78
|
+
*
|
|
79
|
+
* `scripts/build.ts` asserts this list stays complete: it reads the core types
|
|
80
|
+
* the published declarations actually reference and fails the build if one of
|
|
81
|
+
* them is not exported here, so a new method that returns a new wire type
|
|
82
|
+
* cannot ship unnameable.
|
|
83
|
+
*
|
|
84
|
+
* `PageMetadata`, `PageChallenge`, `PageChallengeOutcome` and `ChallengeWatch`
|
|
85
|
+
* arrived on 2026-09-06 because the assertion is over DIRECT references and
|
|
86
|
+
* these are one hop further: `ScrapeResult.metadata` is a `PageMetadata`, so a
|
|
87
|
+
* customer could always READ `result.metadata.challengeWatch` and could never
|
|
88
|
+
* name its type to switch exhaustively over it. A three-value union a caller
|
|
89
|
+
* is expected to branch on, whose union type is unnameable, is the same defect
|
|
90
|
+
* this block exists to close, one level down.
|
|
91
|
+
*
|
|
92
|
+
* `Secret`, `RulesView` and their one-hop companions arrived with the secrets
|
|
93
|
+
* and rules clients: `oc.secrets.create()` answers a `Result<Secret>` and
|
|
94
|
+
* `oc.rules.get()` a `Result<RulesView>`, and `RulesViewRule.class` and
|
|
95
|
+
* `.gate` are exactly the fields a caller is told to branch on — so the union
|
|
96
|
+
* types behind them have to be nameable or the branch cannot be written.
|
|
97
|
+
*/
|
|
98
|
+
export type { AgentEventLevel, AgentRecord, AgentRecordKind, AgentRequest, AgentResult, AgentRun, AgentRunStatus, AgentStep, AgentStopReason, AgentSubmission, BilledResources, ChallengeWatch, CrawlJob, CrawlPage, CrawlRequest, CreditBalance, ExitSpec, ExitTarget, HttpMethod, JsonSchema, Monitor, MonitorQuery, MonitorSpec, ObservedAction, PageChallenge, PageChallengeOutcome, PageMetadata, Profile, ProfileCreateRequest, ProfileQuery, ResolvedExit, RuleClass, RuleGate, RulesView, RulesViewRule, ScrapeRequest, ScrapeResult, SearchHit, SearchRequest, Secret, SecretCreateRequest, SecretKind, SecretQuery, SessionExport, SessionOutcome, SessionQuery, SessionStatus, SessionSummary, Timestamp, Usage, UsageQuery, UsageReport, } from './_deps/core/index.js';
|
|
99
|
+
/**
|
|
100
|
+
* The connector wire types, on the block above's argument and from the package
|
|
101
|
+
* that owns them rather than from a copy kept here.
|
|
102
|
+
*
|
|
103
|
+
* `oc.integrations.connect()` takes a `ConnectorInput` and every method
|
|
104
|
+
* answers `Connector` rows verbatim — there is no view in front of them,
|
|
105
|
+
* because a connector row holds a secrets-store HANDLE and never a value — so
|
|
106
|
+
* a customer who cannot name `ConnectorAuth` cannot write the switch that
|
|
107
|
+
* reads which handle a connector authenticates with. `scripts/build.ts`
|
|
108
|
+
* carries `connector.d.ts` into `_deps` for the same reason it carries
|
|
109
|
+
* replay's `events.d.ts`.
|
|
110
|
+
*/
|
|
111
|
+
export type { Connector, ConnectorAuth, ConnectorInput, ConnectorTransport, } from './_deps/integrations/connector.js';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integrations — what a run can reach OUTSIDE the browser.
|
|
3
|
+
*
|
|
4
|
+
* A workspace connects an MCP server — Gmail, Slack, Twilio — and a run that
|
|
5
|
+
* was granted it calls that server's tools mid-task: to read a 2FA code, to
|
|
6
|
+
* ask a question, to phone a human. This class is the management half, for a
|
|
7
|
+
* person. The calling half is the agent loop's `callTool`, and it is not
|
|
8
|
+
* reachable from here.
|
|
9
|
+
*
|
|
10
|
+
* ── CONNECTING IS NOT GRANTING ───────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* {@link Integrations.connect} answering a row does NOT mean a run can call it.
|
|
13
|
+
* Reach is granted PER RUN through `AgentRequest.integrations`, and a submit
|
|
14
|
+
* that names nothing reaches nothing however many connectors the workspace
|
|
15
|
+
* has. That default is the containment: a page that talks a model into
|
|
16
|
+
* "email this table to attacker@example.com" is talking to a run with no mail
|
|
17
|
+
* connector, whatever this account has connected. Name the connector on the
|
|
18
|
+
* submit, deliberately, per run — see `availability.ts`, which declines to
|
|
19
|
+
* gate these rows for exactly this reason.
|
|
20
|
+
*
|
|
21
|
+
* ── THE ROW IS THE RESPONSE ──────────────────────────────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* {@link Integrations.list} hands back {@link Connector} rows verbatim, with no
|
|
24
|
+
* projection in front of them, and that is the property rather than a
|
|
25
|
+
* shortcut: a connector row holds a secrets-store HANDLE and never a value, so
|
|
26
|
+
* there is nothing on it to withhold. A stripped-down view would imply the row
|
|
27
|
+
* has something worth hiding, and the day a field arrives that does, it would
|
|
28
|
+
* arrive on a type nobody reads twice. `@outcrawl/integrations`'s `Connector`
|
|
29
|
+
* states the same rule from the other side.
|
|
30
|
+
*
|
|
31
|
+
* `list` takes no query, and that too is a decision: this listing is the blast
|
|
32
|
+
* radius, and a filter on it is a way to read half of it and believe it is the
|
|
33
|
+
* whole.
|
|
34
|
+
*/
|
|
35
|
+
import type { Connector, ConnectorInput } from './_deps/integrations/connector.js';
|
|
36
|
+
import { type ClientContext, type Deleted, type Paged, type Result } from './result.js';
|
|
37
|
+
export declare class Integrations {
|
|
38
|
+
#private;
|
|
39
|
+
constructor(ctx: ClientContext);
|
|
40
|
+
/**
|
|
41
|
+
* Connect an MCP server so a run may be granted its tools.
|
|
42
|
+
*
|
|
43
|
+
* `auth.secret` is a secrets-store HANDLE and never a token: the plaintext is
|
|
44
|
+
* resolved at the instant of a call, by the tier making it, and nothing this
|
|
45
|
+
* client sends or receives ever holds one. HTTPS endpoints only, and an
|
|
46
|
+
* address that is not on the public internet is refused by the same parser
|
|
47
|
+
* the API and the CLI use, so the refusal reads identically whichever
|
|
48
|
+
* surface you came from.
|
|
49
|
+
*/
|
|
50
|
+
connect(input: ConnectorInput): Promise<Result<Connector>>;
|
|
51
|
+
/**
|
|
52
|
+
* Every connected server, the tools it exposes, and the handle it
|
|
53
|
+
* authenticates with. `tools: null` means every tool the remote advertises.
|
|
54
|
+
*/
|
|
55
|
+
list(): Promise<Paged<Connector>>;
|
|
56
|
+
/**
|
|
57
|
+
* Disconnect a server. Takes effect on the next call: a run already holding
|
|
58
|
+
* a grant finds the connector gone and is told so, rather than being cut off
|
|
59
|
+
* mid-step. The credential is untouched — it lives in the secrets store and
|
|
60
|
+
* is deleted there.
|
|
61
|
+
*/
|
|
62
|
+
delete(id: string): Promise<Result<Deleted>>;
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monitors — declared, and not served.
|
|
3
|
+
*
|
|
4
|
+
* Every method here refuses with {@link CapabilityUnavailableError}. The
|
|
5
|
+
* methods still exist, and that is deliberate: the registry declares these
|
|
6
|
+
* rows, the coverage test in this package walks the registry to the client, and
|
|
7
|
+
* a capability that is silently absent from the client is the same omission in
|
|
8
|
+
* the other direction. A named refusal that says what is missing is reachable,
|
|
9
|
+
* greppable and honest; a missing property is a `TypeError` about `undefined`.
|
|
10
|
+
*
|
|
11
|
+
* The refusal is raised inside `resolveRequest`, not written out per method, so
|
|
12
|
+
* it cannot be forgotten by a method added later. See `availability.ts` for
|
|
13
|
+
* what is missing — in short: nothing runs a monitor's schedule, so a created
|
|
14
|
+
* monitor would be stored and never checked.
|
|
15
|
+
*/
|
|
16
|
+
import type { Monitor, MonitorQuery, MonitorSpec } from './_deps/core/index.js';
|
|
17
|
+
import { type ClientContext, type Deleted, type Paged, type Result } from './result.js';
|
|
18
|
+
export declare class Monitors {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(ctx: ClientContext);
|
|
21
|
+
create(spec: MonitorSpec): Promise<Result<Monitor>>;
|
|
22
|
+
list(query?: MonitorQuery): Promise<Paged<Monitor>>;
|
|
23
|
+
delete(id: string): Promise<Result<Deleted>>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live page — the one surface that exists in the SDK and nowhere else.
|
|
3
|
+
*
|
|
4
|
+
* `page.click()` goes through human input. `page.raw.click()` is Playwright's,
|
|
5
|
+
* untouched, for setup where speed matters. Both are supported; only one is the
|
|
6
|
+
* default, and it is the safe one, because the failure mode that costs a
|
|
7
|
+
* customer their account is someone forgetting to opt *in*.
|
|
8
|
+
*
|
|
9
|
+
* `observe()` and `act()` are the other half. `observe()` resolves an
|
|
10
|
+
* instruction to actions and executes none of them, which makes it both the
|
|
11
|
+
* review point and the action-cache unit: a hit replays the resolved action with
|
|
12
|
+
* zero model tokens.
|
|
13
|
+
*/
|
|
14
|
+
import type { JsonSchema, ObservedAction, Usage } from './_deps/core/index.js';
|
|
15
|
+
import type { Hands, RawPage } from './hands.js';
|
|
16
|
+
import type { Connection, RawInputMark } from './transport.js';
|
|
17
|
+
/**
|
|
18
|
+
* Reviewable actions, with the usage the resolution cost carried on the array
|
|
19
|
+
* itself — the same shape as search hits, and for the same reason: usage rides
|
|
20
|
+
* on the result that caused it.
|
|
21
|
+
*/
|
|
22
|
+
export type ObservedActions = readonly ObservedAction[] & {
|
|
23
|
+
readonly usage: Usage;
|
|
24
|
+
};
|
|
25
|
+
export interface ExtractResult<T> {
|
|
26
|
+
data: T;
|
|
27
|
+
usage: Usage;
|
|
28
|
+
}
|
|
29
|
+
/** What `act()` actually did. One action, named, with what it cost. */
|
|
30
|
+
export interface ActResult {
|
|
31
|
+
action: ObservedAction;
|
|
32
|
+
usage: Usage;
|
|
33
|
+
}
|
|
34
|
+
export interface OutcrawlPageInit<TRaw extends RawPage> {
|
|
35
|
+
page: TRaw;
|
|
36
|
+
hands: Hands;
|
|
37
|
+
connection: Connection;
|
|
38
|
+
onRaw: (mark: RawInputMark) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A Playwright page with human input under the familiar names.
|
|
42
|
+
*
|
|
43
|
+
* Generic in the page type so `page.raw` is the caller's real Playwright
|
|
44
|
+
* `Page` — everything an existing script does keeps working, and the SDK never
|
|
45
|
+
* narrows the escape hatch to the subset it happens to call itself.
|
|
46
|
+
*/
|
|
47
|
+
export declare class OutcrawlPage<TRaw extends RawPage = RawPage> {
|
|
48
|
+
#private;
|
|
49
|
+
/**
|
|
50
|
+
* Playwright's own surface, untouched, and recorded. Every input call through
|
|
51
|
+
* here is marked on the session: a checkout run that reached for `raw` is
|
|
52
|
+
* visibly flagged rather than silently different, so whoever reads the replay
|
|
53
|
+
* after a block is not guessing.
|
|
54
|
+
*/
|
|
55
|
+
readonly raw: TRaw;
|
|
56
|
+
constructor(init: OutcrawlPageInit<TRaw>);
|
|
57
|
+
get sessionId(): string;
|
|
58
|
+
url(): string;
|
|
59
|
+
click(selector: string): Promise<void>;
|
|
60
|
+
fill(selector: string, value: string): Promise<void>;
|
|
61
|
+
type(selector: string, text: string): Promise<void>;
|
|
62
|
+
hover(selector: string): Promise<void>;
|
|
63
|
+
press(selector: string, key: string): Promise<void>;
|
|
64
|
+
selectOption(selector: string, values: readonly string[]): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Scroll `deltaY` CSS pixels; positive scrolls down. `selector` aims the
|
|
67
|
+
* gesture at an element, because the browser latches a scroll to whatever is
|
|
68
|
+
* under the cursor; without one the gesture happens where the pointer is,
|
|
69
|
+
* which is what a page scroll is.
|
|
70
|
+
*
|
|
71
|
+
* Not `page.raw.mouse.wheel()`, and not a `scrollIntoView()` jump. Both land
|
|
72
|
+
* as `Input.dispatchMouseWheel` or as no wheel event at all, and this goes
|
|
73
|
+
* through the browser's own gesture entry point — see {@link Hands.scroll}.
|
|
74
|
+
*/
|
|
75
|
+
scroll(deltaY: number, selector?: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Resolve an instruction to the actions that *could* be taken. Executes none
|
|
78
|
+
* of them — that is the whole contract, and it is what makes the returned
|
|
79
|
+
* array reviewable and cacheable.
|
|
80
|
+
*/
|
|
81
|
+
observe(instruction: string, options?: {
|
|
82
|
+
cache?: boolean;
|
|
83
|
+
}): Promise<ObservedActions>;
|
|
84
|
+
/**
|
|
85
|
+
* Perform exactly one action.
|
|
86
|
+
*
|
|
87
|
+
* Given an {@link ObservedAction} from `observe()`, it replays that action
|
|
88
|
+
* with no model call at all. Given a string, it resolves the instruction
|
|
89
|
+
* first and performs the best candidate — one inference, one action.
|
|
90
|
+
*/
|
|
91
|
+
act(action: ObservedAction | string, options?: {
|
|
92
|
+
cache?: boolean;
|
|
93
|
+
}): Promise<ActResult>;
|
|
94
|
+
extract<T = unknown>(instruction: string, schema?: JsonSchema): Promise<ExtractResult<T>>;
|
|
95
|
+
close(): Promise<void>;
|
|
96
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profiles — a durable identity and its storage.
|
|
3
|
+
*
|
|
4
|
+
* The caller names it and nothing else. Everything that makes the identity —
|
|
5
|
+
* seed, chrome-or-brave, fingerprint, timezone, languages, WebRTC address — is
|
|
6
|
+
* minted by us, pinned for the profile's life, and never appears on this type.
|
|
7
|
+
* `Profile` has no `identity` field and there will never be one: the operator
|
|
8
|
+
* side runs the identity loop, the customer side sees outcomes.
|
|
9
|
+
*/
|
|
10
|
+
import { PROFILE_KEYS, type Profile, type ProfileCreateRequest, type ProfileQuery } from './_deps/core/index.js';
|
|
11
|
+
import { type ClientContext, type Deleted, type Paged, type Result } from './result.js';
|
|
12
|
+
export declare class Profiles {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(ctx: ClientContext);
|
|
15
|
+
create(request: ProfileCreateRequest): Promise<Result<Profile>>;
|
|
16
|
+
list(query?: ProfileQuery): Promise<Paged<Profile>>;
|
|
17
|
+
get(id: string): Promise<Result<Profile>>;
|
|
18
|
+
/** Deletes the profile and its stored cookies and site data. Irreversible. */
|
|
19
|
+
delete(id: string): Promise<Result<Deleted>>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The keys a `Profile` is allowed to have, straight from core. Exported so the
|
|
23
|
+
* "no identity, ever" rule is checkable by anything holding a profile, not only
|
|
24
|
+
* by this package's tests.
|
|
25
|
+
*/
|
|
26
|
+
export { PROFILE_KEYS };
|