orangeslice 1.8.4 → 1.8.6-beta.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/dist/api.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export interface OrangesliceConfig {
2
+ apiKey?: string;
3
+ baseUrl?: string;
4
+ }
5
+ export declare function configure(opts: OrangesliceConfig): void;
1
6
  interface PostOptions {
2
7
  direct?: boolean;
3
8
  }
package/dist/api.js CHANGED
@@ -1,14 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configure = configure;
3
4
  exports.post = post;
4
5
  /**
5
6
  * Batch-only routing:
6
7
  * - Submit all calls to Railway batch-service /function
7
8
  * - Poll batch-service /function/result on pending responses
8
9
  */
9
- const BASE_URL = "https://enrichly-production.up.railway.app/function";
10
+ const DEFAULT_BASE_URL = "https://enrichly-production.up.railway.app/function";
10
11
  const POLL_TIMEOUT_MS = 600000;
11
12
  const DEFAULT_POLL_INTERVAL_MS = 1000;
13
+ const _config = {};
14
+ function configure(opts) {
15
+ if (opts.apiKey !== undefined)
16
+ _config.apiKey = opts.apiKey;
17
+ if (opts.baseUrl !== undefined)
18
+ _config.baseUrl = opts.baseUrl;
19
+ }
20
+ function resolveBaseUrl() {
21
+ return _config.baseUrl || process.env.ORANGESLICE_BASE_URL || DEFAULT_BASE_URL;
22
+ }
23
+ function resolveApiKey() {
24
+ return _config.apiKey || process.env.ORANGESLICE_API_KEY || "";
25
+ }
12
26
  function sleep(ms) {
13
27
  return new Promise((resolve) => setTimeout(resolve, ms));
14
28
  }
@@ -102,14 +116,22 @@ async function pollBatchUntilComplete(baseUrl, functionId, pending) {
102
116
  throw new Error(`[orangeslice] ${functionId}: polling timed out after ${POLL_TIMEOUT_MS}ms`);
103
117
  }
