@unbrowse/sdk 11.7.1 → 12.0.0-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,54 +1,80 @@
1
1
  # @unbrowse/sdk
2
2
 
3
- Clients submit tasks to your Unbrowse API. The server indexes a site with Browser Use on the first request and replays observed HTTP reads afterward. The SDK never reverse-engineers a site or starts a local browser.
3
+ TypeScript client for the Unbrowse API (`https://unbrowse.ai/api/v1`). Node 18.17+, Bun, Deno,
4
+ or any runtime with `fetch`. No dependencies. The `unbrowse` CLI is built on it.
5
+
6
+ ```bash
7
+ npm install https://github.com/unbrowse-ai/unbrowse-skill/releases/download/v12.0.0-alpha.1/unbrowse-sdk-12.0.0-alpha.1.tgz
8
+ ```
4
9
 
5
10
  ```ts
6
- import { Unbrowse } from '@unbrowse/sdk';
7
-
8
- const client = new Unbrowse({
9
- baseUrl: process.env.UNBROWSE_API_URL,
10
- apiKey: process.env.UNBROWSE_API_KEY,
11
- });
12
- const result = await client.run({
13
- url: 'https://example.com/products',
14
- intent: 'Find matching products',
15
- params: { q: 'books' },
16
- });
11
+ import { Unbrowse } from "@unbrowse/sdk";
12
+
13
+ const ub = new Unbrowse(); // UNBROWSE_API_KEY, https://unbrowse.ai/api/v1
14
+
15
+ let run = await ub.run({ task: "top stories on Hacker News", idempotencyKey: crypto.randomUUID() });
16
+ run = await ub.wait(run.runId); // until it leaves accepted/working
17
+
18
+ // Discover actual capabilities and schemas before selecting a site-specific run.
19
+ console.log(await ub.discover("flight search"));
17
20
  ```
18
21
 
19
- Deploy the Worker and private executor before using the new API. The default hosted URL has not been cut over. See [server execution](../../docs/server-execution.md) for deployment and measured proof.
22
+ Pass an `idempotencyKey` when you may retry: the same key returns the same run instead of starting
23
+ a second one.
20
24
 
21
- ## Credentials
25
+ `new Unbrowse({ apiKey, baseUrl, fetch })`: `apiKey` defaults to `UNBROWSE_API_KEY` (an API key or
26
+ an OAuth access token); `baseUrl` takes an origin or an `/api/v1` URL and defaults to
27
+ `UNBROWSE_BASE_URL`, then v3. Public registry reads need no key.
22
28
 
23
- Provide transient origin-scoped headers with `credentials: { storage: 'local', origin, headers }`, or use `storeCredential(id, { origin, headers })` and pass `credentials: { storage: 'remote', id }`. The server encrypts remote credentials per tenant. `deleteCredential(id)` removes them. No plaintext credential-read endpoint exists.
29
+ ## Runs
24
30
 
25
- Both modes send requests through the API. Local custody means your application retains the credential between calls; the server necessarily processes it during execution. Never paste credentials into prompts or URLs.
31
+ | Method | Route |
32
+ |---|---|
33
+ | `run({ task \| capability, targetUrl?, input?, interactionMode?, idempotencyKey? })` | `POST /runs` (key sent as `idempotency_key` + `Idempotency-Key`) |
34
+ | `inspect(runId)` | `GET /runs/:id` |
35
+ | `wait(runId, { timeoutMs? })` | polls `GET /runs/:id` |
36
+ | `events(runId)` | `GET /runs/:id/events` |
37
+ | `answer(runId, { field: value })` | `GET /runs/:id`, `POST /runs/:id/responses`, `GET /runs/:id` |
38
+ | `resume(runId, expectedStateRevision, responses)` | `POST /runs/:id/responses` |
39
+ | `cancel(runId)` | `POST /runs/:id/cancel` |
26
40
 
27
- ## Explicit requests and retries
41
+ A run is `succeeded` only when its result is verified (`verified: true`). `input_required` is not a
42
+ failure: answer on the same run. `outcome_unknown` means a change may have happened; do not retry
43
+ blindly. A run whose site needs a login nobody saved carries `signIn.url`: give it to the person.
28
44
 
29
- ```ts
30
- const operationKey = crypto.randomUUID(); // retain for this intended operation
31
- await client.run({
32
- url: 'https://example.com/api/items',
33
- intent: 'Create the item requested by the user',
34
- request: { method: 'POST', body: JSON.stringify({ name: 'Example' }) },
35
- }, { headers: { 'Idempotency-Key': operationKey } });
36
- ```
45
+ ## Capabilities and learning
37
46
 
