bereach-openclaw 1.4.2 → 1.4.4
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/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/skills/bereach/SKILL.md +54 -11
- package/skills/bereach/sdk-reference.md +3 -3
- package/skills/bereach/workspace/soul-template.md +2 -1
- package/src/commands/index.ts +11 -60
- package/src/hooks/cache.ts +48 -297
- package/src/hooks/context.ts +341 -237
- package/src/hooks/enforcement.ts +26 -15
- package/src/hooks/lifecycle.ts +5 -236
- package/src/hooks/tracking.ts +32 -289
- package/src/hooks/types.ts +16 -73
- package/src/index.ts +5 -99
- package/src/soul-template-content.ts +2 -2
- package/src/tools/definitions.ts +230 -165
- package/src/tools/index.ts +18 -75
- package/src/client.ts +0 -31
- package/src/hooks/context-compat.ts +0 -24
- package/src/lead-magnet/helpers.ts +0 -212
- package/src/test-sdk-methods.mts +0 -232
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bereach-openclaw",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "BeReach LinkedIn automation plugin for OpenClaw",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"exports": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"bereach"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"bereach": "1.4.
|
|
21
|
+
"bereach": "^1.4.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.5.0",
|
package/skills/bereach/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bereach
|
|
3
3
|
description: "Automate LinkedIn outreach via BeReach (berea.ch). Use when: prospecting, engaging posts, scraping engagement, searching LinkedIn, managing inbox, running campaigns, managing invitations, analytics, company pages, Sales Navigator, content engagement, feed monitoring. Requires BEREACH_API_KEY."
|
|
4
|
-
lastUpdatedAt:
|
|
4
|
+
lastUpdatedAt: 1774303632
|
|
5
5
|
metadata: { "openclaw": { "requires": { "env": ["BEREACH_API_KEY"] }, "primaryEnv": "BEREACH_API_KEY" } }
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -26,7 +26,7 @@ Load sub-skills **on-demand** when the user's request matches a workflow. No nee
|
|
|
26
26
|
| Lead Gen | lead gen, find leads, search, qualify, ICP, pipeline, build list, scrape engagement, competitor, grow database, prospecting, hashtag, Sales Navigator | sub/lead-gen.md | 1774186410 |
|
|
27
27
|
| Outreach | outreach, connect, DM, message, follow up, sequence, connection request, reply, personalized outreach, campaign outreach, warming, follow, like post, draft, batch, withdraw, human review | sub/outreach.md | 1774186410 |
|
|
28
28
|
| OpenClaw Optimization | openclaw, model, opus, sonnet, haiku, config, SOUL.md, heartbeat, prompt caching, AI cost reduction, /model | openclaw-optimization.md | 1774185819 |
|
|
29
|
-
| SDK Reference | sdk, method, parameter, signature, reference, api, script, TypeScript, generate code, automate, batch job | sdk-reference.md |
|
|
29
|
+
| SDK Reference | sdk, method, parameter, signature, reference, api, script, TypeScript, generate code, automate, batch job | sdk-reference.md | 1774303632 |
|
|
30
30
|
|
|
31
31
|
### Workspace Templates
|
|
32
32
|
|
|
@@ -43,22 +43,55 @@ At install or upgrade, set up the workspace with these templates:
|
|
|
43
43
|
|
|
44
44
|
Credits, limits, pipeline stats, campaign state, session recovery, and pending actions are automatically injected into your context at every session start. You do NOT need to manually fetch any of this.
|
|
45
45
|
|
|
46
|
-
**
|
|
46
|
+
**Principle:** Make the user successful before mentioning payment. The onboarding ends with: a saved ICP, a campaign with first contacts, a real action taken on LinkedIn, and awareness of the upgrade path.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
**First interaction (new user) — 3-phase flow:**
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
1. **Phase 1 — Know You** (automatic, no questions):
|
|
51
|
+
- The user's profile is auto-initialized. Narrate what you found (name, role, company).
|
|
52
|
+
- Use `bereach_get_own_posts` (count: 5) to understand their content and topics.
|
|
53
|
+
- Detect language from profile + posts. Respond in that language going forward.
|
|
54
|
+
- Keep it brief: 2-3 sentences summarizing who they are and what they do.
|
|
55
|
+
|
|
56
|
+
2. **Phase 2 — Reveal & ICP**:
|
|
57
|
+
- Propose an Ideal Customer Profile based on their profile: job titles, industries, company size, locations, pain points.
|
|
58
|
+
- Show a "missed opportunity" insight using `bereach_get_profile_views` or engagement scraping on their latest post.
|
|
59
|
+
- Wait for user confirmation. Save via `bereach_context_set({ type: "icp", scope: "user" })`.
|
|
60
|
+
- Include link: "You can edit your ICP anytime in [Context](context-link)."
|
|
61
|
+
|
|
62
|
+
3. **Phase 3 — First Quick Win**:
|
|
63
|
+
- Find 5 leads matching ICP via `bereach_search_people`. Visit top 3 with `bereach_visit_profile`.
|
|
64
|
+
- Present leads with: name, title, company, why they're a fit, suggested approach.
|
|
65
|
+
- Offer the user a choice: send a connection request, warm up with a like/comment, or save to a campaign.
|
|
66
|
+
- Execute the chosen action. Include links: "See your leads in [Pipeline](pipeline-link)." / "Track in [Campaigns](campaigns-link)."
|
|
67
|
+
|
|
68
|
+
4. **Soft upgrade awareness** (free plan users only, NOT a gate):
|
|
69
|
+
- After the quick win: "You have 100 free credits. When you're ready to scale, Unlimited gives you unlimited credits and daily automation. [See plans](pricing-link). What would you like to work on next?"
|
|
70
|
+
- The conversation continues. The user is not blocked.
|
|
71
|
+
|
|
72
|
+
5. Mark onboarding complete: `bereach_state_set({ key: "onboarding", data: { completed: true } })`
|
|
57
73
|
|
|
58
74
|
**Returning user:**
|
|
59
75
|
|
|
60
76
|
Check the Live Status (auto-injected). Surface pending actions (drafts, unread DMs) and resume from where the last session left off.
|
|
61
77
|
|
|
78
|
+
## Upgrade Messaging
|
|
79
|
+
|
|
80
|
+
Upgrade mentions are **contextual** — triggered by usage signals, never hard-gated during onboarding. The free plan includes 100 credits/month, enough for meaningful work.
|
|
81
|
+
|
|
82
|
+
**Rules:**
|
|
83
|
+
- Never block the user from working. After mentioning upgrade, always continue helping.
|
|
84
|
+
- Tone: confident but not pushy. "You've seen what I can do. Unlimited unlocks the full engine."
|
|
85
|
+
- Always include the pricing link from Dashboard Links when mentioning upgrade.
|
|
86
|
+
- Only mention upgrade for free plan users. Paid users never see upgrade prompts.
|
|
87
|
+
|
|
88
|
+
**Templates by trigger:**
|
|
89
|
+
- **Credits running low** (80+ of 100 used): "You've used [X] of your 100 free credits. At this pace, you'll run out in ~[Y] days. [See plans](pricing-link)"
|
|
90
|
+
- **Credits exhausted** (100/100): "You've used all 100 credits this month. You can still manage contacts, read inbox, and review drafts (0-credit tools). [Upgrade for unlimited](pricing-link)"
|
|
91
|
+
- **First reply received**: "[Name] replied! Imagine 50 conversations like this per week. [Scale your outreach](pricing-link)"
|
|
92
|
+
- **Weekly recap** (active user): "This week: [X] leads found, [Y] connections sent, [Z] replies. [Unlock daily automation](pricing-link)"
|
|
93
|
+
- **Daily automation requested on free plan**: "Daily automation is available on the Unlimited plan. [See plans](pricing-link)"
|
|
94
|
+
|
|
62
95
|
## Installation
|
|
63
96
|
|
|
64
97
|
### Tools (interactive use)
|
|
@@ -109,6 +142,16 @@ The SDK auto-reads `BEREACH_API_KEY` from the environment. NEVER hardcode tokens
|
|
|
109
142
|
2. **Step 2** — Run `npx tsx <script-name>.ts`. Fix runtime errors.
|
|
110
143
|
3. For production scripts, both should pass. For quick one-off scripts, tsx alone is acceptable.
|
|
111
144
|
|
|
145
|
+
### Dashboard Links in Responses
|
|
146
|
+
|
|
147
|
+
When mentioning any of these concepts in conversation, ALWAYS include the matching clickable link from the Dashboard Links section of your live status:
|
|
148
|
+
- Leads, contacts, funnel, pipeline → [Pipeline](link)
|
|
149
|
+
- Campaigns → [Campaigns](link)
|
|
150
|
+
- Drafts, scheduled messages → [Drafts](link)
|
|
151
|
+
- ICP, tone, playbook, context → [Context](link)
|
|
152
|
+
- Settings, account, LinkedIn connection → [Settings](link)
|
|
153
|
+
- Upgrade, pricing, plans, credits limit → [Pricing](link)
|
|
154
|
+
|
|
112
155
|
### Always
|
|
113
156
|
|
|
114
157
|
- NEVER hardcode tokens. Read from `process.env.BEREACH_API_KEY`. If not set, exit immediately.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bereach-sdk-reference
|
|
3
3
|
description: "Complete SDK v1.4.0 method reference — parameters, types, and descriptions for all BeReach operations."
|
|
4
|
-
lastUpdatedAt:
|
|
4
|
+
lastUpdatedAt: 1774303632
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!--
|
|
@@ -488,7 +488,7 @@ Actions are rate-limited per LinkedIn account. Limits scale with the workspace m
|
|
|
488
488
|
|
|
489
489
|
The `retryAfter` field in every response tells you how long to wait before the next call.
|
|
490
490
|
|
|
491
|
-
**Important:** `createComment` has a strict
|
|
491
|
+
**Important:** `createComment` has a strict 10/day limit because commenting on others' posts is the highest-risk action for LinkedIn detection. Plan campaigns accordingly.
|
|
492
492
|
|
|
493
493
|
**Note:** The `engagement` bucket has no daily or weekly cap — only a minimum interval between calls. This keeps lightweight actions (like, save, follow) unrestricted while preventing rapid-fire API abuse.
|
|
494
494
|
|
|
@@ -624,7 +624,7 @@ Publish or schedule a LinkedIn post. Supports text, images, mentions, and visibi
|
|
|
624
624
|
|
|
625
625
|
### createComment
|
|
626
626
|
|
|
627
|
-
Add a top-level comment on a LinkedIn post. Deduplicates by post within a campaign. Rate limited to
|
|
627
|
+
Add a top-level comment on a LinkedIn post. Deduplicates by post within a campaign. Rate limited to 10/day (comment_post bucket).
|
|
628
628
|
|
|
629
629
|
`client.actions.createComment(params)`
|
|
630
630
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
lastUpdatedAt:
|
|
2
|
+
lastUpdatedAt: 1774303632
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
<!--
|
|
@@ -194,6 +194,7 @@ Credits, rate limits, pipeline metrics, user context (ICP, tone), and session re
|
|
|
194
194
|
- State saves: only save pipeline progress (phase, scraped sources) to agentState. Never store profile data in state.
|
|
195
195
|
- Enforcement: pacing, credit guards, rate limit guards, doNotContact checks, and visit-before-connect are handled automatically. Focus on strategy, not mechanics.
|
|
196
196
|
- Context extraction: when the user provides a prompt with outreach instructions, tone, message templates, or ICP criteria, ALWAYS extract and save them as campaign-scoped context entries (playbook, tone-voice, icp). Never lose user instructions — persist them.
|
|
197
|
+
- Tone-voice auto-inference: when no `tone-voice` context exists for the active account, proactively analyze the user's recent posts (5-10), sent DMs (3-5 conversations), and comments (10-20) to infer their communication style. Save the result as `tone-voice` context with `scope: "user"`. Do this silently without asking the user. If there is not enough data (new account), skip silently.
|
|
197
198
|
|
|
198
199
|
## Sub-Skills — MANDATORY Loading Table
|
|
199
200
|
|
package/src/commands/index.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { apiFetch } from "../hooks/cache";
|
|
2
2
|
import type { SessionState } from "../hooks/types";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Auto-reply commands execute without invoking the LLM.
|
|
7
|
-
* Client is created lazily on first command invocation.
|
|
8
|
-
*/
|
|
4
|
+
const API_KEY = process.env.BEREACH_API_KEY ?? "";
|
|
5
|
+
|
|
9
6
|
export function registerCommands(api: any, state?: SessionState) {
|
|
10
7
|
if (typeof api?.registerCommand !== "function") return;
|
|
11
8
|
|
|
@@ -13,16 +10,13 @@ export function registerCommands(api: any, state?: SessionState) {
|
|
|
13
10
|
name: "bereach-credits",
|
|
14
11
|
description: "Show current BeReach credit balance",
|
|
15
12
|
handler: async () => {
|
|
16
|
-
const
|
|
17
|
-
|
|
13
|
+
const res = await apiFetch<{ credits: { current: number; limit: number | null; remaining: number | null; isUnlimited: boolean; percentage: number } }>("/api/me/credits", API_KEY);
|
|
14
|
+
if (!res) return { text: "Failed to fetch credits." };
|
|
18
15
|
const c = res.credits;
|
|
19
16
|
return {
|
|
20
17
|
text: c.isUnlimited
|
|
21
18
|
? `BeReach Credits: Unlimited (${c.current} used)`
|
|
22
|
-
:
|
|
23
|
-
`BeReach Credits: ${c.current}/${c.limit} used (${c.percentage}%)`,
|
|
24
|
-
`Remaining: ${c.remaining}`,
|
|
25
|
-
].join("\n"),
|
|
19
|
+
: `BeReach Credits: ${c.current}/${c.limit} used (${c.percentage}%)\nRemaining: ${c.remaining}`,
|
|
26
20
|
};
|
|
27
21
|
},
|
|
28
22
|
});
|
|
@@ -31,33 +25,13 @@ export function registerCommands(api: any, state?: SessionState) {
|
|
|
31
25
|
name: "bereach-status",
|
|
32
26
|
description: "Show LinkedIn rate limit summary",
|
|
33
27
|
handler: async () => {
|
|
34
|
-
const
|
|
35
|
-
|
|
28
|
+
const res = await apiFetch<any>("/api/rate-limits", API_KEY);
|
|
29
|
+
if (!res) return { text: "Failed to fetch limits." };
|
|
30
|
+
const limits = res?.linkedIn?.limits ?? res?.limits ?? {};
|
|
36
31
|
const lines = ["BeReach Rate Limits:"];
|
|
37
|
-
for (const [action, data] of Object.entries(
|
|
38
|
-
const d = data.daily;
|
|
39
|
-
lines.push(` ${action}: ${d.current}/${d.limit} daily (${d.remaining} remaining)`);
|
|
40
|
-
}
|
|
41
|
-
return { text: lines.join("\n") };
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
api.registerCommand({
|
|
46
|
-
name: "bereach-limits",
|
|
47
|
-
description: "Show detailed per-action LinkedIn rate limits",
|
|
48
|
-
handler: async () => {
|
|
49
|
-
const client = getClient();
|
|
50
|
-
const res = await client.profile.getLimits();
|
|
51
|
-
const lines = [`BeReach Rate Limits (multiplier: ${res.multiplier}x):`];
|
|
52
|
-
for (const [action, data] of Object.entries(res.limits) as [string, any][]) {
|
|
32
|
+
for (const [action, data] of Object.entries(limits) as [string, any][]) {
|
|
53
33
|
const d = data.daily;
|
|
54
|
-
|
|
55
|
-
lines.push(`\n ${action}:`);
|
|
56
|
-
lines.push(` Daily: ${d.current}/${d.limit} (${d.remaining} remaining, resets ${data.nextResetDaily})`);
|
|
57
|
-
if (w) {
|
|
58
|
-
lines.push(` Weekly: ${w.current}/${w.limit} (${w.remaining} remaining, resets ${data.nextResetWeekly})`);
|
|
59
|
-
}
|
|
60
|
-
lines.push(` Min interval: ${data.minIntervalSeconds}s`);
|
|
34
|
+
if (d) lines.push(` ${action}: ${d.current}/${d.limit} daily (${d.remaining} remaining)`);
|
|
61
35
|
}
|
|
62
36
|
return { text: lines.join("\n") };
|
|
63
37
|
},
|
|
@@ -78,33 +52,10 @@ export function registerCommands(api: any, state?: SessionState) {
|
|
|
78
52
|
lines.push(` ${reason}: ${count}`);
|
|
79
53
|
}
|
|
80
54
|
}
|
|
81
|
-
if (state.engagementSignals.size > 0) {
|
|
82
|
-
lines.push(` Engagement signals tracked: ${state.engagementSignals.size} profiles`);
|
|
83
|
-
}
|
|
84
55
|
} else {
|
|
85
56
|
lines.push(" No session data available yet.");
|
|
86
57
|
}
|
|
87
58
|
return { text: lines.join("\n") };
|
|
88
59
|
},
|
|
89
60
|
});
|
|
90
|
-
|
|
91
|
-
api.registerCli(
|
|
92
|
-
({ program }: { program: any }) => {
|
|
93
|
-
program
|
|
94
|
-
.command("bereach")
|
|
95
|
-
.description("BeReach status commands")
|
|
96
|
-
.command("status")
|
|
97
|
-
.description("Show LinkedIn rate limit summary")
|
|
98
|
-
.action(async () => {
|
|
99
|
-
const client = getClient();
|
|
100
|
-
const res = await client.profile.getLimits();
|
|
101
|
-
console.log("BeReach Rate Limits:");
|
|
102
|
-
for (const [action, data] of Object.entries(res.limits) as [string, any][]) {
|
|
103
|
-
const d = data.daily;
|
|
104
|
-
console.log(` ${action}: ${d.current}/${d.limit} daily (${d.remaining} remaining)`);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
},
|
|
108
|
-
{ commands: ["bereach"] },
|
|
109
|
-
);
|
|
110
61
|
}
|