104
118
  async function post(functionId, payload, options = {}) {
105
- // Kept for backwards compatibility with older call sites.
106
119
  void options;
107
- const baseUrl = BASE_URL;
120
+ const baseUrl = resolveBaseUrl();
121
+ const apiKey = resolveApiKey();
122
+ if (!apiKey) {
123
+ throw new Error("[orangeslice] No API key configured. " +
124
+ "Set ORANGESLICE_API_KEY in your environment or call configure({ apiKey: 'osk_...' }).");
125
+ }
108
126
  const url = `${baseUrl}?functionId=${functionId}`;
109
127
  const body = JSON.stringify(payload);
128
+ const headers = {
129
+ "Content-Type": "application/json",
130
+ Authorization: `Bearer ${apiKey}`
131
+ };
110
132
  const res = await fetchWithRedirect(url, {
111
133
  method: "POST",
112
- headers: { "Content-Type": "application/json" },
134
+ headers,
113
135
  body
114
136
  });
115
137
  if (!res.ok) {
package/dist/cli.js CHANGED
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const child_process_1 = require("child_process");
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
+ const readline = __importStar(require("readline"));
40
41
  const LEGACY_DOCS_DIR = path.join(__dirname, "..", "docs");
41
42
  const TARGET_DIR = path.join(process.cwd(), "orangeslice-docs");
42
43
  const AGENTS_FILE = path.join(TARGET_DIR, "AGENTS.md");
@@ -110,6 +111,74 @@ function installOrangeslice(cwd) {
110
111
  console.log(" Installing orangeslice...");
111
112
  (0, child_process_1.execSync)("npm install orangeslice", { stdio: "inherit", cwd });
112
113
  }
114
+ function prompt(question) {
115
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
116
+ return new Promise((resolve) => {
117
+ rl.question(question, (answer) => {
118
+ rl.close();
119
+ resolve(answer.trim());
120
+ });
121
+ });
122
+ }
123
+ function readEnvFile(envPath) {
124
+ const entries = new Map();
125
+ if (!fs.existsSync(envPath))
126
+ return entries;
127
+ const lines = fs.readFileSync(envPath, "utf8").split("\n");
128
+ for (const line of lines) {
129
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)/);
130
+ if (match)
131
+ entries.set(match[1], match[2]);
132
+ }
133
+ return entries;
134
+ }
135
+ function writeEnvVar(envPath, key, value) {
136
+ const entries = readEnvFile(envPath);
137
+ entries.set(key, value);
138
+ const lines = [];
139
+ if (fs.existsSync(envPath)) {
140
+ const original = fs.readFileSync(envPath, "utf8").split("\n");
141
+ let replaced = false;
142
+ for (const line of original) {
143
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)/);
144
+ if (match && match[1] === key) {
145
+ lines.push(`${key}=${value}`);
146
+ replaced = true;
147
+ }
148
+ else {
149
+ lines.push(line);
150
+ }
151
+ }
152
+ if (!replaced) {
153
+ if (lines.length > 0 && lines[lines.length - 1] !== "")
154
+ lines.push("");
155
+ lines.push(`${key}=${value}`);
156
+ }
157
+ }
158
+ else {
159
+ lines.push(`${key}=${value}`, "");
160
+ }
161
+ fs.writeFileSync(envPath, lines.join("\n"), "utf8");
162
+ }
163
+ async function setupApiKey(cwd) {
164
+ const envPath = path.join(cwd, ".env");
165
+ const existing = readEnvFile(envPath).get("ORANGESLICE_API_KEY");
166
+ if (existing) {
167
+ console.log(` ✓ API key already configured in .env (${existing.slice(0, 12)}...)\n`);
168
+ return;
169
+ }
170
+ console.log(" API key required. Get one at: https://www.orangeslice.ai/dashboard/api-keys\n");
171
+ const key = await prompt(" Paste your API key (osk_...): ");
172
+ if (!key) {
173
+ console.log("\n ⚠ Skipped. Set ORANGESLICE_API_KEY in .env or call configure({ apiKey }) before using the SDK.\n");
174
+ return;
175
+ }
176
+ if (!key.startsWith("osk_")) {
177
+ console.log("\n ⚠ Key doesn't start with osk_ — saving anyway. Double-check it's correct.\n");
178
+ }
179
+ writeEnvVar(envPath, "ORANGESLICE_API_KEY", key);
180
+ console.log(`\n ✓ API key saved to .env\n`);
181
+ }
113
182
  async function main() {
114
183
  console.log("\norangeslice\n");
115
184
  const docsDir = resolveDocsDir();
@@ -124,29 +193,27 @@ async function main() {
124
193
  ensurePackageJson(cwd);
125
194
  installOrangeslice(cwd);
126
195
  console.log(" ✓ Package installed in current directory\n");
196
+ // API key setup
197
+ await setupApiKey(cwd);
127
198
  console.log("\nReady - services-style API\n");
128
- console.log(" import { services } from 'orangeslice';\n");
199
+ console.log(" import { configure, services } from 'orangeslice';\n");
200
+ console.log(" // Option A: env var (loaded automatically from .env by your framework)");
201
+ console.log(" // ORANGESLICE_API_KEY=osk_... in .env\n");
202
+ console.log(" // Option B: programmatic");
203
+ console.log(" configure({ apiKey: 'osk_...' });\n");
129
204
  console.log(" Agent setup (do this first):");
130
205
  console.log(" Ask your agent to read:");
131
206
  console.log(" 1) ./orangeslice-docs/AGENTS.md");
132
207
  console.log(" 2) ./orangeslice-docs/services/index.md");
133
208
  console.log(' Then tell it: "Use these docs as source of truth for all orangeslice operations."\n');
134
- console.log(" Routing note:");
135
- console.log(" - all services submit to the batch-service /function endpoint");
136
- console.log(" - pending jobs poll batch-service /function/result endpoints");
137
- console.log(" - this package currently includes only batch-backed services\n");
138
209
  console.log(" // LinkedIn B2B SQL");
139
210
  console.log(' const { rows } = await services.company.linkedin.search({ sql: "SELECT * FROM linkedin_company LIMIT 10" });\n');
140
211
  console.log(" // Web search");
141
212
  console.log(" const page = await services.web.search({ query: 'best CRM software' });\n");
142
- console.log(" // Batched web search");
143
- console.log(" const pages = await services.web.batchSearch({ queries: [{ query: 'site:linkedin.com/in stripe' }] });\n");
144
213
  console.log(" // AI structured output");
145
214
  console.log(" const { object } = await services.ai.generateObject({ prompt: '...', schema: {...} });\n");
146
215
  console.log(" // Browser automation (Kernel)");
147
216
  console.log(' const browser = await services.browser.execute({ code: "return await page.title();" });\n');
148
- console.log(" // Apify actor");
149
- console.log(" const actor = await services.apify.runActor({ actor: 'apify/web-scraper', input: {} });\n");
150
217
  console.log(" Always parallelize independent calls with Promise.all.\n");
151
218
  }
152
219
  main().catch(console.error);
@@ -28,8 +28,51 @@ export interface CompanyGetEmployeesFromLinkedinResult {
28
28
  nextPage: number | null;
29
29
  totalResults: number | null;
30
30
  }
31
+ export interface PersonLinkedinFindUrlParams {
32
+ name?: string;
33
+ title?: string;
34
+ company?: string;
35
+ keyword?: string;
36
+ location?: string;
37
+ }
38
+ export interface CompanyLinkedinFindUrlParams {
39
+ companyName?: string;
40
+ website?: string;
41
+ location?: string;
42
+ }
43
+ export interface PersonContactGetParams {
44
+ linkedinUrl?: string;
45
+ required: Array<"email" | "work_email" | "phone">;
46
+ sources?: Array<"bcr">;
47
+ maxCoverage?: boolean;
48
+ domain?: string;
49
+ firstName?: string;
50
+ lastName?: string;
51
+ company?: string;
52
+ phoneNumber?: string;
53
+ userId?: string;
54
+ }
55
+ export interface PersonContactGetResult {
56
+ work_emails: string[];
57
+ work_phones: string[];
58
+ personal_emails: string[];
59
+ personal_phones: string[];
60
+ unknown_phones: string[];
61
+ }
31
62
  export declare function personLinkedinEnrich(params: Record<string, unknown>): Promise<unknown>;
32
63
  export declare function companyLinkedinEnrich(params: Record<string, unknown>): Promise<unknown>;
64
+ /**
65
+ * Find a LinkedIn person profile URL from name/title/company context.
66
+ */
67
+ export declare function personLinkedinFindUrl(params: PersonLinkedinFindUrlParams): Promise<string | null>;
68
+ /**
69
+ * Find a LinkedIn company URL from website/company context.
70
+ */
71
+ export declare function companyLinkedinFindUrl(params: CompanyLinkedinFindUrlParams): Promise<string | null>;
72
+ /**
73
+ * Run contact waterfall through Inngest and poll until completion.
74
+ */
75
+ export declare function personContactGet(params: PersonContactGetParams): Promise<PersonContactGetResult>;
33
76
  export declare function companyGetEmployeesFromLinkedin(params: CompanyGetEmployeesFromLinkedinParams): Promise<CompanyGetEmployeesFromLinkedinResult>;
34
77
  export declare function geoParseAddress(params: Record<string, unknown>): Promise<unknown>;
35
78
  export declare function builtWithLookupDomain(params: Record<string, unknown>): Promise<unknown>;
package/dist/expansion.js CHANGED
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.personLinkedinEnrich = personLinkedinEnrich;
4
4
  exports.companyLinkedinEnrich = companyLinkedinEnrich;
5
+ exports.personLinkedinFindUrl = personLinkedinFindUrl;
6
+ exports.companyLinkedinFindUrl = companyLinkedinFindUrl;
7
+ exports.personContactGet = personContactGet;
5
8
  exports.companyGetEmployeesFromLinkedin = companyGetEmployeesFromLinkedin;
6
9
  exports.geoParseAddress = geoParseAddress;
7
10
  exports.builtWithLookupDomain = builtWithLookupDomain;
@@ -85,6 +88,26 @@ async function companyLinkedinEnrich(params) {
85
88
  const data = await (0, api_1.post)("b2b", { sql }, { direct: true });
86
89
  return data.rows?.[0] ?? null;
87
90
  }
91
+ /**
92
+ * Find a LinkedIn person profile URL from name/title/company context.
93
+ */
94
+ async function personLinkedinFindUrl(params) {
95
+ const url = await (0, api_1.post)("linkedinFindProfileUrl", params);
96
+ return typeof url === "string" && url.trim().length > 0 ? url : null;
97
+ }
98
+ /**
99
+ * Find a LinkedIn company URL from website/company context.
100
+ */
101
+ async function companyLinkedinFindUrl(params) {
102
+ const url = await (0, api_1.post)("findLinkedinCompanyUrl", params);
103
+ return typeof url === "string" && url.trim().length > 0 ? url : null;
104
+ }
105
+ /**
106
+ * Run contact waterfall through Inngest and poll until completion.
107
+ */
108
+ async function personContactGet(params) {
109
+ return (0, api_1.post)("contactInfoWaterfall", { ...params });
110
+ }
88
111
  async function companyGetEmployeesFromLinkedin(params) {
89
112
  return (0, api_1.post)("b2b-get-employees-for-company", params);
90
113
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { configure } from "./api";
2
+ export type { OrangesliceConfig } from "./api";
1
3
  export { linkedinSearch } from "./b2b";
2
4
  export type { LinkedInSearchParams, LinkedInSearchResponse } from "./b2b";
3
5
  export { webSearch, webBatchSearch } from "./serp";
@@ -12,19 +14,20 @@ export { runApifyActor } from "./apify";
12
14
  export type { RunActorParams, RunActorResult, DatasetListParams } from "./apify";
13
15
  export { googleMapsScrape } from "./googleMaps";
14
16
  export type { GoogleMapsScrapeParams } from "./googleMaps";
15
- export { personLinkedinEnrich, companyLinkedinEnrich, companyGetEmployeesFromLinkedin, geoParseAddress, builtWithLookupDomain, builtWithRelationships, builtWithSearchByTech } from "./expansion";
16
- export type { CompanyGetEmployeesFromLinkedinParams, CompanyGetEmployeesFromLinkedinResult, CompanyEmployeeFromB2B } from "./expansion";
17
+ export { personLinkedinEnrich, personLinkedinFindUrl, personContactGet, companyLinkedinEnrich, companyLinkedinFindUrl, companyGetEmployeesFromLinkedin, geoParseAddress, builtWithLookupDomain, builtWithRelationships, builtWithSearchByTech } from "./expansion";
18
+ export type { PersonLinkedinFindUrlParams, CompanyLinkedinFindUrlParams, PersonContactGetParams, PersonContactGetResult, CompanyGetEmployeesFromLinkedinParams, CompanyGetEmployeesFromLinkedinResult, CompanyEmployeeFromB2B } from "./expansion";
19
+ import { runApifyActor } from "./apify";
17
20
  import { linkedinSearch } from "./b2b";
18
- import { webBatchSearch, webSearch } from "./serp";
19
- import { generateObject } from "./generateObject";
20
- import { scrapeWebsite } from "./firecrawl";
21
21
  import { browserExecute } from "./browser";
22
- import { runApifyActor } from "./apify";
22
+ import { personLinkedinEnrich, personLinkedinFindUrl, personContactGet, companyLinkedinEnrich, companyLinkedinFindUrl, companyGetEmployeesFromLinkedin, geoParseAddress, builtWithLookupDomain, builtWithRelationships, builtWithSearchByTech } from "./expansion";
23
+ import { scrapeWebsite } from "./firecrawl";
24
+ import { generateObject } from "./generateObject";
23
25
  import { googleMapsScrape } from "./googleMaps";
24
- import { personLinkedinEnrich, companyLinkedinEnrich, companyGetEmployeesFromLinkedin, geoParseAddress, builtWithLookupDomain, builtWithRelationships, builtWithSearchByTech } from "./expansion";
26
+ import { webBatchSearch, webSearch } from "./serp";
25
27
  export declare const services: {
26
28
  company: {
27
29
  linkedin: {
30
+ findUrl: typeof companyLinkedinFindUrl;
28
31
  enrich: typeof companyLinkedinEnrich;
29
32
  search: typeof linkedinSearch;
30
33
  };
@@ -32,9 +35,13 @@ export declare const services: {
32
35
  };
33
36
  person: {
34
37
  linkedin: {
38
+ findUrl: typeof personLinkedinFindUrl;
35
39
  enrich: typeof personLinkedinEnrich;
36
40
  search: typeof linkedinSearch;
37
41
  };
42
+ contact: {
43
+ get: typeof personContactGet;
44
+ };
38
45
  };
39
46
  web: {
40
47
  search: typeof webSearch;
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
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.companyLinkedinEnrich = exports.personLinkedinEnrich = exports.googleMapsScrape = exports.runApifyActor = exports.browserExecute = exports.scrapeWebsite = exports.generateObject = exports.webBatchSearch = exports.webSearch = exports.linkedinSearch = 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.googleMapsScrape = exports.runApifyActor = exports.browserExecute = exports.scrapeWebsite = exports.generateObject = exports.webBatchSearch = exports.webSearch = exports.linkedinSearch = exports.configure = void 0;
4
+ var api_1 = require("./api");
5
+ Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return api_1.configure; } });
4
6
  var b2b_1 = require("./b2b");
5
7
  Object.defineProperty(exports, "linkedinSearch", { enumerable: true, get: function () { return b2b_1.linkedinSearch; } });
6
8
  var serp_1 = require("./serp");
@@ -18,23 +20,27 @@ var googleMaps_1 = require("./googleMaps");
18
20
  Object.defineProperty(exports, "googleMapsScrape", { enumerable: true, get: function () { return googleMaps_1.googleMapsScrape; } });
19
21
  var expansion_1 = require("./expansion");
20
22
  Object.defineProperty(exports, "personLinkedinEnrich", { enumerable: true, get: function () { return expansion_1.personLinkedinEnrich; } });
23
+ Object.defineProperty(exports, "personLinkedinFindUrl", { enumerable: true, get: function () { return expansion_1.personLinkedinFindUrl; } });
24
+ Object.defineProperty(exports, "personContactGet", { enumerable: true, get: function () { return expansion_1.personContactGet; } });
21
25
  Object.defineProperty(exports, "companyLinkedinEnrich", { enumerable: true, get: function () { return expansion_1.companyLinkedinEnrich; } });
26
+ Object.defineProperty(exports, "companyLinkedinFindUrl", { enumerable: true, get: function () { return expansion_1.companyLinkedinFindUrl; } });
22
27
  Object.defineProperty(exports, "companyGetEmployeesFromLinkedin", { enumerable: true, get: function () { return expansion_1.companyGetEmployeesFromLinkedin; } });
23
28
  Object.defineProperty(exports, "geoParseAddress", { enumerable: true, get: function () { return expansion_1.geoParseAddress; } });
24
29
  Object.defineProperty(exports, "builtWithLookupDomain", { enumerable: true, get: function () { return expansion_1.builtWithLookupDomain; } });
25
30
  Object.defineProperty(exports, "builtWithRelationships", { enumerable: true, get: function () { return expansion_1.builtWithRelationships; } });
26
31
  Object.defineProperty(exports, "builtWithSearchByTech", { enumerable: true, get: function () { return expansion_1.builtWithSearchByTech; } });
32
+ const apify_2 = require("./apify");
27
33
  const b2b_2 = require("./b2b");
28
- const serp_2 = require("./serp");
29
- const generateObject_2 = require("./generateObject");
30
- const firecrawl_2 = require("./firecrawl");
31
34
  const browser_2 = require("./browser");
32
- const apify_2 = require("./apify");
33
- const googleMaps_2 = require("./googleMaps");
34
35
  const expansion_2 = require("./expansion");
36
+ const firecrawl_2 = require("./firecrawl");
37
+ const generateObject_2 = require("./generateObject");
38
+ const googleMaps_2 = require("./googleMaps");
39
+ const serp_2 = require("./serp");
35
40
  exports.services = {
36
41
  company: {
37
42
  linkedin: {
43
+ findUrl: expansion_2.companyLinkedinFindUrl,
38
44
  enrich: expansion_2.companyLinkedinEnrich,
39
45
  search: b2b_2.linkedinSearch
40
46
  },
@@ -42,8 +48,12 @@ exports.services = {
42
48
  },
43
49
  person: {
44
50
  linkedin: {
51
+ findUrl: expansion_2.personLinkedinFindUrl,
45
52
  enrich: expansion_2.personLinkedinEnrich,
46
53
  search: b2b_2.linkedinSearch
54
+ },
55
+ contact: {
56
+ get: expansion_2.personContactGet
47
57
  }
48
58
  },
49
59
  web: {
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Gmail email sending via Google integration
3
+ ---
4
+
5
+ # Gmail Integration
6
+
7
+ Typed functions for Gmail actions powered by Orange Slice Google integrations.
8
+
9
+ ## Email
10
+
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
@@ -0,0 +1,51 @@
1
+ # sendEmail
2
+
3
+ Send an email from the connected Gmail account.
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.
6
+
7
+ ```typescript
8
+ // Basic email
9
+ const result = await integrations.gmail.sendEmail({
10
+ recipient_email: "jane@example.com",
11
+ subject: "Hello from Orange Slice",
12
+ body: "Hi Jane, this is a test email."
13
+ });
14
+
15
+ // HTML email with additional recipients
16
+ await integrations.gmail.sendEmail({
17
+ recipient_email: "primary@example.com",
18
+ extra_recipients: ["secondary@example.com"],
19
+ cc: ["manager@example.com"],
20
+ subject: "Weekly digest",
21
+ body: "<h3>Weekly Digest</h3><p>Everything looks good.</p>",
22
+ is_html: true
23
+ });
24
+ ```
25
+
26
+ ## Input
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) |
40
+
41
+ \*Gmail requires at least one recipient (`recipient_email`, `cc`, or `bcc`) and at least one of `subject` or `body`.
42
+
43
+ ## Output
44
+
45
+ ```typescript
46
+ {
47
+ successful: boolean;
48
+ data?: unknown;
49
+ error?: string;
50
+ }
51
+ ```
@@ -148,3 +148,27 @@ const invite = await integrations.slack.conversationsInviteShared({
148
148
  ```
149
149
 
150
150
  See [slack/](./slack/) for all available functions.
151
+
152
+ ### Gmail
153
+
154
+ Send emails from connected Google Gmail accounts.
155
+
156
+ ```typescript
157
+ // Send a plain text email
158
+ const result = await integrations.gmail.sendEmail({
159
+ recipient_email: "john@example.com",
160
+ subject: "Quick update",
161
+ body: "Hey John - sharing a quick status update."
162
+ });
163
+
164
+ // Send HTML with CC recipients
165
+ await integrations.gmail.sendEmail({
166
+ recipient_email: "team@example.com",
167
+ cc: ["manager@example.com"],
168
+ subject: "Weekly summary",
169
+ body: "<h2>Weekly Summary</h2><p>All systems operational.</p>",
170
+ is_html: true
171
+ });
172
+ ```
173
+
174
+ See [gmail/](./gmail/) for available functions.
@@ -216,6 +216,16 @@ type search = (params: {
216
216
  }>;
217
217
  ```
218
218
 
219
+ ## Prospecting Continuation Trigger (Get More Leads)
220
+
221
+ For **every prospecting workflow**, aggressively default to creating a continuation trigger after the initial setup so the user can get more leads and run the workflow again without rebuilding anything.
222
+
223
+ 1. Read `triggers-runtime`
224
+ 2. Create or update a continuation trigger named `Get more <topic> leads` (fallback: `Get more leads`)
225
+ 3. Configure the trigger to continue the same prospecting strategy (next pages / additional query batches / source expansion) and run the workflow
226
+
227
+ Only skip this when the user explicitly says they do not want a continuation trigger.
228
+
219
229
  ---
220
230
 
221
231
  ## Examples
@@ -94,6 +94,7 @@ interface B2BPersonExperience {
94
94
  company_name: string;
95
95
  company_domain: string | null;
96
96
  company_linkedin_url: string | null;
97
+ company_employee_count: number | null; // Employee count of the company
97
98
  locality: string | null; // City/region of the job
98
99
  start_date: string | null; // "YYYY-MM-DD"
99
100
  start_date_year: number | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orangeslice",
3
- "version": "1.8.4",
3
+ "version": "1.8.6-beta.0",
4
4
  "description": "B2B LinkedIn database prospector - 1.15B profiles, 85M companies",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",