38
- Writes require an explicit method and retained key. The SDK does not retry execution automatically. An uncertain dispatch stays indeterminate; changed content under the same key is rejected. A completed duplicate returns the saved result. These rules do not guarantee exactly-once effects in an external system.
47
+ | Method | Route |
48
+ |---|---|
49
+ | `discover(query)` | `POST /capabilities/search` |
50
+ | `capability(id)`, `skills()` | `GET /capabilities/:id`, `GET /skills` |
51
+ | `learn({ har \| traces, goal?, title? })` | `POST /learn` |
52
+ | `learned(id?)`, `harnessYaml(id)`, `skillMd(id)` | `GET /learned[/:id[/harness.yaml\|/skill.md]]` |
39
53
 
40
- `resolve` requires a target URL and executes through the gateway. `execute` requires explicit URL and method; an old skill ID alone does not authorize a request. Search methods remain free discovery.
54
+ ## Account, logins, vault
41
55
 
42
- ## Payments
56
+ | Method | Route |
57
+ |---|---|
58
+ | `me()`, `usage()` | `GET /me`, `GET /usage` |
59
+ | `logins.list()`, `.save(login)`, `.remove({ origin } \| { ref })` | `/logins` — values in, masked hints out |
60
+ | `accounts.connect({ origin, username, password })`, `accounts.register({ origin, username })` | `/accounts/connections`, `/accounts/register` |
61
+ | `vault()` | `GET /vault` — refs and audit, never secrets |
43
62
 
44
- Execution is free by default. No payment provider is required. Optional server configuration supports x402 Base USDC with Stripe transaction verification, Stripe MPP for Link Agent Wallet, and verified Stripe Billing subscription admission. Link agent payments use MPP; subscriptions use Billing.
63
+ ## Public registry
45
64
 
46
- A payment-required error contains response headers, the complete challenge, the operation key and `retryWithPayment(headers)`. Obtain a proof with an official payment client under explicit spending authority, then pass `PAYMENT-SIGNATURE` or `Authorization: Payment …` to that closure. It preserves request body and operation identity. The retained legacy `payAndRetry` helper does not implement this new protocol.
65
+ | Method | Route |
66
+ |---|---|
67
+ | `sites(query?)`, `site(host)` | `GET /sites`, `GET /sites/:host` (no key) |
68
+ | `openapi(host)` | `GET /sites/:host/openapi.json` |
69
+ | `callTool(host, tool, input)` | `POST /sites/:host/call/:tool` (metered like a run) |
70
+ | `siteMcpUrl(host)` | the site as its own MCP server |
47
71
 
48
- No paid mode is enabled automatically. Live Stripe/Link settlement requires configured provider accounts and independent validation. See [payment setup](../../docs/machine-payments.md).
72
+ ## Errors
49
73
 
50
- ## Browser choices and limits
74
+ Every failure throws `UnbrowseError` with `status`, `code` (the server's, e.g. `quota_exceeded`,
75
+ `unknown_argument`) and `body`.
51
76
 
52
- Server-owned Chromium is the default. Remote Browser Use Cloud is explicit opt-in. `browser: 'disabled'` permits known-route replay but rejects a cache miss.
77
+ ## Install helpers
53
78
 
