orangeslice 2.1.0 → 2.1.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
@@ -64,11 +64,21 @@ const [companies, searchPage, ai] = await Promise.all([
64
64
  }
65
65
  })
66
66
  ]);
67
+
68
+ const startups = await services.crunchbase.search({
69
+ sql: `
70
+ SELECT name, website_url, linkedin_url
71
+ FROM public.crunchbase_scraper_lean
72
+ WHERE operating_status = 'Active'
73
+ LIMIT 10
74
+ `
75
+ });
67
76
  ```
68
77
 
69
78
  ## Service map
70
79
 
71
80
  - `services.company.linkedin.search/enrich`
81
+ - `services.crunchbase.search` (returns rows array directly)
72
82
  - `services.company.getEmployeesFromLinkedin` (database-only B2B path)
73
83
  - `services.person.linkedin.search/enrich`
74
84
  - `services.web.search/batchSearch`
package/dist/cli.js CHANGED
@@ -100,6 +100,19 @@ Use these docs as the source of truth. If there is any conflict between your pri
100
100
  - Be concise, factual, and deterministic.
101
101
  - Ask a clarifying question only when a missing detail blocks progress.
102
102
 
103
+ ## Package Setup (Do Not Guess)
104
+ - Import from the package name, not a local file path:
105
+ - \`import { services } from "orangeslice"\`
106
+ - \`import { configure, services } from "orangeslice"\` when setting API key programmatically
107
+ - Do NOT use \`import { services } from "./orangeslice"\` unless the user explicitly has a local wrapper file at that path.
108
+ - \`npx orangeslice\` is a setup/bootstrap command (docs sync, package install, auth). It does NOT execute user app scripts.
109
+
110
+ ## Runtime Requirements
111
+ - If writing standalone scripts that use top-level \`await\`, use ESM:
112
+ - Set \`"type": "module"\` in \`package.json\`, or
113
+ - Use \`.mjs\` files.
114
+ - If the project is CommonJS and cannot switch to ESM, avoid top-level \`await\` and wrap async code in an async function.
115
+
103
116
  ## Mandatory Read Order (Before writing code)
104
117
  1. \`./services/index.md\` - service map and capabilities
105
118
  2. Relevant docs under \`./services/**\` for every service you plan to call
@@ -304,6 +317,7 @@ function printHelp() {
304
317
  console.log(" npx orangeslice");
305
318
  console.log(" npx orangeslice login [--force]");
306
319
  console.log(" npx orangeslice logout");
320
+ console.log(" npx orangeslice auth <API_KEY>");
307
321
  console.log(" npx orangeslice auth status\n");
308
322
  }
309
323
  async function runLogin(args) {
@@ -322,6 +336,15 @@ function runLogout() {
322
336
  console.log(" Note: ORANGESLICE_API_KEY env var is still set in this shell.");
323
337
  }
324
338
  }
339
+ function runAuthSet(apiKey) {
340
+ const trimmed = apiKey.trim();
341
+ if (!trimmed) {
342
+ console.log(" Usage: npx orangeslice auth <API_KEY>");
343
+ return;
344
+ }
345
+ saveConfigFile({ apiKey: trimmed });
346
+ console.log(` ✓ API key saved to ${CONFIG_PATH} (${maskKey(trimmed)})`);
347
+ }
325
348
  function runAuthStatus() {
326
349
  const envKey = process.env.ORANGESLICE_API_KEY?.trim() || "";
327
350
  if (envKey) {
@@ -346,8 +369,17 @@ async function main() {
346
369
  runLogout();
347
370
  return;
348
371
  }
349
- if (command === "auth" && args[1] === "status") {
350
- runAuthStatus();
372
+ if (command === "auth") {
373
+ const subcommand = args[1];
374
+ if (subcommand === "status") {
375
+ runAuthStatus();
376
+ return;
377
+ }
378
+ if (subcommand && subcommand !== "--help" && subcommand !== "-h") {
379
+ runAuthSet(subcommand);
380
+ return;
381
+ }
382
+ printHelp();
351
383
  return;
352
384
  }
353
385
  if (command && (command === "--help" || command === "-h" || command === "help")) {
@@ -0,0 +1,10 @@
1
+ export interface CrunchbaseSearchParams {
2
+ sql: string;
3
+ userId?: string;
4
+ }
5
+ /**
6
+ * Search the Crunchbase lean table using SQL.
7
+ *
8
+ * Returns rows directly (no envelope).
9
+ */
10
+ export declare function crunchbaseSearch<T = Record<string, unknown>>(params: CrunchbaseSearchParams): Promise<T[]>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.crunchbaseSearch = crunchbaseSearch;
4
+ const api_1 = require("./api");
5
+ /**
6
+ * Search the Crunchbase lean table using SQL.
7
+ *
8
+ * Returns rows directly (no envelope).
9
+ */
10
+ async function crunchbaseSearch(params) {
11
+ const data = await (0, api_1.post)("/execute/crunchbase-sql", { sql: params.sql });
12
+ return data.rows ?? [];
13
+ }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { configure } from "./api";
2
2
  export type { OrangesliceConfig } from "./api";
3
3
  export { linkedinSearch } from "./b2b";
4
4
  export type { LinkedInSearchParams, LinkedInSearchResponse } from "./b2b";
5
+ export { crunchbaseSearch } from "./crunchbase";
6
+ export type { CrunchbaseSearchParams } from "./crunchbase";
5
7
  export { webSearch, webBatchSearch } from "./serp";
6
8
  export type { WebSearchQuery, WebSearchResult, WebSearchResponse, BatchWebSearchParams } from "./serp";
7
9
  export { generateObject } from "./generateObject";
@@ -21,12 +23,16 @@ export type { PersonLinkedinFindUrlParams, CompanyLinkedinFindUrlParams, PersonC
21
23
  import { runApifyActor } from "./apify";
22
24
  import { linkedinSearch } from "./b2b";
23
25
  import { browserExecute } from "./browser";
26
+ import { crunchbaseSearch } from "./crunchbase";
24
27
  import { personLinkedinEnrich, personLinkedinFindUrl, personContactGet, companyLinkedinEnrich, companyLinkedinFindUrl, companyGetEmployeesFromLinkedin, geoParseAddress, builtWithLookupDomain, builtWithRelationships, builtWithSearchByTech } from "./expansion";
25
28
  import { scrapeWebsite } from "./firecrawl";
26
29
  import { generateObject } from "./generateObject";
27
30
  import { googleMapsScrape } from "./googleMaps";
28
31
  import { webBatchSearch, webSearch } from "./serp";
29
32
  export declare const services: {
33
+ crunchbase: {
34
+ search: typeof crunchbaseSearch;
35
+ };
30
36
  company: {
31
37
  linkedin: {
32
38
  findUrl: typeof companyLinkedinFindUrl;
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.services = exports.builtWithSearchByTech = exports.builtWithRelationships = exports.builtWithLookupDomain = exports.geoParseAddress = exports.companyGetEmployeesFromLinkedin = exports.companyLinkedinFindUrl = exports.companyLinkedinEnrich = exports.personContactGet = exports.personLinkedinFindUrl = exports.personLinkedinEnrich = exports.PREDICT_LEADS_OPERATION_IDS = exports.predictLeads = exports.executePredictLeads = exports.googleMapsScrape = exports.runApifyActor = exports.browserExecute = exports.scrapeWebsite = exports.generateObject = exports.webBatchSearch = exports.webSearch = exports.linkedinSearch = exports.configure = void 0;
3
+ exports.services = exports.builtWithSearchByTech = exports.builtWithRelationships = exports.builtWithLookupDomain = exports.geoParseAddress = exports.companyGetEmployeesFromLinkedin = exports.companyLinkedinFindUrl = exports.companyLinkedinEnrich = exports.personContactGet = exports.personLinkedinFindUrl = exports.personLinkedinEnrich = exports.PREDICT_LEADS_OPERATION_IDS = exports.predictLeads = exports.executePredictLeads = exports.googleMapsScrape = exports.runApifyActor = exports.browserExecute = exports.scrapeWebsite = exports.generateObject = exports.webBatchSearch = exports.webSearch = exports.crunchbaseSearch = exports.linkedinSearch = exports.configure = void 0;
4
4
  var api_1 = require("./api");
5
5
  Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return api_1.configure; } });
6
6
  var b2b_1 = require("./b2b");
7
7
  Object.defineProperty(exports, "linkedinSearch", { enumerable: true, get: function () { return b2b_1.linkedinSearch; } });
8
+ var crunchbase_1 = require("./crunchbase");
9
+ Object.defineProperty(exports, "crunchbaseSearch", { enumerable: true, get: function () { return crunchbase_1.crunchbaseSearch; } });
8
10
  var serp_1 = require("./serp");
9
11
  Object.defineProperty(exports, "webSearch", { enumerable: true, get: function () { return serp_1.webSearch; } });
10
12
  Object.defineProperty(exports, "webBatchSearch", { enumerable: true, get: function () { return serp_1.webBatchSearch; } });
@@ -36,6 +38,7 @@ Object.defineProperty(exports, "builtWithSearchByTech", { enumerable: true, get:
36
38
  const apify_2 = require("./apify");
37
39
  const b2b_2 = require("./b2b");
38
40
  const browser_2 = require("./browser");
41
+ const crunchbase_2 = require("./crunchbase");
39
42
  const expansion_2 = require("./expansion");
40
43
  const firecrawl_2 = require("./firecrawl");
41
44
  const generateObject_2 = require("./generateObject");
@@ -43,6 +46,9 @@ const googleMaps_2 = require("./googleMaps");
43
46
  const predictLeads_2 = require("./predictLeads");
44
47
  const serp_2 = require("./serp");
45
48
  exports.services = {
49
+ crunchbase: {
50
+ search: crunchbase_2.crunchbaseSearch
51
+ },
46
52
  company: {
47
53
  linkedin: {
48
54
  findUrl: expansion_2.companyLinkedinFindUrl,
@@ -9,4 +9,4 @@ Typed functions for Gmail actions powered by Orange Slice Google integrations.
9
9
  ## Email
10
10
 
11
11
  - `integrations.gmail.sendEmail(input)` - Send an email through the connected Gmail account
12
- - Heavy rate limit: `sendEmail` is capped at **20 calls/day** per connected Gmail account
12
+ - Heavy rate limit: `sendEmail` is capped at **40 calls/day** per connected Gmail account
@@ -2,7 +2,7 @@
2
2
 
3
3
  Send an email from the connected Gmail account.
4
4
 
5
- > Rate limit note for AI: `integrations.gmail.sendEmail(...)` is heavily rate-limited to **20 calls/day** per connected Gmail account. Use sparingly and batch/aggregate where possible.
5
+ > Rate limit note for AI: `integrations.gmail.sendEmail(...)` is heavily rate-limited to **40 calls/day** per connected Gmail account. Use sparingly and batch/aggregate where possible.
6
6
 
7
7
  ```typescript
