orangeslice 1.1.0 ā 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -8
- package/dist/b2b.js +1 -1
- package/dist/cli.js +28 -9
- package/dist/firecrawl.d.ts +49 -0
- package/dist/firecrawl.js +64 -0
- package/dist/index.d.ts +21 -12
- package/dist/index.js +16 -12
- package/dist/serp.d.ts +41 -0
- package/dist/serp.js +52 -0
- package/docs/AGENTS.md +319 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,21 +1,53 @@
|
|
|
1
1
|
# orangeslice
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turn any AI agent into a B2B sales research assistant with access to **1B+ LinkedIn profiles**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npx orangeslice
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This copies documentation to `./orangeslice-docs/` and installs the package. Point your AI agent (Claude Code, Cursor, etc.) to `./orangeslice-docs/AGENTS.md` and it becomes your sales research agent.
|
|
10
|
+
|
|
11
|
+
## What Your Agent Can Do
|
|
12
|
+
|
|
13
|
+
| Function | Capability |
|
|
14
|
+
|----------|------------|
|
|
15
|
+
| `b2b` | Query 1B+ LinkedIn profiles, companies, funding, jobs |
|
|
16
|
+
| `serp` | Google search for news, articles, reviews |
|
|
17
|
+
| `firecrawl` | Scrape websites, extract social URLs |
|
|
18
|
+
|
|
19
|
+
## Quick Example
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { orangeslice } from 'orangeslice';
|
|
23
|
+
|
|
24
|
+
// B2B Database - Company + people research
|
|
25
|
+
const company = await orangeslice.b2b.sql(`
|
|
26
|
+
SELECT company_name, employee_count, description
|
|
27
|
+
FROM linkedin_company WHERE domain = 'stripe.com'
|
|
28
|
+
`);
|
|
6
29
|
|
|
7
|
-
|
|
30
|
+
// Google Search - Find news and articles
|
|
31
|
+
const news = await orangeslice.serp.search("Stripe funding 2024");
|
|
32
|
+
|
|
33
|
+
// Website Scraping - Get page content + social links
|
|
34
|
+
const about = await orangeslice.firecrawl.scrape("https://stripe.com/about");
|
|
35
|
+
console.log(about.markdown); // Page as markdown
|
|
36
|
+
console.log(about.socialUrls); // LinkedIn, Twitter, etc.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
All calls are rate-limited automatically.
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
8
42
|
|
|
9
43
|
| Doc | What it covers |
|
|
10
44
|
|-----|----------------|
|
|
11
|
-
| [
|
|
45
|
+
| [**AGENTS.md**](./docs/AGENTS.md) | **Start here** - Sales agent instructions |
|
|
46
|
+
| [B2B_DATABASE.md](./docs/B2B_DATABASE.md) | Database overview, query examples |
|
|
12
47
|
| [B2B_SCHEMA.md](./docs/B2B_SCHEMA.md) | All tables and columns |
|
|
13
|
-
| [B2B_EMPLOYEE_SEARCH.md](./docs/B2B_EMPLOYEE_SEARCH.md) |
|
|
48
|
+
| [B2B_EMPLOYEE_SEARCH.md](./docs/B2B_EMPLOYEE_SEARCH.md) | Finding people by title/company |
|
|
14
49
|
| [B2B_GENERALIZATION_RULES.md](./docs/B2B_GENERALIZATION_RULES.md) | Query patterns and best practices |
|
|
15
50
|
| [B2B_NLP_QUERY_MAPPINGS.md](./docs/B2B_NLP_QUERY_MAPPINGS.md) | Natural language to SQL mappings |
|
|
16
|
-
| [B2B_TABLE_INDICES.ts](./docs/B2B_TABLE_INDICES.ts) | TypeScript types for all tables |
|
|
17
|
-
|
|
18
|
-
Start with `B2B_DATABASE.md` and `B2B_SCHEMA.md` to understand the data model.
|
|
19
51
|
|
|
20
52
|
## Installation
|
|
21
53
|
|
package/dist/b2b.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.query = query;
|
|
|
7
7
|
const queue_1 = require("./queue");
|
|
8
8
|
// Default config
|
|
9
9
|
let config = {
|
|
10
|
-
proxyUrl: process.env.
|
|
10
|
+
proxyUrl: process.env.ORANGESLICE_API_URL || "https://orangeslice.ai/api/function?functionId=b2b",
|
|
11
11
|
concurrency: 2,
|
|
12
12
|
minDelayMs: 100, // 100ms between requests = max 10/sec
|
|
13
13
|
};
|
package/dist/cli.js
CHANGED
|
@@ -36,26 +36,45 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
39
40
|
const DOCS_DIR = path.join(__dirname, "..", "docs");
|
|
40
41
|
const TARGET_DIR = path.join(process.cwd(), "orangeslice-docs");
|
|
41
42
|
async function main() {
|
|
42
|
-
console.log("
|
|
43
|
-
//
|
|
43
|
+
console.log("\n orangeslice - setting up B2B client for AI agents...\n");
|
|
44
|
+
// 1. Copy docs
|
|
45
|
+
console.log("1. Copying B2B documentation...");
|
|
44
46
|
if (!fs.existsSync(TARGET_DIR)) {
|
|
45
47
|
fs.mkdirSync(TARGET_DIR, { recursive: true });
|
|
46
48
|
}
|
|
47
|
-
// Copy all docs
|
|
48
49
|
const files = fs.readdirSync(DOCS_DIR);
|
|
49
|
-
let copied = 0;
|
|
50
50
|
for (const file of files) {
|
|
51
51
|
const src = path.join(DOCS_DIR, file);
|
|
52
52
|
const dest = path.join(TARGET_DIR, file);
|
|
53
53
|
fs.copyFileSync(src, dest);
|
|
54
|
-
console.log(`
|
|
55
|
-
copied++;
|
|
54
|
+
console.log(` ${file}`);
|
|
56
55
|
}
|
|
57
|
-
console.log(
|
|
58
|
-
|
|
59
|
-
console.log("
|
|
56
|
+
console.log(` Copied ${files.length} docs to ./orangeslice-docs/\n`);
|
|
57
|
+
// 2. Install package
|
|
58
|
+
console.log("2. Installing orangeslice package...");
|
|
59
|
+
try {
|
|
60
|
+
(0, child_process_1.execSync)("npm install orangeslice", { stdio: "inherit", cwd: process.cwd() });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
console.log(" (skipped - no package.json or npm not available)\n");
|
|
64
|
+
}
|
|
65
|
+
// 3. Show usage
|
|
66
|
+
console.log("\n Done! Your AI agent is ready.\n");
|
|
67
|
+
console.log(" Point your AI agent (Claude, Cursor, etc.) to:\n");
|
|
68
|
+
console.log(" ./orangeslice-docs/AGENTS.md\n");
|
|
69
|
+
console.log(" This tells the agent how to act as your sales research assistant");
|
|
70
|
+
console.log(" with access to 1B+ LinkedIn profiles.\n");
|
|
71
|
+
console.log(" Example usage:\n");
|
|
72
|
+
console.log(' import { orangeslice } from "orangeslice";');
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log(" const company = await orangeslice.b2b.sql(`");
|
|
75
|
+
console.log(" SELECT company_name, employee_count, description");
|
|
76
|
+
console.log(" FROM linkedin_company");
|
|
77
|
+
console.log(" WHERE domain = 'stripe.com'");
|
|
78
|
+
console.log(" `);\n");
|
|
60
79
|
}
|
|
61
80
|
main().catch(console.error);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface SocialUrls {
|
|
2
|
+
emailGeneral: string[];
|
|
3
|
+
facebookProfile: string[];
|
|
4
|
+
instagramProfile: string[];
|
|
5
|
+
twitterUser: string[];
|
|
6
|
+
youtubeChannel: string[];
|
|
7
|
+
youtubeVideo: string[];
|
|
8
|
+
tiktokProfile: string[];
|
|
9
|
+
linkedinProfile: string[];
|
|
10
|
+
linkedinCompany: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface PageData {
|
|
13
|
+
markdown: string;
|
|
14
|
+
links: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface FirecrawlResponse {
|
|
17
|
+
markdown: string;
|
|
18
|
+
data: PageData[];
|
|
19
|
+
socialUrls: SocialUrls;
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Scrape a website and get markdown content.
|
|
24
|
+
* Automatically rate-limited.
|
|
25
|
+
*
|
|
26
|
+
* @param url - URL to scrape
|
|
27
|
+
* @param limit - Number of pages to crawl (default 1 for single page scrape)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Single page scrape
|
|
31
|
+
* const page = await firecrawl.scrape("https://stripe.com/about");
|
|
32
|
+
*
|
|
33
|
+
* // Multi-page crawl
|
|
34
|
+
* const site = await firecrawl.scrape("https://stripe.com", 5);
|
|
35
|
+
*/
|
|
36
|
+
export declare function scrape(url: string, limit?: number): Promise<FirecrawlResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Scrape a website and return just the markdown content
|
|
39
|
+
*/
|
|
40
|
+
export declare function markdown(url: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Scrape a website and extract social URLs
|
|
43
|
+
*/
|
|
44
|
+
export declare function socials(url: string): Promise<SocialUrls>;
|
|
45
|
+
export declare const firecrawl: {
|
|
46
|
+
scrape: typeof scrape;
|
|
47
|
+
markdown: typeof markdown;
|
|
48
|
+
socials: typeof socials;
|
|
49
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.firecrawl = void 0;
|
|
4
|
+
exports.scrape = scrape;
|
|
5
|
+
exports.markdown = markdown;
|
|
6
|
+
exports.socials = socials;
|
|
7
|
+
const queue_1 = require("./queue");
|
|
8
|
+
const API_URL = process.env.ORANGESLICE_API_URL || "https://orangeslice.ai/api/function?functionId=firecrawl";
|
|
9
|
+
// Shared queue for all Firecrawl requests
|
|
10
|
+
const queue = (0, queue_1.createQueue)(2);
|
|
11
|
+
const rateLimiter = (0, queue_1.createRateLimiter)(500); // 500ms between requests (crawls are expensive)
|
|
12
|
+
/**
|
|
13
|
+
* Scrape a website and get markdown content.
|
|
14
|
+
* Automatically rate-limited.
|
|
15
|
+
*
|
|
16
|
+
* @param url - URL to scrape
|
|
17
|
+
* @param limit - Number of pages to crawl (default 1 for single page scrape)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Single page scrape
|
|
21
|
+
* const page = await firecrawl.scrape("https://stripe.com/about");
|
|
22
|
+
*
|
|
23
|
+
* // Multi-page crawl
|
|
24
|
+
* const site = await firecrawl.scrape("https://stripe.com", 5);
|
|
25
|
+
*/
|
|
26
|
+
async function scrape(url, limit = 1) {
|
|
27
|
+
return queue(async () => {
|
|
28
|
+
return rateLimiter(async () => {
|
|
29
|
+
const response = await fetch(API_URL, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify({ url, limit }),
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`Firecrawl request failed: ${response.status} ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const data = (await response.json());
|
|
38
|
+
if (data.error) {
|
|
39
|
+
throw new Error(`Firecrawl error: ${data.error}`);
|
|
40
|
+
}
|
|
41
|
+
return data;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Scrape a website and return just the markdown content
|
|
47
|
+
*/
|
|
48
|
+
async function markdown(url) {
|
|
49
|
+
const data = await scrape(url, 1);
|
|
50
|
+
return data.markdown;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Scrape a website and extract social URLs
|
|
54
|
+
*/
|
|
55
|
+
async function socials(url) {
|
|
56
|
+
const data = await scrape(url, 1);
|
|
57
|
+
return data.socialUrls;
|
|
58
|
+
}
|
|
59
|
+
// Export as namespace
|
|
60
|
+
exports.firecrawl = {
|
|
61
|
+
scrape,
|
|
62
|
+
markdown,
|
|
63
|
+
socials,
|
|
64
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { b2b } from "./b2b";
|
|
2
|
-
|
|
2
|
+
import { serp } from "./serp";
|
|
3
|
+
import { firecrawl } from "./firecrawl";
|
|
4
|
+
export { b2b, serp, firecrawl };
|
|
3
5
|
/**
|
|
4
|
-
* Main orangeslice namespace
|
|
6
|
+
* Main orangeslice namespace - AI sales agent toolkit
|
|
5
7
|
*
|
|
6
8
|
* @example
|
|
7
9
|
* import { orangeslice } from 'orangeslice';
|
|
8
10
|
*
|
|
9
|
-
* //
|
|
10
|
-
* orangeslice.b2b.configure({ concurrency: 3 });
|
|
11
|
-
*
|
|
12
|
-
* // Query - automatically rate-limited
|
|
11
|
+
* // B2B Database - 1B+ LinkedIn profiles
|
|
13
12
|
* const companies = await orangeslice.b2b.sql("SELECT * FROM linkedin_company WHERE domain = 'stripe.com'");
|
|
14
13
|
*
|
|
15
|
-
* //
|
|
16
|
-
* const results = await
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* // Google Search
|
|
15
|
+
* const results = await orangeslice.serp.search("best CRM software 2024");
|
|
16
|
+
*
|
|
17
|
+
* // Website Scraping
|
|
18
|
+
* const page = await orangeslice.firecrawl.scrape("https://stripe.com/about");
|
|
19
|
+
*
|
|
20
|
+
* // All calls are automatically rate-limited and queued
|
|
21
21
|
*/
|
|
22
22
|
export declare const orangeslice: {
|
|
23
23
|
b2b: {
|
|
@@ -25,5 +25,14 @@ export declare const orangeslice: {
|
|
|
25
25
|
query: typeof import("./b2b").query;
|
|
26
26
|
configure: typeof import("./b2b").configure;
|
|
27
27
|
};
|
|
28
|
+
serp: {
|
|
29
|
+
search: typeof import("./serp").search;
|
|
30
|
+
organic: typeof import("./serp").organic;
|
|
31
|
+
};
|
|
32
|
+
firecrawl: {
|
|
33
|
+
scrape: typeof import("./firecrawl").scrape;
|
|
34
|
+
markdown: typeof import("./firecrawl").markdown;
|
|
35
|
+
socials: typeof import("./firecrawl").socials;
|
|
36
|
+
};
|
|
28
37
|
};
|
|
29
38
|
export default orangeslice;
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.orangeslice = exports.b2b = void 0;
|
|
3
|
+
exports.orangeslice = exports.firecrawl = exports.serp = exports.b2b = void 0;
|
|
4
4
|
const b2b_1 = require("./b2b");
|
|
5
5
|
Object.defineProperty(exports, "b2b", { enumerable: true, get: function () { return b2b_1.b2b; } });
|
|
6
|
+
const serp_1 = require("./serp");
|
|
7
|
+
Object.defineProperty(exports, "serp", { enumerable: true, get: function () { return serp_1.serp; } });
|
|
8
|
+
const firecrawl_1 = require("./firecrawl");
|
|
9
|
+
Object.defineProperty(exports, "firecrawl", { enumerable: true, get: function () { return firecrawl_1.firecrawl; } });
|
|
6
10
|
/**
|
|
7
|
-
* Main orangeslice namespace
|
|
11
|
+
* Main orangeslice namespace - AI sales agent toolkit
|
|
8
12
|
*
|
|
9
13
|
* @example
|
|
10
14
|
* import { orangeslice } from 'orangeslice';
|
|
11
15
|
*
|
|
12
|
-
* //
|
|
13
|
-
* orangeslice.b2b.configure({ concurrency: 3 });
|
|
14
|
-
*
|
|
15
|
-
* // Query - automatically rate-limited
|
|
16
|
+
* // B2B Database - 1B+ LinkedIn profiles
|
|
16
17
|
* const companies = await orangeslice.b2b.sql("SELECT * FROM linkedin_company WHERE domain = 'stripe.com'");
|
|
17
18
|
*
|
|
18
|
-
* //
|
|
19
|
-
* const results = await
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* // Google Search
|
|
20
|
+
* const results = await orangeslice.serp.search("best CRM software 2024");
|
|
21
|
+
*
|
|
22
|
+
* // Website Scraping
|
|
23
|
+
* const page = await orangeslice.firecrawl.scrape("https://stripe.com/about");
|
|
24
|
+
*
|
|
25
|
+
* // All calls are automatically rate-limited and queued
|
|
24
26
|
*/
|
|
25
27
|
exports.orangeslice = {
|
|
26
28
|
b2b: b2b_1.b2b,
|
|
29
|
+
serp: serp_1.serp,
|
|
30
|
+
firecrawl: firecrawl_1.firecrawl,
|
|
27
31
|
};
|
|
28
32
|
exports.default = exports.orangeslice;
|
package/dist/serp.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface SerpResult {
|
|
2
|
+
title: string;
|
|
3
|
+
link: string;
|
|
4
|
+
snippet: string;
|
|
5
|
+
position?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SerpResponse {
|
|
8
|
+
organic_results?: SerpResult[];
|
|
9
|
+
related_questions?: Array<{
|
|
10
|
+
question: string;
|
|
11
|
+
snippet: string;
|
|
12
|
+
}>;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SerpOptions {
|
|
16
|
+
/** Regex pattern to filter links */
|
|
17
|
+
linkRegexPattern?: string;
|
|
18
|
+
/** Enable advanced search features */
|
|
19
|
+
advance_search?: boolean;
|
|
20
|
+
/** Page number (default 1) */
|
|
21
|
+
page?: number;
|
|
22
|
+
/** Time-based search filter (e.g., "qdr:d" for past day, "qdr:w" for past week) */
|
|
23
|
+
tbs?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Search Google via SERP API.
|
|
27
|
+
* Automatically rate-limited.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const results = await serp.search("best CRM software 2024");
|
|
31
|
+
* const filtered = await serp.search("site:linkedin.com CEO stripe", { linkRegexPattern: "linkedin.com/in/" });
|
|
32
|
+
*/
|
|
33
|
+
export declare function search(query: string, options?: SerpOptions): Promise<SerpResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Search and return just the organic results
|
|
36
|
+
*/
|
|
37
|
+
export declare function organic(query: string, options?: SerpOptions): Promise<SerpResult[]>;
|
|
38
|
+
export declare const serp: {
|
|
39
|
+
search: typeof search;
|
|
40
|
+
organic: typeof organic;
|
|
41
|
+
};
|
package/dist/serp.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serp = void 0;
|
|
4
|
+
exports.search = search;
|
|
5
|
+
exports.organic = organic;
|
|
6
|
+
const queue_1 = require("./queue");
|
|
7
|
+
const API_URL = process.env.ORANGESLICE_API_URL || "https://orangeslice.ai/api/function?functionId=serp";
|
|
8
|
+
// Shared queue for all SERP requests
|
|
9
|
+
const queue = (0, queue_1.createQueue)(2);
|
|
10
|
+
const rateLimiter = (0, queue_1.createRateLimiter)(200); // 200ms between requests
|
|
11
|
+
/**
|
|
12
|
+
* Search Google via SERP API.
|
|
13
|
+
* Automatically rate-limited.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const results = await serp.search("best CRM software 2024");
|
|
17
|
+
* const filtered = await serp.search("site:linkedin.com CEO stripe", { linkRegexPattern: "linkedin.com/in/" });
|
|
18
|
+
*/
|
|
19
|
+
async function search(query, options = {}) {
|
|
20
|
+
return queue(async () => {
|
|
21
|
+
return rateLimiter(async () => {
|
|
22
|
+
const response = await fetch(API_URL, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
query,
|
|
27
|
+
...options,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`SERP request failed: ${response.status} ${response.statusText}`);
|
|
32
|
+
}
|
|
33
|
+
const data = (await response.json());
|
|
34
|
+
if (data.error) {
|
|
35
|
+
throw new Error(`SERP error: ${data.error}`);
|
|
36
|
+
}
|
|
37
|
+
return data;
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Search and return just the organic results
|
|
43
|
+
*/
|
|
44
|
+
async function organic(query, options = {}) {
|
|
45
|
+
const data = await search(query, options);
|
|
46
|
+
return data.organic_results || [];
|
|
47
|
+
}
|
|
48
|
+
// Export as namespace
|
|
49
|
+
exports.serp = {
|
|
50
|
+
search,
|
|
51
|
+
organic,
|
|
52
|
+
};
|
package/docs/AGENTS.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Sales Agent
|
|
2
|
+
|
|
3
|
+
You are a B2B sales research agent with access to:
|
|
4
|
+
- **1.15 billion LinkedIn profiles** and millions of companies
|
|
5
|
+
- **Google Search** (SERP API)
|
|
6
|
+
- **Website scraping** (Firecrawl)
|
|
7
|
+
|
|
8
|
+
## What You Can Do
|
|
9
|
+
|
|
10
|
+
| Capability | Tool | Example |
|
|
11
|
+
|------------|------|---------|
|
|
12
|
+
| **Company research** | `b2b` | Look up any company by domain, name, or LinkedIn URL |
|
|
13
|
+
| **Find decision makers** | `b2b` | Find C-suite, VPs, Directors at target companies |
|
|
14
|
+
| **Employee lookup** | `b2b` | Search employees by title, role, or department |
|
|
15
|
+
| **Funding intelligence** | `b2b` | Find recently funded companies and their investors |
|
|
16
|
+
| **Google search** | `serp` | Search for company news, press releases, reviews |
|
|
17
|
+
| **Website scraping** | `firecrawl` | Extract content from company websites |
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { orangeslice } from 'orangeslice';
|
|
24
|
+
|
|
25
|
+
// 1. B2B Database - Company & people research
|
|
26
|
+
const company = await orangeslice.b2b.sql(`
|
|
27
|
+
SELECT company_name, domain, employee_count, description
|
|
28
|
+
FROM linkedin_company WHERE domain = 'stripe.com'
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
// 2. Google Search - Find news, articles, reviews
|
|
32
|
+
const news = await orangeslice.serp.search("Stripe funding 2024");
|
|
33
|
+
|
|
34
|
+
// 3. Website Scraping - Get company page content
|
|
35
|
+
const about = await orangeslice.firecrawl.scrape("https://stripe.com/about");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
All calls are automatically rate-limited. Fire away freely.
|
|
39
|
+
|
|
40
|
+
## Sales Workflows
|
|
41
|
+
|
|
42
|
+
### 1. Research a Target Account
|
|
43
|
+
|
|
44
|
+
```sql
|
|
45
|
+
-- Step 1: Get company details
|
|
46
|
+
SELECT id, company_name, domain, employee_count, locality, description
|
|
47
|
+
FROM linkedin_company
|
|
48
|
+
WHERE domain = 'openai.com';
|
|
49
|
+
|
|
50
|
+
-- Step 2: Find their leadership team
|
|
51
|
+
SELECT lp.first_name, lp.last_name, lp.headline, pos.title, lp.public_profile_url
|
|
52
|
+
FROM linkedin_profile lp
|
|
53
|
+
JOIN linkedin_profile_position3 pos ON pos.linkedin_profile_id = lp.id
|
|
54
|
+
WHERE pos.linkedin_company_id = 11130470 -- OpenAI's ID from step 1
|
|
55
|
+
AND pos.end_date IS NULL
|
|
56
|
+
AND (pos.title ILIKE 'ceo%' OR pos.title ILIKE 'cto%' OR pos.title ILIKE 'cfo%'
|
|
57
|
+
OR pos.title ILIKE '%vp%' OR pos.title ILIKE '%head of%')
|
|
58
|
+
LIMIT 30;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Find Your Ideal Customer Profile (ICP)
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
-- Software companies, 100-500 employees, with recent funding
|
|
65
|
+
SELECT lc.company_name, lc.domain, lc.employee_count,
|
|
66
|
+
cf.round_name, cf.round_date, cf.round_amount
|
|
67
|
+
FROM linkedin_company lc
|
|
68
|
+
JOIN linkedin_crunchbase_funding cf ON cf.linkedin_company_id = lc.id
|
|
69
|
+
WHERE lc.industry_code = 4 -- Software Development
|
|
70
|
+
AND lc.employee_count BETWEEN 100 AND 500
|
|
71
|
+
AND cf.round_date >= '2024-01-01'
|
|
72
|
+
ORDER BY cf.round_date DESC
|
|
73
|
+
LIMIT 50;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Find Specific Personas
|
|
77
|
+
|
|
78
|
+
```sql
|
|
79
|
+
-- Heads of Sales at mid-market companies
|
|
80
|
+
SELECT lp.first_name, lp.last_name, lp.headline,
|
|
81
|
+
pos.title, lc.company_name, lc.employee_count
|
|
82
|
+
FROM linkedin_profile lp
|
|
83
|
+
JOIN linkedin_profile_position3 pos ON pos.linkedin_profile_id = lp.id
|
|
84
|
+
JOIN linkedin_company lc ON lc.id = pos.linkedin_company_id
|
|
85
|
+
WHERE pos.end_date IS NULL
|
|
86
|
+
AND lc.employee_count BETWEEN 100 AND 1000
|
|
87
|
+
AND (pos.title ILIKE '%head of sales%'
|
|
88
|
+
OR pos.title ILIKE '%vp sales%'
|
|
89
|
+
OR pos.title ILIKE '%chief revenue%')
|
|
90
|
+
LIMIT 30;
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 4. Competitive Intelligence
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
-- Who works at competitor company?
|
|
97
|
+
SELECT lp.first_name, lp.last_name, lp.headline, pos.title
|
|
98
|
+
FROM linkedin_profile lp
|
|
99
|
+
JOIN linkedin_profile_position3 pos ON pos.linkedin_profile_id = lp.id
|
|
100
|
+
WHERE pos.linkedin_company_id = 2135371 -- Competitor's ID
|
|
101
|
+
AND pos.end_date IS NULL
|
|
102
|
+
AND pos.title ILIKE '%sales%'
|
|
103
|
+
LIMIT 50;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Google Search (SERP)
|
|
109
|
+
|
|
110
|
+
Search the web for company news, press releases, reviews, and more.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Basic search
|
|
114
|
+
const results = await orangeslice.serp.search("Stripe Series C funding");
|
|
115
|
+
|
|
116
|
+
// Get just organic results
|
|
117
|
+
const organic = await orangeslice.serp.organic("best CRM software 2024");
|
|
118
|
+
|
|
119
|
+
// Filter by site
|
|
120
|
+
const linkedin = await orangeslice.serp.search("site:linkedin.com/in CEO Ramp");
|
|
121
|
+
|
|
122
|
+
// Time-based search (past week)
|
|
123
|
+
const recent = await orangeslice.serp.search("OpenAI news", { tbs: "qdr:w" });
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### SERP Options
|
|
127
|
+
|
|
128
|
+
| Option | Type | Description |
|
|
129
|
+
|--------|------|-------------|
|
|
130
|
+
| `linkRegexPattern` | string | Filter results by URL pattern |
|
|
131
|
+
| `advance_search` | boolean | Enable advanced search features |
|
|
132
|
+
| `page` | number | Page number (default 1) |
|
|
133
|
+
| `tbs` | string | Time filter: `qdr:d` (day), `qdr:w` (week), `qdr:m` (month) |
|
|
134
|
+
|
|
135
|
+
### Use Cases
|
|
136
|
+
|
|
137
|
+
- Find company news and press releases
|
|
138
|
+
- Research competitors' public announcements
|
|
139
|
+
- Find LinkedIn profiles via Google
|
|
140
|
+
- Check company reviews on G2, Capterra, etc.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Website Scraping (Firecrawl)
|
|
145
|
+
|
|
146
|
+
Scrape any website and get markdown content + extracted social URLs.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Scrape a single page
|
|
150
|
+
const page = await orangeslice.firecrawl.scrape("https://stripe.com/about");
|
|
151
|
+
console.log(page.markdown); // Page content as markdown
|
|
152
|
+
console.log(page.socialUrls); // Extracted social links
|
|
153
|
+
|
|
154
|
+
// Just get markdown
|
|
155
|
+
const content = await orangeslice.firecrawl.markdown("https://company.com/team");
|
|
156
|
+
|
|
157
|
+
// Just get social URLs
|
|
158
|
+
const socials = await orangeslice.firecrawl.socials("https://company.com");
|
|
159
|
+
// Returns: { linkedinCompany: [...], twitterUser: [...], ... }
|
|
160
|
+
|
|
161
|
+
// Multi-page crawl (up to 5 pages)
|
|
162
|
+
const site = await orangeslice.firecrawl.scrape("https://company.com", 5);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Social URLs Extracted
|
|
166
|
+
|
|
167
|
+
| Field | Description |
|
|
168
|
+
|-------|-------------|
|
|
169
|
+
| `linkedinCompany` | Company LinkedIn pages |
|
|
170
|
+
| `linkedinProfile` | Individual LinkedIn profiles |
|
|
171
|
+
| `twitterUser` | Twitter/X profiles |
|
|
172
|
+
| `facebookProfile` | Facebook pages |
|
|
173
|
+
| `instagramProfile` | Instagram profiles |
|
|
174
|
+
| `youtubeChannel` | YouTube channels |
|
|
175
|
+
| `tiktokProfile` | TikTok profiles |
|
|
176
|
+
| `emailGeneral` | Email addresses |
|
|
177
|
+
|
|
178
|
+
### Use Cases
|
|
179
|
+
|
|
180
|
+
- Scrape company "About" or "Team" pages
|
|
181
|
+
- Find social media links from company websites
|
|
182
|
+
- Extract contact emails from websites
|
|
183
|
+
- Get company descriptions from their own sites
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Key Tables
|
|
188
|
+
|
|
189
|
+
| Table | Records | Use For |
|
|
190
|
+
|-------|---------|---------|
|
|
191
|
+
| `linkedin_company` | Millions | Company lookup, enrichment |
|
|
192
|
+
| `linkedin_profile` | 1.15B | Profile details |
|
|
193
|
+
| `linkedin_profile_position3` | 2.6B | Job history, current employer |
|
|
194
|
+
| `linkedin_crunchbase_funding` | - | Funding rounds |
|
|
195
|
+
| `linkedin_job` | 1.48B | Job postings |
|
|
196
|
+
|
|
197
|
+
## Performance Rules
|
|
198
|
+
|
|
199
|
+
### ā
Fast Queries (use these)
|
|
200
|
+
```sql
|
|
201
|
+
-- By domain (indexed)
|
|
202
|
+
WHERE domain = 'stripe.com'
|
|
203
|
+
|
|
204
|
+
-- By universal_name (indexed)
|
|
205
|
+
WHERE universal_name = 'stripe'
|
|
206
|
+
|
|
207
|
+
-- By company ID (indexed)
|
|
208
|
+
WHERE linkedin_company_id = 2135371
|
|
209
|
+
|
|
210
|
+
-- By profile ID (indexed)
|
|
211
|
+
WHERE linkedin_profile_id = 12345
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### ā ļø Slow Queries (avoid these)
|
|
215
|
+
```sql
|
|
216
|
+
-- Text search on names (no index)
|
|
217
|
+
WHERE company_name ILIKE '%stripe%' -- SLOW
|
|
218
|
+
|
|
219
|
+
-- Headline search (full scan)
|
|
220
|
+
WHERE headline ILIKE '%sales%' -- SLOW
|
|
221
|
+
|
|
222
|
+
-- COUNT on huge companies
|
|
223
|
+
SELECT COUNT(*) FROM ... WHERE linkedin_company_id = 1586 -- TIMEOUT
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Company Size Matters
|
|
227
|
+
|
|
228
|
+
| Company Size | Simple Query | Aggregations |
|
|
229
|
+
|-------------|--------------|--------------|
|
|
230
|
+
| Small (<1K) | 4-20ms | 5-50ms |
|
|
231
|
+
| Medium (1K-10K) | 10-30ms | 100-500ms |
|
|
232
|
+
| Large (10K-100K) | 10-40ms | 1-15s |
|
|
233
|
+
| Massive (100K+) | 15-65ms | **TIMEOUT** |
|
|
234
|
+
|
|
235
|
+
**For Amazon/Google (100K+ employees):** Only use simple `LIMIT` queries, no `COUNT` or `GROUP BY`.
|
|
236
|
+
|
|
237
|
+
## Common Company IDs
|
|
238
|
+
|
|
239
|
+
| Company | ID | Employees |
|
|
240
|
+
|---------|-----|-----------|
|
|
241
|
+
| Amazon | 1586 | 770K |
|
|
242
|
+
| Google | 1441 | 330K |
|
|
243
|
+
| Stripe | 2135371 | ~9K |
|
|
244
|
+
| OpenAI | 11130470 | ~7K |
|
|
245
|
+
| Ramp | 1406226 | ~3.5K |
|
|
246
|
+
|
|
247
|
+
## Title Search Patterns
|
|
248
|
+
|
|
249
|
+
| Role | ILIKE Pattern |
|
|
250
|
+
|------|---------------|
|
|
251
|
+
| C-Suite | `ceo%`, `cto%`, `cfo%`, `%chief%` |
|
|
252
|
+
| VPs | `%vp %`, `%vice president%` |
|
|
253
|
+
| Directors | `%director%`, `%head of%` |
|
|
254
|
+
| Sales | `%account exec%`, `%sales rep%`, `%ae %` |
|
|
255
|
+
| SDRs | `%sales development%`, `%sdr%`, `%bdr%` |
|
|
256
|
+
| Engineering | `%engineer%`, `%developer%` |
|
|
257
|
+
| Recruiters | `%recruit%`, `%talent%`, `%sourcer%` |
|
|
258
|
+
|
|
259
|
+
## What You Cannot Do
|
|
260
|
+
|
|
261
|
+
ā **No direct contact data** - email addresses and phone numbers are restricted
|
|
262
|
+
ā **No Indeed data** - Indeed tables are restricted
|
|
263
|
+
ā **No traffic/web data** - Domain traffic and web analytics restricted
|
|
264
|
+
|
|
265
|
+
## Rate Limits
|
|
266
|
+
|
|
267
|
+
The `orangeslice` package automatically handles rate limiting:
|
|
268
|
+
|
|
269
|
+
| Function | Concurrency | Min Delay |
|
|
270
|
+
|----------|-------------|-----------|
|
|
271
|
+
| `b2b` | 2 concurrent | 100ms |
|
|
272
|
+
| `serp` | 2 concurrent | 200ms |
|
|
273
|
+
| `firecrawl` | 2 concurrent | 500ms |
|
|
274
|
+
|
|
275
|
+
You can fire off many calls - they'll be queued automatically.
|
|
276
|
+
|
|
277
|
+
## Detailed Documentation
|
|
278
|
+
|
|
279
|
+
For comprehensive schema and query patterns, see:
|
|
280
|
+
- `B2B_DATABASE.md` - Full database guide with examples
|
|
281
|
+
- `B2B_SCHEMA.md` - Complete table schemas
|
|
282
|
+
- `B2B_EMPLOYEE_SEARCH.md` - Finding employees by title
|
|
283
|
+
|
|
284
|
+
## Example Session
|
|
285
|
+
|
|
286
|
+
**User:** "Research Ramp - give me everything"
|
|
287
|
+
|
|
288
|
+
**Agent:**
|
|
289
|
+
```typescript
|
|
290
|
+
import { orangeslice } from 'orangeslice';
|
|
291
|
+
|
|
292
|
+
// 1. B2B Database - Company info + leadership
|
|
293
|
+
const company = await orangeslice.b2b.sql(`
|
|
294
|
+
SELECT id, company_name, domain, employee_count, locality, description
|
|
295
|
+
FROM linkedin_company WHERE domain = 'ramp.com'
|
|
296
|
+
`);
|
|
297
|
+
|
|
298
|
+
const leadership = await orangeslice.b2b.sql(`
|
|
299
|
+
SELECT lp.first_name, lp.last_name, lp.headline, pos.title
|
|
300
|
+
FROM linkedin_profile lp
|
|
301
|
+
JOIN linkedin_profile_position3 pos ON pos.linkedin_profile_id = lp.id
|
|
302
|
+
WHERE pos.linkedin_company_id = 1406226
|
|
303
|
+
AND pos.end_date IS NULL
|
|
304
|
+
AND (pos.title ILIKE 'ceo%' OR pos.title ILIKE 'cto%' OR pos.title ILIKE '%vp%')
|
|
305
|
+
LIMIT 20
|
|
306
|
+
`);
|
|
307
|
+
|
|
308
|
+
// 2. Google Search - Recent news
|
|
309
|
+
const news = await orangeslice.serp.search("Ramp fintech funding 2024", { tbs: "qdr:m" });
|
|
310
|
+
|
|
311
|
+
// 3. Website Scraping - About page + socials
|
|
312
|
+
const about = await orangeslice.firecrawl.scrape("https://ramp.com/about");
|
|
313
|
+
console.log(about.markdown); // Company description
|
|
314
|
+
console.log(about.socialUrls); // LinkedIn, Twitter, etc.
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
**Start by understanding what the user wants to research, then use the appropriate tools to find the information.**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orangeslice",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Turn any AI agent into a B2B sales research assistant with 1B+ LinkedIn profiles",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
"b2b",
|
|
22
22
|
"rate-limit",
|
|
23
23
|
"ai-agent",
|
|
24
|
-
"linkedin"
|
|
24
|
+
"linkedin",
|
|
25
|
+
"sales-agent",
|
|
26
|
+
"claude-code",
|
|
27
|
+
"cursor",
|
|
28
|
+
"mcp"
|
|
25
29
|
],
|
|
26
30
|
"author": "",
|
|
27
31
|
"license": "MIT",
|