54
- Current learned routes are single observed JSON reads. Browser form interactions, multi-step DAGs, automatic password login/session refresh, binary downloads and streaming are outside this gateway. Local HTML parsing and research formatting may still run client-side; upstream execution goes through your API.
79
+ `mcpCommands(url)`, `cursorInstallLink(url)`, `vscodeInstallLink(url)` build install commands and
80
+ links for the hosted MCP (`https://unbrowse.ai/mcp`). They never carry a key.
package/dist/client.d.ts CHANGED
@@ -1,190 +1,127 @@
1
- import type { AttributionLedger, AvailableEndpoint, CreatorTransactionsResponse, Dashboard, ExecuteInput, ExecuteResponse, FeedbackInput, FeedbackResponse, HealthResponse, LoginInput, LoginResponse, RequestOptions, ResolveInput, ResolveResponse, SearchDomainInput, SearchInput, SearchResponse, SearchEndpointsInput, SearchEndpointsResponse, SkillManifest, StatsResponse, StealAuthInput, StealAuthResponse, UnbrowseClientOptions, PublishSkillInput, PublishResponse, AnnotateInput, AnnotateResponse, PaymentProvider, PaymentProviderResponse } from "./contracts.js";
2
- import type { ServerRunInput, ServerRunResult, ServerRegistry, ServerPrimitiveCall, ServerPrimitiveSelection, ServerWorkflow, ServerWorkflowResult } from "./server-types.js";
3
- import { type SpawnRuntimeOptions } from "./runtime.js";
4
- export declare function telemetryEnvDisabled(value: string | undefined): boolean;
5
- /** A first visit learns with the server browser for up to 8 minutes; wait longer than the server's deadline. */
6
- export declare const RUN_TIMEOUT_MS = 600000;
7
- /** Collapse an SDK route to a fixed category without retaining path parameters. */
8
- export declare function sdkUsageOperationForPath(path: string, method?: string): string;
1
+ import type { Json, RunRequest, RunView } from "./types.ts";
2
+ export declare const DEFAULT_BASE_URL = "https://unbrowse.ai/api/v1";
3
+ export type UnbrowseOptions = {
4
+ /** API key (`ub_live_…`) or OAuth access token. Defaults to `UNBROWSE_API_KEY`. Public routes need none. */
5
+ apiKey?: string;
6
+ /** Defaults to `UNBROWSE_BASE_URL` (origin or `/api/v1` URL), then https://unbrowse.ai/api/v1. */
7
+ baseUrl?: string;
8
+ fetch?: typeof globalThis.fetch;
9
+ };
10
+ /** An API error: the HTTP status, the server's error code and its body. */
11
+ export declare class UnbrowseError extends Error {
12
+ readonly status: number;
13
+ readonly code: string;
14
+ readonly body?: unknown | undefined;
15
+ constructor(message: string, status: number, code: string, body?: unknown | undefined);
16
+ }
9
17
  export declare class Unbrowse {
10
18
  readonly baseUrl: string;
11
- readonly apiKey?: string;
12
- readonly clientId?: string;
13
- private readonly defaultHeaders?;
14
- private readonly fetchImpl;
15
- private readonly timeoutMs?;
16
- private readonly telemetryEnabled;
17
- private readonly telemetryEndpoint;
18
- private _runtimeHandle;
19
- constructor(options?: UnbrowseClientOptions);
20
- /**
21
- * Build a `params` object from an `AvailableEndpoint.input_params` spec, using
22
- * the `example_value` of each declared key. Skips keys without an example.
23
- */
24
- static paramsFromInputSpec(spec: AvailableEndpoint["input_params"] | undefined): Record<string, unknown>;
25
- /**
26
- * Point at an already-running Unbrowse runtime. No probe, no spawn.
27
- * `.close()` is a no-op for the runtime (the caller owns it).
28
- */
29
- static connect(baseUrl: string, opts?: Omit<UnbrowseClientOptions, "baseUrl">): Promise<Unbrowse>;
30
- /**
31
- * Spawn a co-located Unbrowse runtime and return a client pointed at it.
32
- * The client takes ownership of the child process: `.close()` will tear
33
- * it down.
34
- */
35
- static spawn(opts?: SpawnRuntimeOptions & Omit<UnbrowseClientOptions, "baseUrl">): Promise<Unbrowse>;
36
- /**
37
- * Probe 127.0.0.1 on the requested port (default 6969). Adopts a live
38
- * runtime via `connect`; spawns a new one via `spawn` if dead. The
39
- * resulting client only owns the runtime if it had to spawn.
40
- */
41
- static local(opts?: SpawnRuntimeOptions & Omit<UnbrowseClientOptions, "baseUrl">): Promise<Unbrowse>;
42
- /**
43
- * Release any resources owned by this client. If the client spawned its
44
- * own runtime, this kills the child process. Otherwise no-op.
45
- */
46
- close(): Promise<void>;
47
- private reportUsage;
48
- request<T>(method: string, path: string, body?: unknown, options?: RequestOptions): Promise<T>;
49
- run(input: ServerRunInput, options?: RequestOptions): Promise<ServerRunResult>;
50
- registry(options?: RequestOptions): Promise<ServerRegistry>;
51
- selectPrimitive(goal: string, options?: RequestOptions): Promise<ServerPrimitiveSelection>;
52
- callPrimitive(input: ServerPrimitiveCall, options?: RequestOptions): Promise<ServerRunResult>;
53
- runWorkflow(input: ServerWorkflow, options?: RequestOptions): Promise<ServerWorkflowResult>;
54
- storeCredential(id: string, credential: import("./server-types.js").ServerCredential, options?: RequestOptions): Promise<{
55
- id: string;
56
- origin: string;
57
- storage: "remote";
58
- }>;
59
- deleteCredential(id: string, options?: RequestOptions): Promise<{
60
- deleted: true;
19
+ private apiKey?;
20
+ private fetchImpl;
21
+ constructor(opts?: UnbrowseOptions);
22
+ private send;
23
+ private req;
24
+ private post;
25
+ /**
26
+ * Start a run by capability id or plain-language task. `idempotencyKey` makes retries safe; the route
27
+ * reads it as `idempotency_key` or an `Idempotency-Key` header (camelCase in the body is ignored).
28
+ */
29
+ run(request: RunRequest): Promise<RunView>;
30
+ inspect(runId: string): Promise<RunView>;
31
+ events(runId: string): Promise<{
32
+ events: RunView["events"];
61
33
  }>;
62
- resolve(input: ResolveInput, options?: RequestOptions): Promise<ResolveResponse>;
63
- execute(skill: string | ExecuteInput | ResolveResponse, input?: Omit<ExecuteInput, "skillId">, options?: RequestOptions): Promise<ExecuteResponse>;
64
- getSkill(skillId: string, options?: RequestOptions): Promise<SkillManifest>;
65
- login(input: LoginInput, options?: RequestOptions): Promise<LoginResponse>;
66
- importAuth(input: StealAuthInput, options?: RequestOptions): Promise<StealAuthResponse>;
67
- stealAuth(input: StealAuthInput, options?: RequestOptions): Promise<StealAuthResponse>;
68
- search(input: SearchInput, options?: RequestOptions): Promise<SearchResponse>;
69
- searchDomain(input: SearchDomainInput, options?: RequestOptions): Promise<SearchResponse>;
70
- /**
71
- * Semantic search across ALL indexed endpoints (flat shape).
72
- *
73
- * Returns one row per endpoint, ranked by semantic match against the
74
- * intent string. Use this when you want to pick endpoints across many
75
- * skills (vs `search()` which groups by skill).
76
- *
77
- * Optional `domain` scopes the search to one host's endpoints; omit for
78
- * cross-marketplace search. Authenticated agents are charged the
79
- * standard search fee per the existing x402 economics; anonymous
80
- * callers get free rate-limited public discovery.
81
- *
82
- * @example
83
- * ```ts
84
- * const { endpoints } = await u.searchEndpoints({ intent: "trending repositories", k: 5 });
85
- * for (const ep of endpoints) {
86
- * console.log(ep.score, ep.skill_id, ep.endpoint_id, ep.description);
87
- * }
88
- * ```
89
- */
90
- searchEndpoints(input: SearchEndpointsInput, options?: RequestOptions): Promise<SearchEndpointsResponse>;
91
- feedback(input: FeedbackInput, options?: RequestOptions): Promise<FeedbackResponse>;
92
- stats(options?: RequestOptions): Promise<StatsResponse>;
93
- health(options?: RequestOptions): Promise<HealthResponse>;
94
- /**
95
- * Earnings/spending dashboard for the authenticated agent.
96
- * Requires `apiKey` set on the client. Backend: `GET /v1/dashboard/me`.
97
- */
98
- dashboard(options?: RequestOptions): Promise<Dashboard>;
99
- /**
100
- * Public dashboard for any wallet address. Backend: `GET /v1/dashboard/wallet/:walletAddress`.
101
- */
102
- dashboardByWallet(walletAddress: string, options?: RequestOptions): Promise<Dashboard>;
103
34
  /**
104
- * Per-transaction earnings ledger for a creator agent.
105
- * Backend: `GET /v1/transactions/creator/:agentId`.
106
- */
107
- creatorTransactions(agentId: string, options?: RequestOptions): Promise<CreatorTransactionsResponse>;
108
- /**
109
- * Fund a Flex escrow on the caller's behalf. Thin wrapper around
110
- * `fundEscrow` in `./flex.ts` — requires the caller to pass
111
- * `signer` + `rpc` for v6.16-preview.0 (until SDK ships its own
112
- * @solana/kit pipeline). For pure tx construction without sending,
113
- * import `buildEscrowCreationTx` from the package root.
114
- */
115
- fundEscrow(params: {
116
- amountUsdc: string;
117
- walletAddress?: string;
118
- facilitatorAddress?: string;
119
- mint?: string;
120
- refundTimeoutSlots?: number;
121
- deadmanTimeoutSlots?: number;
122
- signer?: unknown;
123
- rpc?: unknown;
124
- }): Promise<{
125
- escrowAddress: string;
126
- txSignature: string;
127
- }>;
128
- /**
129
- * Register a session key against the caller's existing Flex escrow.
130
- * Thin wrapper around `registerSessionKey` in `./flex.ts`.
131
- */
132
- registerSessionKey(params: {
133
- sessionKeyAddress: string;
134
- walletAddress?: string;
135
- escrowAddress?: string;
136
- expiresAtSlot?: string;
137
- revocationGracePeriodSlots?: number;
138
- signer?: unknown;
139
- rpc?: unknown;
140
- }): Promise<{
141
- txSignature: string;
142
- }>;
143
- /**
144
- * Execute a skill against a Flex-metered route. preview.0/preview.1 stub:
145
- * the backend metered-execute route (`POST /v1/skills/:id/execute` with
146
- * Flex-shaped 402 + usage_units settlement) ships in v6.16.0-preview.2.
147
- * Callers must use `Unbrowse#execute` against fixed-price skills until
148
- * then. The method signature stays for forward compat; the body refuses
149
- * with a precise version-targeted error.
150
- */
151
- executeMetered<T = unknown>(_skillOrId: string | {
152
- skill_id: string;
153
- }, _input: unknown, _opts?: {
154
- onUsage?: (units: number) => void;
155
- wallet?: import("./flex.js").FlexWalletLike;
156
- }, _options?: RequestOptions): Promise<T>;
157
- /**
158
- * Indexer earnings via delta-based attribution.
159
- * Backend: `GET /v1/attribution/indexer/:indexerId`.
160
- */
161
- indexerAttribution(indexerId: string, options?: RequestOptions): Promise<AttributionLedger>;
162
- /**
163
- * Publish a captured skill to the marketplace.
164
- * Backend: `POST /v1/skills`.
165
- *
166
- * SDK gap-fill (Wave 2 of rebuild-the-unbrowse-sdk-as-a-thin-http-first-ty
167
- * + the parity gate from tests/cli-mcp-sdk-parity.test.ts). Mirrors the
168
- * CLI `unbrowse publish` and the MCP `unbrowse_publish` tool.
169
- */
170
- publish(skill: PublishSkillInput, options?: RequestOptions): Promise<PublishResponse>;
171
- /**
172
- * Annotate an endpoint with a tip, constraint, or gotcha.
173
- * Backend: `POST /v1/skills/:id/endpoints/:eid/annotate`.
174
- *
175
- * SDK gap-fill. Mirrors CLI `unbrowse annotate` + MCP `unbrowse_annotate`.
176
- */
177
- annotate(input: AnnotateInput, options?: RequestOptions): Promise<AnnotateResponse>;
178
- /**
179
- * Persist the agent's chosen x402 payment rail on the backend.
180
- * Backend: `POST /v1/account/payment-provider` (shipped in #691).
181
- *
182
- * SDK gap-fill: surface the Wave 2 backend route as a typed SDK
183
- * method so SDK callers can sync provider choice without rolling
184
- * a raw fetch. Allowed values are the same six the route whitelists:
185
- * pay_sh / lobster_cash / external_solana / privy_embedded /
186
- * privy_embedded_solana / skip.
187
- */
188
- paymentProvider(provider: PaymentProvider, options?: RequestOptions): Promise<PaymentProviderResponse>;
35
+ * Answer a run's open requirements. The route reads snake_case (`requirement_id`, `expected_revision`);
36
+ * the documented camelCase (`requirementId`, `expectedRevision`) is converted here.
37
+ */
38
+ resume(runId: string, expectedStateRevision: number, responses: Json[]): Promise<any>;
39
+ /** Answer open requirements by field name — `{ origin: "SIN" }` — and return the updated run. */
40
+ answer(runId: string, answers: Record<string, Json>): Promise<RunView>;
41
+ /** Poll a run until it leaves `accepted`/`working` (or the timeout passes). */
42
+ wait(runId: string, opts?: {
43
+ timeoutMs?: number;
44
+ }): Promise<RunView>;
45
+ cancel(runId: string): Promise<any>;
46
+ /** Your private capabilities first, then the public registry. */
47
+ discover(query: string): Promise<any>;
48
+ capability(id: string): Promise<any>;
49
+ skills(): Promise<any>;
50
+ /** Compile HAR files or traces of a task done by hand into a one-call capability. */
51
+ learn(body: {
52
+ har?: Json;
53
+ traces?: Json[];
54
+ goal?: string;
55
+ title?: string;
56
+ }): Promise<any>;
57
+ learned(id?: string): Promise<any>;
58
+ harnessYaml(id: string): Promise<string>;
59
+ skillMd(id: string): Promise<string>;
60
+ usage(): Promise<any>;
61
+ me(): Promise<any>;
62
+ /**
63
+ * The password manager, for services that keep their own "save your login" page (e.g. Kata): values go in,
64
+ * only masked hints come back. One login per site; saving again for the same origin updates it.
65
+ */
66
+ logins: {
67
+ list: () => Promise<{
68
+ logins: LoginView[];
69
+ }>;
70
+ save: (login: {
71
+ origin: string;
72
+ label?: string;
73
+ username?: string;
74
+ email?: string;
75
+ password?: string;
76
+ totp?: string;
77
+ }) => Promise<{
78
+ login: LoginView;
79
+ updated: boolean;
80
+ }>;
81
+ remove: (by: {
82
+ origin: string;
83
+ } | {
84
+ ref: string;
85
+ }) => Promise<{
86
+ removed: number;
87
+ }>;
88
+ };
89
+ accounts: {
90
+ /** Store a password in the vault; returns a `vault://` ref. */
91
+ connect: (a: {
92
+ origin: string;
93
+ username?: string;
94
+ password?: string;
95
+ label?: string;
96
+ }) => Promise<any>;
97
+ /** Generate and vault a password for a new account. */
98
+ register: (a: {
99
+ origin: string;
100
+ username: string;
101
+ label?: string;
102
+ }) => Promise<any>;
103
+ };
104
+ /** Vault refs and audit, never secrets. */
105
+ vault(): Promise<any>;
106
+ sites(query?: string): Promise<any>;
107
+ site(host: string): Promise<any>;
108
+ openapi(host: string): Promise<any>;
109
+ /** Run one site tool (metered like a run). */
110
+ callTool(host: string, tool: string, input?: Record<string, Json>): Promise<any>;
111
+ /** One site as its own MCP server: its compiled tools, a plain-words task, and the recorded browser on that site. */
112
+ siteMcpUrl(host: string): string;
189
113
  }
190
- //# sourceMappingURL=client.d.ts.map
114
+ export declare function normalizeHost(site: string): string;
115
+ /** A saved login as anyone but the vault sees it: never a value. */
116
+ export type LoginView = {
117
+ ref: string;
118
+ origin: string;
119
+ label?: string;
120
+ hints: {
121
+ username?: string;
122
+ email?: string;
123
+ };
124
+ fields: string[];
125
+ createdAt: number;
126
+ rotatedAt?: number;
127
+ };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,4 @@
1
- export { Unbrowse } from "./client.js";
2
- export type { ServerRunInput, ServerRunResult, ServerCredential, ServerSessionCookie, ServerPrimitive, ServerRegistry, ServerPrimitiveCall, ServerPrimitiveSelection, ServerWorkflowNode, ServerWorkflow, ServerWorkflowResult } from "./server-types.js";
3
- export { PaymentRequiredError, RuntimeUnavailableError, SponsorExhaustedError, UnbrowseApiError, } from "./errors.js";
4
- export { locateUnbrowseBinary, probeUnbrowseRuntime, spawnUnbrowseRuntime, } from "./runtime.js";
5
- export type { RuntimeHandle, SpawnRuntimeOptions } from "./runtime.js";
6
- export { payAndRetry } from "./x402.js";
7
- export type { WalletLike, X402PaymentPayload, X402PaymentRequirement, } from "./x402.js";
8
- export { buildEscrowCreationTx, buildFlexAuthorization, buildSessionKeyRegistrationTx, fundEscrow, payAndRetryFlex, registerSessionKey, setupDelegation, USDC_MINT_DEVNET, USDC_MINT_MAINNET, } from "./flex.js";
9
- export type { BuiltFlexTx, DelegationClientLike, DelegationSessionKeyResponse, FlexAuthorization, FlexFundEscrowParams, FlexRegisterSessionKeyParams, FlexWalletLike, SetupDelegationParams, SetupDelegationResult, TransactionSignerOpaque, } from "./flex.js";
10
- export type { AttributionLedger, AvailableEndpoint, CreatorTransaction, CreatorTransactionsResponse, Dashboard, DashboardContributions, DashboardEarnings, DashboardSpending, EndpointAnnotation, EndpointConstraint, EndpointDescriptor, ExecuteInput, ExecuteResponse, ExecutionTrace, FeedbackInput, FeedbackResponse, HealthResponse, LoginInput, LoginResponse, OrchestrationTiming, ProjectionOptions, RequestOptions, ResolveInput, ResolveResponse, ResponseSchema, SearchDomainInput, SearchHit, SearchInput, SearchResponse, SearchEndpointsInput, SearchEndpointsResponse, EndpointSearchHit, SkillManifest, StatsResponse, StealAuthInput, StealAuthResponse, UnbrowseClientOptions, WalletInfo, } from "./contracts.js";
11
- //# sourceMappingURL=index.d.ts.map
1
+ export { DEFAULT_BASE_URL, Unbrowse, UnbrowseError, normalizeHost } from "./client.ts";
2
+ export type { LoginView, UnbrowseOptions } from "./client.ts";
3
+ export type * from "./types.ts";
4
+ export { cursorInstallLink, mcpCommands, vscodeInstallLink } from "./mcp-install.ts";
package/dist/index.js CHANGED
@@ -1,6 +1,186 @@
1
- export { Unbrowse } from "./client.js";
2
- export { PaymentRequiredError, RuntimeUnavailableError, SponsorExhaustedError, UnbrowseApiError, } from "./errors.js";
3
- export { locateUnbrowseBinary, probeUnbrowseRuntime, spawnUnbrowseRuntime, } from "./runtime.js";
4
- export { payAndRetry } from "./x402.js";
5
- export { buildEscrowCreationTx, buildFlexAuthorization, buildSessionKeyRegistrationTx, fundEscrow, payAndRetryFlex, registerSessionKey, setupDelegation, USDC_MINT_DEVNET, USDC_MINT_MAINNET, } from "./flex.js";
6
- //# sourceMappingURL=index.js.map
1
+ // src/client.ts
2
+ var DEFAULT_BASE_URL = "https://unbrowse.ai/api/v1";
3
+
4
+ class UnbrowseError extends Error {
5
+ status;
6
+ code;
7
+ body;
8
+ constructor(message, status, code, body) {
9
+ super(message);
10
+ this.status = status;
11
+ this.code = code;
12
+ this.body = body;
13
+ this.name = "UnbrowseError";
14
+ }
15
+ }
16
+ var env = (name) => (typeof process !== "undefined" ? process.env?.[name] : undefined) || undefined;
17
+ function apiBase(url) {
18
+ const u = url.replace(/\/+$/, "");
19
+ return /\/api\/v1$/.test(u) ? u : `${u}/api/v1`;
20
+ }
21
+
22
+ class Unbrowse {
23
+ baseUrl;
24
+ apiKey;
25
+ fetchImpl;
26
+ constructor(opts = {}) {
27
+ this.baseUrl = apiBase(opts.baseUrl ?? env("UNBROWSE_BASE_URL") ?? DEFAULT_BASE_URL);
28
+ this.apiKey = opts.apiKey ?? env("UNBROWSE_API_KEY");
29
+ this.fetchImpl = opts.fetch ?? ((...a) => globalThis.fetch(...a));
30
+ }
31
+ async send(path, init) {
32
+ const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
33
+ ...init,
34
+ headers: {
35
+ ...this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {},
36
+ "content-type": "application/json",
37
+ ...init?.headers ?? {}
38
+ }
39
+ });
40
+ if (!res.ok) {
41
+ const body = await res.json().catch(() => ({}));
42
+ const err = typeof body.error === "object" ? body.error : undefined;
43
+ throw new UnbrowseError(err?.message ?? body.error_description ?? res.statusText, res.status, err?.code ?? (typeof body.error === "string" ? body.error : "http_error"), body);
44
+ }
45
+ return res;
46
+ }
47
+ async req(path, init) {
48
+ return (await this.send(path, init)).json();
49
+ }
50
+ post(path, body) {
51
+ return this.req(path, { method: "POST", body: JSON.stringify(body) });
52
+ }
53
+ run(request) {
54
+ const { idempotencyKey, ...rest } = request;
55
+ return this.req("/runs", {
56
+ method: "POST",
57
+ body: JSON.stringify(idempotencyKey ? { ...rest, idempotency_key: idempotencyKey } : rest),
58
+ ...idempotencyKey ? { headers: { "idempotency-key": idempotencyKey } } : {}
59
+ });
60
+ }
61
+ inspect(runId) {
62
+ return this.req(`/runs/${runId}`);
63
+ }
64
+ events(runId) {
65
+ return this.req(`/runs/${runId}/events`);
66
+ }
67
+ resume(runId, expectedStateRevision, responses) {
68
+ const wire = responses.map((r) => {
69
+ const x = r ?? {};
70
+ return {
71
+ requirement_id: x.requirement_id ?? x.requirementId,
72
+ expected_revision: x.expected_revision ?? x.expectedRevision,
73
+ action: x.action ?? "accept",
74
+ ...x.values !== undefined ? { values: x.values } : {}
75
+ };
76
+ });
77
+ return this.post(`/runs/${runId}/responses`, { expected_state_revision: expectedStateRevision, responses: wire });
78
+ }
79
+ async answer(runId, answers) {
80
+ const view = await this.inspect(runId);
81
+ const open = view.requirements.filter((r) => r.state === "open");
82
+ const responses = Object.entries(answers).map(([field, value]) => {
83
+ const req = open.find((r) => r.affectedAction === field || r.id === field);
84
+ if (!req)
85
+ throw new UnbrowseError(`${field} is not an open requirement. Open: ${open.map((r) => r.affectedAction).join(", ") || "none"}`, 422, "invalid_answer");
86
+ return { requirementId: req.id, expectedRevision: req.revision, action: "accept", values: { [req.affectedAction]: value } };
87
+ });
88
+ await this.resume(runId, view.stateRevision, responses);
89
+ return this.inspect(runId);
90
+ }
91
+ async wait(runId, opts = {}) {
92
+ const deadline = Date.now() + (opts.timeoutMs ?? 600000);
93
+ let view = await this.inspect(runId);
94
+ for (let delay = 500;(view.status === "accepted" || view.status === "working") && Date.now() < deadline; delay = Math.min(delay * 2, 5000)) {
95
+ await new Promise((r) => setTimeout(r, delay));
96
+ view = await this.inspect(runId);
97
+ }
98
+ return view;
99
+ }
100
+ cancel(runId) {
101
+ return this.req(`/runs/${runId}/cancel`, { method: "POST" });
102
+ }
103
+ discover(query) {
104
+ return this.post("/capabilities/search", { query });
105
+ }
106
+ capability(id) {
107
+ return this.req(`/capabilities/${encodeURIComponent(id)}`);
108
+ }
109
+ skills() {
110
+ return this.req("/skills");
111
+ }
112
+ learn(body) {
113
+ return this.post("/learn", body);
114
+ }
115
+ learned(id) {
116
+ return this.req(id ? `/learned/${encodeURIComponent(id)}` : "/learned");
117
+ }
118
+ async harnessYaml(id) {
119
+ return (await this.send(`/learned/${encodeURIComponent(id)}/harness.yaml`)).text();
120
+ }
121
+ async skillMd(id) {
122
+ return (await this.send(`/learned/${encodeURIComponent(id)}/skill.md`)).text();
123
+ }
124
+ usage() {
125
+ return this.req("/usage");
126
+ }
127
+ me() {
128
+ return this.req("/me");
129
+ }
130
+ logins = {
131
+ list: () => this.req("/logins"),
132
+ save: (login) => this.post("/logins", login),
133
+ remove: (by) => this.post("/logins/remove", by)
134
+ };
135
+ accounts = {
136
+ connect: (a) => this.post("/accounts/connections", a),
137
+ register: (a) => this.post("/accounts/register", a)
138
+ };
139
+ vault() {
140
+ return this.req("/vault");
141
+ }
142
+ sites(query = "") {
143
+ return this.req(`/sites${query ? `?q=${encodeURIComponent(query)}` : ""}`);
144
+ }
145
+ site(host) {
146
+ return this.req(`/sites/${normalizeHost(host)}`);
147
+ }
148
+ openapi(host) {
149
+ return this.req(`/sites/${normalizeHost(host)}/openapi.json`);
150
+ }
151
+ callTool(host, tool, input = {}) {
152
+ return this.post(`/sites/${normalizeHost(host)}/call/${encodeURIComponent(tool)}`, input);
153
+ }
154
+ siteMcpUrl(host) {
155
+ return `${this.baseUrl}/sites/${normalizeHost(host)}/mcp`;
156
+ }
157
+ }
158
+ function normalizeHost(site) {
159
+ let host = site.trim().toLowerCase();
160
+ if (/^[a-z]+:\/\//.test(host))
161
+ host = new URL(host).hostname;
162
+ return host.replace(/\/.*$/, "").replace(/^www\./, "");
163
+ }
164
+ // src/mcp-install.ts
165
+ function cursorInstallLink(url) {
166
+ return `cursor://anysphere.cursor-deeplink/mcp/install?name=unbrowse&config=${encodeURIComponent(btoa(JSON.stringify({ url })))}`;
167
+ }
168
+ function vscodeInstallLink(url, insiders = false) {
169
+ return `${insiders ? "vscode-insiders" : "vscode"}:mcp/install?${encodeURIComponent(JSON.stringify({ name: "unbrowse", type: "http", url }))}`;
170
+ }
171
+ function mcpCommands(url) {
172
+ return {
173
+ claudeCode: `claude mcp add --transport http unbrowse ${url}`,
174
+ codex: `codex mcp add unbrowse --url ${url} && codex mcp login unbrowse`,
175
+ json: JSON.stringify({ mcpServers: { unbrowse: { url } } }, null, 2)
176
+ };
177
+ }
178
+ export {
179
+ DEFAULT_BASE_URL,
180
+ Unbrowse,
181
+ UnbrowseError,
182
+ cursorInstallLink,
183
+ mcpCommands,
184
+ normalizeHost,
185
+ vscodeInstallLink
186
+ };