8
8
  // Basic email
@@ -25,18 +25,18 @@ await integrations.gmail.sendEmail({
25
25
 
26
26
  ## Input
27
27
 
28
- | Parameter | Type | Required | Description |
29
- | ------------------ | ----------- | -------- | ----------- |
30
- | `recipient_email` | `string` | No\* | Primary `To` recipient |
31
- | `extra_recipients` | `string[]` | No | Additional `To` recipients |
32
- | `cc` | `string[]` | No | CC recipients |
33
- | `bcc` | `string[]` | No | BCC recipients |
34
- | `subject` | `string` | No\* | Email subject |
35
- | `body` | `string` | No\* | Email body (plain text or HTML) |
36
- | `is_html` | `boolean` | No | Set to `true` when body is HTML |
37
- | `from_email` | `string` | No | Optional verified send-as alias |
38
- | `attachment` | `object` | No | Optional attachment payload |
39
- | `user_id` | `string` | No | Gmail user id (`"me"` by default) |
28
+ | Parameter | Type | Required | Description |
29
+ | ------------------ | ---------- | -------- | --------------------------------- |
30
+ | `recipient_email` | `string` | No\* | Primary `To` recipient |
31
+ | `extra_recipients` | `string[]` | No | Additional `To` recipients |
32
+ | `cc` | `string[]` | No | CC recipients |
33
+ | `bcc` | `string[]` | No | BCC recipients |
34
+ | `subject` | `string` | No\* | Email subject |
35
+ | `body` | `string` | No\* | Email body (plain text or HTML) |
36
+ | `is_html` | `boolean` | No | Set to `true` when body is HTML |
37
+ | `from_email` | `string` | No | Optional verified send-as alias |
38
+ | `attachment` | `object` | No | Optional attachment payload |
39
+ | `user_id` | `string` | No | Gmail user id (`"me"` by default) |
40
40
 
41
41
  \*Gmail requires at least one recipient (`recipient_email`, `cc`, or `bcc`) and at least one of `subject` or `body`.
42
42
 
@@ -16,6 +16,7 @@ description: Strategies for searching or finding people and companies. This is a
16
16
  Run queries with built-in filters when the criteria is searchable:
17
17
 
18
18
  - **Web search (`services.web.search`)** — **Default for LinkedIn**. Use for keywords, niche queries, fuzzy matching, anything descriptive.
19
+ - **Crunchbase (`services.crunchbase.search`)** — **Default for funding data**. Use for funding-stage, round type, amount, date windows, and investor-backed company discovery.
19
20
  - **LinkedIn B2B DB** — **Indexed lookups ONLY:** company by domain/slug/ID, employees at a known company (by company_id), basic funding (2-table join). Everything else = web search. See [QUICK_REF](./linkedin_data/QUICK_REF.md).
20
21
  - **Google Maps** — industry, location, ratings
21
22
  - **LinkedIn job search** — job filters, titles
@@ -83,11 +84,11 @@ When using qualification columns, think Circle & Star:
83
84
  | Source | Use When | Limitations |
84
85
  | ------------------------ | ----------------------------------------------------------- | -------------------------------------------------------- |
85
86
  | **Web Search (Default)** | **Everything else** — keywords, niche, fuzzy, specific | Requires verification columns for false positives. |
87
+ | **Crunchbase (Funding Default)** | Funding-focused prospecting: stage, round type, amount, recency, investors | Best for funding intelligence; use other sources for non-funding discovery criteria. |
86
88
  | **PredictLeads** | Company intelligence, buying signals, and structured company events at scale | Coverage varies by company/market; use web search for very niche long-tail discovery. |
87
89
  | **Niche Directory Scrape** | Well-defined categories with existing lists (see below) | Requires finding the right directory first. |
88
90
  | **LinkedIn B2B DB** | **Indexed lookups ONLY:** company by domain/slug/ID, employees at known company, basic 2-table funding. | **3s hard max. No keyword search, no LATERAL, no 3+ table joins.** Everything else = web search. |
89
91
  | **Google Maps** | Local/SMB, physical locations, restaurants, retail | Limited to businesses with physical presence. |
90
- | **NPI Database** | Healthcare providers | Healthcare only. Free. |
91
92
  | **Apify Actors** | Platform-specific scraping (Instagram, TikTok, job boards) | Per-platform setup. May break with platform changes. |
92
93
 
93
94
  ### PredictLeads: When It Is Better Than Everything Else
@@ -105,6 +106,12 @@ Prefer other sources when:
105
106
  - You need local storefront/SMB discovery -> use Google Maps
106
107
  - You need fast indexed LinkedIn lookups by known IDs/domain/company -> use LinkedIn B2B DB
107
108
 
109
+ ### Funding Prospecting Standard: Use Crunchbase First
110
+
111
+ For any request centered on funding data (for example: "Series A fintech companies", "companies that raised in the last 12 months", "recently funded startups"), use `services.crunchbase.search` as the **standard/default source**.
112
+
113
+ Use LinkedIn B2B DB funding joins only when the user explicitly needs a LinkedIn-only workflow or a narrow lookup tied to existing LinkedIn records. Otherwise, Crunchbase should be the first choice for funding-oriented discovery.
114
+
108
115
  ### Niche Directory Scraping — For Well-Defined Categories
109
116
 
110
117
  When users ask for companies in a **specific, well-defined niche** (e.g., "fast food chains", "Fortune 500 companies", "Y Combinator startups"), the best approach is often to **find and scrape a curated directory or list**.
@@ -260,27 +267,28 @@ Don't overthink it — just create 2-3 views that match the columns you built. S
260
267
 
261
268
  ## Examples
262
269
 
263
- | User Request | Approach | Why |
264
- | -------------------------------------------- | ---------------- | ------------------------------------------------------------------- |
265
- | "AI CRM companies" | Web search | Keyword query → `"AI CRM" site:linkedin.com/company` |
266
- | "Fintech startups" | Web search | Fuzzy/descriptive → `"fintech" "startup" site:linkedin.com/company` |
267
- | "SDRs at Series A companies" | Web search | Specific criteria → `"SDR" "Series A" site:linkedin.com/in` |
268
- | "Companies using Kubernetes" | Web search | Technology match `"Kubernetes" site:linkedin.com/company` |
269
- | "VPs who worked at Google" | Web search | Fuzzy history match → `"VP" "Google" site:linkedin.com/in` |
270
- | "1000 software engineers in Bay Area" | B2B DB | Simple title + location + high volume |
271
- | "All healthcare companies 100-500 employees" | B2B DB | Industry + size + high volume |
272
- | "Fast food chains that..." | Directory scrape | Scrape Wikipedia list `browser.execute` |
273
- | "Restaurants in Austin" | Google Maps | Local/SMB with physical presence |
274
- | "Companies hiring SDRs" | LinkedIn Jobs | Job search with title filter |
275
- | "Warehouses implementing WMS" | Circle + columns | Pull logistics companies add "WMS Score" column |
276
- | "Companies that recently switched CRMs" | Circle + columns | Pull SaaS companies → add "CRM Change Signals" column |
270
+ | User Request | Approach | Why |
271
+ | -------------------------------------------- | ---------------- | --------------------------------------------------------------------------- |
272
+ | "AI CRM companies" | Web search | Keyword query → `"AI CRM" site:linkedin.com/company` |
273
+ | "Fintech startups" | Web search | Fuzzy/descriptive → `"fintech" "startup" site:linkedin.com/company` |
274
+ | "SDRs at Series A companies" | Web search | Specific criteria → `"SDR" "Series A" site:linkedin.com/in` |
275
+ | "Series A/B companies raised last year" | Crunchbase | Funding-specific discovery is best handled via `services.crunchbase.search` |
276
+ | "Companies using Kubernetes" | Web search | Technology match → `"Kubernetes" site:linkedin.com/company` |
277
+ | "VPs who worked at Google" | Web search | Fuzzy history match `"VP" "Google" site:linkedin.com/in` |
278
+ | "1000 software engineers in Bay Area" | B2B DB | Simple title + location + high volume |
279
+ | "All healthcare companies 100-500 employees" | B2B DB | Industry + size + high volume |
280
+ | "Fast food chains that..." | Directory scrape | Scrape Wikipedia list → `browser.execute` |
281
+ | "Restaurants in Austin" | Google Maps | Local/SMB with physical presence |
282
+ | "Companies hiring SDRs" | LinkedIn Jobs | Job search with title filter |
283
+ | "Warehouses implementing WMS" | Circle + columns | Pull logistics companies → add "WMS Score" column |
284
+ | "Companies that recently switched CRMs" | Circle + columns | Pull SaaS companies → add "CRM Change Signals" column |
277
285
 
278
286
  ---
279
287
 
280
288
  ## Tools
281
289
 
282
290
  - **LinkedIn:** `services.company.linkedin.search({ sql: "SELECT ... FROM linkedin_company ..." })`, `services.person.linkedin.search({ sql: "SELECT ... FROM linkedin_profile ..." })` — **Lookup tool only, 3s max, 2-table joins max. Use web search for anything else.**
283
- - **Healthcare:** `healthcare.npi`
291
+ - **Funding:** `services.crunchbase.search({ sql: "SELECT ... FROM ... WHERE ..." })` — **Default for funding search and screening.**
284
292
  - **Local/SMB:** `googleMaps.scrape`
285
293
  - **Web:** `web.search` + `browser.execute`
286
294
  - **Platforms:** `services.apify.runActor`
@@ -390,6 +390,38 @@ WHERE lc.company_size = '51-200 employees'
390
390
  - **Use `lc` alias** for company tables
391
391
  - **Default to US**: `lc.country_code = 'US'`
392
392
 
393
+ ## Return Type
394
+
395
+ `services.company.linkedin.search()` returns an object envelope:
396
+
397
+ ```typescript
398
+ {
399
+ rows: (Record < string, unknown > []);
400
+ count: number;
401
+ }
402
+ ```
403
+
404
+ - `rows`: Result rows from your SQL query, with exactly the columns you selected.
405
+ - `count`: Number of rows returned in `rows`.
406
+
407
+ Example:
408
+
409
+ ```typescript
410
+ const searchResult = await services.company.linkedin.search({
411
+ sql: `
412
+ SELECT
413
+ lc.company_name,
414
+ lc.domain,
415
+ 'https://www.linkedin.com/company/' || lc.universal_name AS lc_linkedin_url
416
+ FROM linkedin_company lc
417
+ WHERE lc.domain = 'stripe.com'
418
+ LIMIT 1
419
+ `
420
+ });
421
+
422
+ return searchResult.rows; // Most spreadsheet snippets should return rows
423
+ ```
424
+
393
425
  ---
394
426
 
395
427
  ## Table Aliases