@specstream/sdk 1.2.0 → 1.3.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,6 +1,8 @@
1
1
  # @specstream/sdk
2
2
 
3
- TypeScript client for [SpecStream](https://specstream.ai), the live web data engine for teams and agents. Describe the list you need in plain words; SpecStream reads the web where those things live, hands back only the matches, each with the page it came from, and you pay for each result delivered.
3
+ TypeScript client for [SpecStream](https://specstream.ai), the live web data engine for teams and agents. Describe the list you need in plain words; SpecStream reads the web where those things live, hands back only the matches, and you pay for each result delivered.
4
+
5
+ Source URL and read time on every value.
4
6
 
5
7
  The client has no runtime dependencies and needs Node 22 or newer. It uses the same account, permissions, projects and balance as the workspace.
6
8
 
@@ -10,6 +12,29 @@ npm install @specstream/sdk
10
12
 
11
13
  Create an API key under **Agents & API** in your SpecStream workspace and keep it in the environment: `SPECSTREAM_API_KEY`.
12
14
 
15
+ ## Search API and Extract API
16
+
17
+ SpecStream has three products: Search API, Extract API and Deep Search. The first two take one call at a flat price, with no quote or approval. The sections after this one cover Deep Search.
18
+
19
+ `client.webSearch` calls the [Search API](https://specstream.ai/docs/search-api), `GET /v1/web-search`, at $5 per 1,000 searches. It is the most accurate web search API in the world ([benchmarks](https://specstream.ai/benchmarks)) and answers from the live web in a few seconds. Write `q` in everyday words, in any language.
20
+
21
+ `client.extractPages` calls the [Extract API](https://specstream.ai/docs/extract-api), `POST /v1/extract`, at $0.80 per 1,000 pages. It takes up to 20 URLs a call, reads web pages, PDF, Word and Excel, and returns each one's main text as `markdown` (the default) or `text`. A page with no text is free.
22
+
23
+ ```ts
24
+ import { Client } from "@specstream/sdk";
25
+
26
+ const client = new Client({ apiKey: process.env.SPECSTREAM_API_KEY! });
27
+
28
+ const search = await client.webSearch({ q: "What is the latest stable release of PostgreSQL?" });
29
+ console.log(search.answer?.value, search.answer?.page);
30
+
31
+ const extract = await client.extractPages({
32
+ urls: search.sources.slice(0, 5).map((source) => source.url),
33
+ format: "markdown",
34
+ });
35
+ for (const page of extract.results) console.log(page.url, page.status, page.text);
36
+ ```
37
+
13
38
  ## Read a request, get the price
14
39
 
15
40
  ```ts
@@ -59,7 +84,7 @@ If the response is lost, `getCapacityRequest(savedRequestKey)` returns the same
59
84
 
60
85
  ## Pricing
61
86
 
62
- New accounts start with $10 of work for 30 days, no card. A delivered result costs 10 cents, 30 cents or $1 depending on how many facts it carries. No result, no charge. See [pricing](https://specstream.ai/pricing).
87
+ New accounts start with $10 of credit for 30 days, no card. The Search API costs $5 per 1,000 searches and the Extract API $0.80 per 1,000 pages. In Deep Search, a delivered result costs 5 cents, 15 cents or $1 depending on how many facts it carries. No result, no charge. See [pricing](https://specstream.ai/pricing).
63
88
 
64
89
  ## Links
65
90
 
package/dist/index.d.ts CHANGED
@@ -55,6 +55,17 @@ export declare class Client {
55
55
  "limit"?: number;
56
56
  "cursor"?: string;
57
57
  }): Promise<Schemas["WorkspaceSearchPage"]>;
58
+ webSearch(query: {
59
+ "q": string;
60
+ "passages"?: boolean;
61
+ }): Promise<Schemas["WebSearchPage"]>;
62
+ extractPages(request: Schemas["ExtractRequest"]): Promise<Schemas["ExtractPage"]>;
63
+ webSearchAnswers(query?: {
64
+ "q"?: string;
65
+ "limit"?: number;
66
+ "cursor"?: string;
67
+ }): Promise<Schemas["WebSearchAnswerPage"]>;
68
+ webSearchAnswer(answerId: string): Promise<Schemas["WebSearchAnswer"]>;
58
69
  workspaceListNotifications(query?: {
59
70
  "tab"?: "all" | "unread" | "archived";
60
71
  "limit"?: number;
@@ -152,9 +163,20 @@ export declare class Client {
152
163
  "project_id"?: string;
153
164
  "cursor"?: string;
154
165
  "limit"?: number;
155
- }): Promise<{
166
+ "format"?: "json" | "markdown" | "csv";
167
+ }): Promise<({
156
168
  [key: string]: unknown;
157
- }>;
169
+ } | {
170
+ "format": "markdown" | "csv";
171
+ "columns": Array<{
172
+ "column_id": string;
173
+ "label": string;
174
+ }>;
175
+ "table": string;
176
+ "find_id"?: (string | null);
177
+ "accepted_count"?: number;
178
+ "next_cursor"?: (string | null);
179
+ })>;
158
180
  getCapacityExecution(findId: string): Promise<Schemas["CommerceData"]>;
159
181
  createCapacityFunding(request: Schemas["CapacityFundingRequest"]): Promise<Schemas["CapacityFundingData"]>;
160
182
  getCapacityFunding(fundingId: string): Promise<Schemas["CapacityFundingData"]>;
@@ -280,6 +302,7 @@ export declare class Client {
280
302
  "field_id"?: string;
281
303
  }): Promise<Schemas["RelationshipData"]>;
282
304
  workspaceSaveRelationships(collectionId: string, request: Schemas["RelationshipSelection"]): Promise<Schemas["RelationshipData"]>;
305
+ workspaceShareCollection(collectionId: string, request: Schemas["CollectionShareInput"]): Promise<Schemas["CollectionShareData"]>;
283
306
  workspaceReportResult(collectionId: string, request: Schemas["ResultReportInput"]): Promise<Schemas["ResultReportData"]>;
284
307
  listRequestTemplates(query?: {
285
308
  "record_type"?: string;
package/dist/index.js CHANGED
@@ -56,6 +56,17 @@ export function assertFamily(entityType) {
56
56
  }
57
57
  return entityType;
58
58
  }
59
+ const RETRY_AFTER_MAX_MS = 10_000;
60
+ /** Milliseconds to wait before the one retry of a 503 that names its Retry-After, or null. */
61
+ function retryAfterMs(response) {
62
+ if (response.status !== 503)
63
+ return null;
64
+ const value = (response.headers?.get("retry-after") || "").trim();
65
+ if (!value)
66
+ return null;
67
+ const ms = /^\d+(\.\d+)?$/.test(value) ? Number(value) * 1000 : Date.parse(value) - Date.now();
68
+ return Number.isFinite(ms) && ms >= 0 && ms <= RETRY_AFTER_MAX_MS ? ms : null;
69
+ }
59
70
  export class Client {
60
71
  apiKey;
61
72
  baseUrl;
@@ -220,6 +231,34 @@ export class Client {
220
231
  path += "?" + queryString.toString();
221
232
  return this.request("GET", path);
222
233
  }
234
+ webSearch(query) {
235
+ let path = `/v1/web-search`;
236
+ const queryString = new URLSearchParams();
237
+ for (const [key, value] of Object.entries(query))
238
+ if (value !== undefined && value !== null)
239
+ queryString.set(key, String(value));
240
+ if (queryString.size)
241
+ path += "?" + queryString.toString();
242
+ return this.request("GET", path);
243
+ }
244
+ extractPages(request) {
245
+ let path = `/v1/extract`;
246
+ return this.request("POST", path, request);
247
+ }
248
+ webSearchAnswers(query = {}) {
249
+ let path = `/v1/web-search/answers`;
250
+ const queryString = new URLSearchParams();
251
+ for (const [key, value] of Object.entries(query))
252
+ if (value !== undefined && value !== null)
253
+ queryString.set(key, String(value));
254
+ if (queryString.size)
255
+ path += "?" + queryString.toString();
256
+ return this.request("GET", path);
257
+ }
258
+ webSearchAnswer(answerId) {
259
+ let path = `/v1/web-search/answers/${encodeURIComponent(answerId)}`;
260
+ return this.request("GET", path);
261
+ }
223
262
  workspaceListNotifications(query = {}) {
224
263
  let path = `/v1/workspace/notifications`;
225
264
  const queryString = new URLSearchParams();
@@ -805,6 +844,10 @@ export class Client {
805
844
  let path = `/v1/workspace/collections/${encodeURIComponent(collectionId)}/relationships`;
806
845
  return this.request("PUT", path, request);
807
846
  }
847
+ workspaceShareCollection(collectionId, request) {
848
+ let path = `/v1/workspace/collections/${encodeURIComponent(collectionId)}/share`;
849
+ return this.request("POST", path, request);
850
+ }
808
851
  workspaceReportResult(collectionId, request) {
809
852
  let path = `/v1/workspace/collections/${encodeURIComponent(collectionId)}/reports`;
810
853
  return this.request("POST", path, request);
@@ -980,21 +1023,29 @@ export class Client {
980
1023
  Authorization: token,
981
1024
  Accept: "application/json",
982
1025
  "Content-Type": "application/json",
983
- "User-Agent": "specstream-typescript/1.2.0",
1026
+ "User-Agent": "specstream-typescript/1.3.2",
984
1027
  };
985
1028
  }
986
1029
  async request(method, path, body) {
987
1030
  const url = this.baseUrl + path;
1031
+ const send = () => this.fetchFn(url, {
1032
+ method,
1033
+ headers: this.headers(),
1034
+ redirect: "error",
1035
+ signal: AbortSignal.timeout(this.timeoutMs),
1036
+ body: body === undefined ? undefined : JSON.stringify(body),
1037
+ });
988
1038
  let response;
989
1039
  let text;
990
1040
  try {
991
- response = await this.fetchFn(url, {
992
- method,
993
- headers: this.headers(),
994
- redirect: "error",
995
- signal: AbortSignal.timeout(this.timeoutMs),
996
- body: body === undefined ? undefined : JSON.stringify(body),
997
- });
1041
+ response = await send();
1042
+ const wait = retryAfterMs(response);
1043
+ if (wait !== null) {
1044
+ // The server did nothing and names when to ask again: ask once more.
1045
+ await response.body?.cancel();
1046
+ await new Promise((resolve) => setTimeout(resolve, wait));
1047
+ response = await send();
1048
+ }
998
1049
  text = await response.text();
999
1050
  }
1000
1051
  catch (error) {
@@ -6,6 +6,7 @@ export interface Schemas {
6
6
  "worded_evidence"?: ({
7
7
  [key: string]: unknown;
8
8
  } | null);
9
+ "unconfirmed"?: (Array<string> | null);
9
10
  "lineage"?: (Schemas["MatchLineage"] | null);
10
11
  "spec_version"?: "accepted_row.v1";
11
12
  "entity_id": string;
@@ -267,6 +268,7 @@ export interface Schemas {
267
268
  "share_percent": number;
268
269
  "months": number;
269
270
  "clearing_days": number;
271
+ "yearly_clearing_days": number;
270
272
  "pending_cents": number;
271
273
  "payable_cents": number;
272
274
  "paid_cents": number;
@@ -456,6 +458,20 @@ export interface Schemas {
456
458
  "source_collection_ids"?: (Array<string> | null);
457
459
  "bbox"?: (Array<unknown> | null);
458
460
  };
461
+ "CollectionShareData": {
462
+ "id": string;
463
+ "snapshot_id": string;
464
+ "public_url": string;
465
+ "created_at": string;
466
+ "expires_at"?: (string | null);
467
+ "revoked": boolean;
468
+ "allow_export": boolean;
469
+ "row_count": number;
470
+ };
471
+ "CollectionShareInput": {
472
+ "expires_days"?: (number | null);
473
+ "allow_export"?: boolean;
474
+ };
459
475
  "CombineCollectionsInput": {
460
476
  "request_id": string;
461
477
  "name": string;
@@ -505,6 +521,12 @@ export interface Schemas {
505
521
  "name": string;
506
522
  "project_id"?: (string | null);
507
523
  };
524
+ "CoreEntity": {
525
+ "name": string;
526
+ "url": string;
527
+ "description": string;
528
+ "request": string;
529
+ };
508
530
  "CountComparison": {
509
531
  "op": "gte" | "gt" | "lte" | "lt" | "eq" | "ne";
510
532
  "value": number;
@@ -649,7 +671,7 @@ export interface Schemas {
649
671
  [key: string]: unknown;
650
672
  };
651
673
  };
652
- "ErrorCode": "unsupported_query" | "unsupported_predicate" | "unsupported_entity_family" | "ambiguous_geo" | "capability_pending" | "source_blocked" | "challenge_page" | "unquoted_plan" | "compiler_unavailable" | "parse_failed" | "payments_not_live" | "capacity" | "kill_switch" | "internal_failure" | "plan_required" | "invalid_token";
674
+ "ErrorCode": "unsupported_query" | "unsupported_predicate" | "unsupported_entity_family" | "ambiguous_geo" | "capability_pending" | "source_blocked" | "challenge_page" | "unquoted_plan" | "missing_api_key" | "owner_only" | "compiler_unavailable" | "parse_failed" | "payments_not_live" | "capacity" | "kill_switch" | "internal_failure" | "plan_required" | "invalid_token" | "insufficient_balance";
653
675
  "ErrorEnvelope": {
654
676
  "error": Schemas["ErrorBody"];
655
677
  };
@@ -691,6 +713,25 @@ export interface Schemas {
691
713
  "ExportRequest": {
692
714
  "format": Schemas["ExportFormat"];
693
715
  };
716
+ "ExtractPage": {
717
+ "request_id": string;
718
+ "results": Array<Schemas["ExtractedPage"]>;
719
+ "took_ms": number;
720
+ "price_usd": number;
721
+ };
722
+ "ExtractRequest": {
723
+ "urls": Array<string>;
724
+ "format"?: "markdown" | "text";
725
+ };
726
+ "ExtractedPage": {
727
+ "url": string;
728
+ "status": "ok" | "blocked" | "not_found" | "empty" | "unsupported" | "invalid_url" | "failed";
729
+ "title": (string | null);
730
+ "text": string;
731
+ "published": (string | null);
732
+ "read_from"?: ("live" | "site" | "archive" | null);
733
+ "captured_at"?: (string | null);
734
+ };
694
735
  "FieldObservation": {
695
736
  "entity_id": string;
696
737
  "field_id": string;
@@ -1185,6 +1226,7 @@ export interface Schemas {
1185
1226
  };
1186
1227
  "ParseResult": {
1187
1228
  "retained"?: (Schemas["RetainedRequest"] | null);
1229
+ "core_entity"?: (Schemas["CoreEntity"] | null);
1188
1230
  "grading"?: (Schemas["ReadingGrade"] | null);
1189
1231
  "understanding"?: (Schemas["Understanding"] | null);
1190
1232
  "decision_id"?: (string | null);
@@ -2218,6 +2260,126 @@ export interface Schemas {
2218
2260
  "expirationTime"?: ((number) | null);
2219
2261
  "name": string;
2220
2262
  };
2263
+ "WebSearchAnswer": {
2264
+ "id"?: (string | null);
2265
+ "request_id": string;
2266
+ "query": string;
2267
+ "kind": "list" | "fact";
2268
+ "columns": Array<string>;
2269
+ "answer": (Schemas["WebSearchValue"] | null);
2270
+ "rows": Array<Schemas["WebSearchRow"]>;
2271
+ "sources": Array<Schemas["WebSearchSource"]>;
2272
+ "handoff": (Schemas["WebSearchHandoff"] | null);
2273
+ "took_ms": number;
2274
+ "price_usd": number;
2275
+ "passages"?: (Array<Schemas["WebSearchPassage"]> | null);
2276
+ "request": string;
2277
+ "created_at": string;
2278
+ };
2279
+ "WebSearchAnswerPage": {
2280
+ "results": Array<Schemas["WebSearchAnswerSummary"]>;
2281
+ "next_cursor": (string | null);
2282
+ };
2283
+ "WebSearchAnswerSummary": {
2284
+ "id": string;
2285
+ "request": string;
2286
+ "created_at": string;
2287
+ "rows": number;
2288
+ "answer": (string | null);
2289
+ "source"?: (string | null);
2290
+ "sources"?: Array<Schemas["WebSearchCited"]>;
2291
+ "list": boolean;
2292
+ "kind": "list" | "fact";
2293
+ };
2294
+ "WebSearchBatchPage": {
2295
+ "results": Array<Schemas["WebSearchBatchResult"]>;
2296
+ "took_ms": number;
2297
+ "price_usd": number;
2298
+ };
2299
+ "WebSearchBatchRequest": {
2300
+ "questions": Array<Schemas["WebSearchQuestion"]>;
2301
+ };
2302
+ "WebSearchBatchResult": {
2303
+ "request_id": string;
2304
+ "status": number;
2305
+ "data": (Schemas["WebSearchPage"] | null);
2306
+ "error": (Schemas["ErrorBody"] | null);
2307
+ "retry_after_s"?: (number | null);
2308
+ };
2309
+ "WebSearchCall": {
2310
+ "method": "POST";
2311
+ "path": "/v1/parse";
2312
+ "body": {
2313
+ [key: string]: unknown;
2314
+ };
2315
+ };
2316
+ "WebSearchCited": {
2317
+ "url": string;
2318
+ "title": string;
2319
+ "site": string;
2320
+ };
2321
+ "WebSearchHandoff": {
2322
+ "line": string;
2323
+ "request": string;
2324
+ "rows_estimated": number;
2325
+ "rows_basis": "request" | "page" | "seen";
2326
+ "count_page": (string | null);
2327
+ "next": Schemas["WebSearchNext"];
2328
+ };
2329
+ "WebSearchNext": {
2330
+ "tool": "compile";
2331
+ "arguments": {
2332
+ [key: string]: unknown;
2333
+ };
2334
+ "http": Schemas["WebSearchCall"];
2335
+ };
2336
+ "WebSearchPage": {
2337
+ "id"?: (string | null);
2338
+ "request_id": string;
2339
+ "query": string;
2340
+ "kind": "list" | "fact";
2341
+ "columns": Array<string>;
2342
+ "answer": (Schemas["WebSearchValue"] | null);
2343
+ "rows": Array<Schemas["WebSearchRow"]>;
2344
+ "sources": Array<Schemas["WebSearchSource"]>;
2345
+ "handoff": (Schemas["WebSearchHandoff"] | null);
2346
+ "took_ms": number;
2347
+ "price_usd": number;
2348
+ "passages"?: (Array<Schemas["WebSearchPassage"]> | null);
2349
+ };
2350
+ "WebSearchPassage": {
2351
+ "url": string;
2352
+ "title": string;
2353
+ "text": string;
2354
+ };
2355
+ "WebSearchQuestion": {
2356
+ "q": string;
2357
+ "id"?: (string | null);
2358
+ };
2359
+ "WebSearchRow": {
2360
+ "name": string;
2361
+ "page": string;
2362
+ "snippet": string;
2363
+ "probability": number;
2364
+ "fields": {
2365
+ [key: string]: Schemas["WebSearchValue"];
2366
+ };
2367
+ "sources"?: Array<number>;
2368
+ };
2369
+ "WebSearchSource": {
2370
+ "url": string;
2371
+ "title": string;
2372
+ "site": string;
2373
+ "snippet": string;
2374
+ "cited": boolean;
2375
+ "published"?: (string | null);
2376
+ };
2377
+ "WebSearchValue": {
2378
+ "value": string;
2379
+ "page": string;
2380
+ "seen_at"?: (string | null);
2381
+ "confirmed"?: (boolean | null);
2382
+ };
2221
2383
  "WebSourceCell": {
2222
2384
  "source_url": string;
2223
2385
  "response_sha256": string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specstream/sdk",
3
- "version": "1.2.0",
3
+ "version": "1.3.2",
4
4
  "description": "TypeScript client for building lead lists and company lists from the live web. Every match comes with its source, priced before it runs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",