bereach-openclaw 0.2.7 → 0.2.10
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 +91 -9
- package/openclaw.plugin.json +8 -2
- package/package.json +2 -1
- package/scripts/test-register.ts +14 -2
- package/src/client.ts +17 -3
- package/src/commands/index.ts +7 -2
- package/src/index.ts +7 -7
- package/src/tools/index.ts +14 -9
- package/tsconfig.json +2 -1
package/README.md
CHANGED
|
@@ -8,29 +8,111 @@ LinkedIn outreach automation via [BeReach](https://berea.ch). Registers 33 in-pr
|
|
|
8
8
|
openclaw plugins install bereach-openclaw
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Upgrade
|
|
12
|
+
|
|
13
|
+
`openclaw plugins update bereach` can leave `node_modules` and `extensions/` out of sync (version mismatch → trim/crash errors). **Use uninstall + reinstall** instead:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
openclaw plugins uninstall bereach
|
|
17
|
+
openclaw plugins install bereach-openclaw
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then restart the Gateway. Verify versions match:
|
|
21
|
+
```bash
|
|
22
|
+
cat /data/.openclaw/node_modules/bereach-openclaw/package.json | grep version
|
|
23
|
+
cat /data/.openclaw/extensions/bereach/package.json | grep version
|
|
24
|
+
```
|
|
25
|
+
|
|
11
26
|
## Setup
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
The API key can be set in 3 ways (in order of precedence):
|
|
29
|
+
|
|
30
|
+
**1. OpenClaw config** (recommended) — `~/.openclaw/openclaw.json` or `/data/.openclaw/openclaw.json`:
|
|
14
31
|
|
|
15
|
-
```
|
|
32
|
+
```json
|
|
16
33
|
{
|
|
17
|
-
plugins: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
"plugins": {
|
|
35
|
+
"allow": ["bereach"],
|
|
36
|
+
"entries": {
|
|
37
|
+
"bereach": {
|
|
38
|
+
"enabled": true,
|
|
39
|
+
"config": {
|
|
40
|
+
"BEREACH_API_KEY": "brc_your_token_here"
|
|
23
41
|
}
|
|
24
42
|
}
|
|
25
43
|
}
|
|
44
|
+
},
|
|
45
|
+
"tools": {
|
|
46
|
+
"allow": ["bereach"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Note:** `plugins.allow` loads the plugin; `tools.allow` enables the tools (they are registered with `optional: true`).
|
|
52
|
+
|
|
53
|
+
**Skills + model (optional)** — `skills.entries` has no `model` field; model is set at the agent level. To use Sonnet for BeReach:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"skills": {
|
|
58
|
+
"entries": {
|
|
59
|
+
"bereach": { "enabled": true }
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"agents": {
|
|
63
|
+
"defaults": {
|
|
64
|
+
"model": { "primary": "anthropic/claude-sonnet-4-5" },
|
|
65
|
+
"models": {
|
|
66
|
+
"anthropic/claude-sonnet-4-5": { "alias": "sonnet" }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
26
69
|
}
|
|
27
70
|
}
|
|
28
71
|
```
|
|
29
72
|
|
|
30
|
-
|
|
73
|
+
**2. Environment variable** — `BEREACH_API_KEY=brc_xxx` in your shell or `export BEREACH_API_KEY="brc_xxx"` in `~/.bashrc`.
|
|
74
|
+
|
|
75
|
+
**3. Docker** — pass via `-e` when launching the container: `docker run -e BEREACH_API_KEY=brc_xxx ...`
|
|
31
76
|
|
|
32
77
|
Restart the Gateway after configuring.
|
|
33
78
|
|
|
79
|
+
## Troubleshooting
|
|
80
|
+
|
|
81
|
+
### "Cannot read properties of undefined (reading 'trim')" or "plugin disabled (not in allowlist)"
|
|
82
|
+
|
|
83
|
+
OpenClaw loads plugins from **two locations**:
|
|
84
|
+
- `node_modules/bereach-openclaw/` — the npm package (source)
|
|
85
|
+
- `extensions/bereach/` — the active copy OpenClaw uses at runtime
|
|
86
|
+
|
|
87
|
+
If `extensions/bereach/` is corrupt or incomplete, you get trim/undefined errors. Fix:
|
|
88
|
+
|
|
89
|
+
**1. Backup and remove the active extension:**
|
|
90
|
+
```bash
|
|
91
|
+
mv /data/.openclaw/extensions/bereach /data/.openclaw/extensions/bereach.bak.$(date +%s)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**2. Reinstall from npm (inside the container):**
|
|
95
|
+
```bash
|
|
96
|
+
cd /data/.openclaw
|
|
97
|
+
npm i bereach-openclaw@latest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**3. Restart OpenClaw** so it rebuilds `extensions/bereach/` from the package.
|
|
101
|
+
|
|
102
|
+
**4. If `extensions/bereach/` is still incomplete after restart**, force a symlink:
|
|
103
|
+
```bash
|
|
104
|
+
rm -rf /data/.openclaw/extensions/bereach
|
|
105
|
+
ln -s /data/.openclaw/node_modules/bereach-openclaw /data/.openclaw/extensions/bereach
|
|
106
|
+
# then restart the container
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**5. Verify** — compare the two manifests:
|
|
110
|
+
```bash
|
|
111
|
+
wc -l /data/.openclaw/extensions/bereach/openclaw.plugin.json
|
|
112
|
+
wc -l /data/.openclaw/node_modules/bereach-openclaw/openclaw.plugin.json
|
|
113
|
+
```
|
|
114
|
+
They should match. Per [OpenClaw Plugin Manifest](https://docs.openclaw.ai/plugins/manifest), tools are registered at runtime via `api.registerTool()`.
|
|
115
|
+
|
|
34
116
|
## Usage
|
|
35
117
|
|
|
36
118
|
### Tools (33 registered)
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "bereach",
|
|
3
3
|
"name": "BeReach",
|
|
4
|
+
"version": "0.2.10",
|
|
4
5
|
"description": "LinkedIn outreach automation — 33 tools, auto-reply commands, campaign monitoring",
|
|
5
6
|
"configSchema": {
|
|
6
7
|
"type": "object",
|
|
7
|
-
"required": [
|
|
8
|
+
"required": [
|
|
9
|
+
"BEREACH_API_KEY"
|
|
10
|
+
],
|
|
8
11
|
"additionalProperties": false,
|
|
9
12
|
"properties": {
|
|
10
13
|
"BEREACH_API_KEY": {
|
|
@@ -20,5 +23,8 @@
|
|
|
20
23
|
"sensitive": true,
|
|
21
24
|
"placeholder": "brc_..."
|
|
22
25
|
}
|
|
23
|
-
}
|
|
26
|
+
},
|
|
27
|
+
"skills": [
|
|
28
|
+
"skills/bereach"
|
|
29
|
+
]
|
|
24
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bereach-openclaw",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "BeReach LinkedIn automation plugin for OpenClaw",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"scripts": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"bereach": "^0.1.4"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.10.0",
|
|
18
19
|
"tsx": "^4.21.0",
|
|
19
20
|
"typescript": "^5.9.3"
|
|
20
21
|
}
|
package/scripts/test-register.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
/**
|
|
3
4
|
* Simulates OpenClaw plugin registration to catch config/trim errors.
|
|
4
5
|
* Run: pnpm exec tsx scripts/test-register.ts
|
|
@@ -10,13 +11,13 @@ import register from "../src/index";
|
|
|
10
11
|
const apiKey = process.env.BEREACH_API_KEY || "brc_test_placeholder";
|
|
11
12
|
|
|
12
13
|
const mockApi = {
|
|
13
|
-
|
|
14
|
+
pluginConfig: { BEREACH_API_KEY: apiKey },
|
|
14
15
|
registerTool: () => {},
|
|
15
16
|
registerCommand: () => {},
|
|
16
17
|
registerCli: () => {},
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
console.log("Testing plugin register (simulates OpenClaw load)...");
|
|
20
|
+
console.log("Testing plugin register with config (simulates OpenClaw load)...");
|
|
20
21
|
try {
|
|
21
22
|
register(mockApi);
|
|
22
23
|
console.log("✓ register() completed — no trim/undefined errors");
|
|
@@ -24,3 +25,14 @@ try {
|
|
|
24
25
|
console.error("✗ register() failed:", err?.message || err);
|
|
25
26
|
process.exit(1);
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
console.log("\nTesting plugin register WITHOUT config (should load, fail on first use)...");
|
|
30
|
+
try {
|
|
31
|
+
register({ pluginConfig: undefined, registerTool: () => {}, registerCommand: () => {}, registerCli: () => {} });
|
|
32
|
+
console.log("✓ register() completed — plugin loads without config (lazy init)");
|
|
33
|
+
} catch (err: any) {
|
|
34
|
+
console.error("✗ register() failed with missing config:", err?.message || err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log("\n✓ All tests passed");
|
package/src/client.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { Bereach } from "bereach";
|
|
2
2
|
|
|
3
3
|
let instance: Bereach | null = null;
|
|
4
|
+
let pendingKey: string | undefined;
|
|
5
|
+
|
|
6
|
+
function normalizeKey(raw: unknown): string | undefined {
|
|
7
|
+
if (raw == null) return undefined;
|
|
8
|
+
const s = typeof raw === "string" ? raw : String(raw);
|
|
9
|
+
const t = s.trim();
|
|
10
|
+
return t.length > 0 ? t : undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function setApiKey(key: string | undefined): void {
|
|
14
|
+
pendingKey = normalizeKey(key);
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
export function createClient(key?: string): Bereach {
|
|
6
18
|
if (!instance) {
|
|
7
|
-
const
|
|
8
|
-
const apiKey =
|
|
9
|
-
typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : undefined;
|
|
19
|
+
const apiKey = normalizeKey(key ?? pendingKey ?? process.env.BEREACH_API_KEY);
|
|
10
20
|
if (!apiKey) {
|
|
11
21
|
throw new Error(
|
|
12
22
|
"BEREACH_API_KEY is required (set in plugin config or environment)",
|
|
@@ -16,3 +26,7 @@ export function createClient(key?: string): Bereach {
|
|
|
16
26
|
}
|
|
17
27
|
return instance;
|
|
18
28
|
}
|
|
29
|
+
|
|
30
|
+
export function getClient(): Bereach {
|
|
31
|
+
return createClient();
|
|
32
|
+
}
|
package/src/commands/index.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getClient } from "../client";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Registers auto-reply commands and CLI commands.
|
|
5
5
|
* Auto-reply commands execute without invoking the LLM.
|
|
6
|
+
* Client is created lazily on first command invocation.
|
|
6
7
|
*/
|
|
7
|
-
export function registerCommands(api: any
|
|
8
|
+
export function registerCommands(api: any) {
|
|
8
9
|
api.registerCommand({
|
|
9
10
|
name: "bereach-credits",
|
|
10
11
|
description: "Show current BeReach credit balance",
|
|
11
12
|
handler: async () => {
|
|
13
|
+
const client = getClient();
|
|
12
14
|
const res = await (client as any).profile.getCredits();
|
|
13
15
|
const c = res.credits;
|
|
14
16
|
return {
|
|
@@ -24,6 +26,7 @@ export function registerCommands(api: any, client: Bereach) {
|
|
|
24
26
|
name: "bereach-status",
|
|
25
27
|
description: "Show LinkedIn rate limit summary",
|
|
26
28
|
handler: async () => {
|
|
29
|
+
const client = getClient();
|
|
27
30
|
const res = await (client as any).profile.getLimits();
|
|
28
31
|
const lines = ["BeReach Rate Limits:"];
|
|
29
32
|
for (const [action, data] of Object.entries(res.limits) as [string, any][]) {
|
|
@@ -38,6 +41,7 @@ export function registerCommands(api: any, client: Bereach) {
|
|
|
38
41
|
name: "bereach-limits",
|
|
39
42
|
description: "Show detailed per-action LinkedIn rate limits",
|
|
40
43
|
handler: async () => {
|
|
44
|
+
const client = getClient();
|
|
41
45
|
const res = await (client as any).profile.getLimits();
|
|
42
46
|
const lines = [`BeReach Rate Limits (multiplier: ${res.multiplier}x):`];
|
|
43
47
|
for (const [action, data] of Object.entries(res.limits) as [string, any][]) {
|
|
@@ -62,6 +66,7 @@ export function registerCommands(api: any, client: Bereach) {
|
|
|
62
66
|
.command("status")
|
|
63
67
|
.description("Show LinkedIn rate limit summary")
|
|
64
68
|
.action(async () => {
|
|
69
|
+
const client = getClient();
|
|
65
70
|
const res = await (client as any).profile.getLimits();
|
|
66
71
|
console.log("BeReach Rate Limits:");
|
|
67
72
|
for (const [action, data] of Object.entries(res.limits) as [string, any][]) {
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { setApiKey } from "./client";
|
|
2
2
|
import { registerAllTools } from "./tools";
|
|
3
3
|
import { registerCommands } from "./commands";
|
|
4
4
|
|
|
5
5
|
function getApiKey(api: any): string | undefined {
|
|
6
|
-
const cfg = api?.config;
|
|
7
6
|
const key =
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
api?.pluginConfig?.BEREACH_API_KEY ?? // 1. OpenClaw config (plugins.entries.bereach.config)
|
|
8
|
+
api?.config?.BEREACH_API_KEY ?? // 2. Fallback config path
|
|
9
|
+
process.env.BEREACH_API_KEY; // 3. Env var (or ~/.bashrc export)
|
|
10
10
|
return typeof key === "string" && key.trim().length > 0 ? key.trim() : undefined;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export default function register(api: any) {
|
|
14
14
|
const apiKey = getApiKey(api);
|
|
15
|
-
|
|
16
|
-
registerAllTools(api
|
|
17
|
-
registerCommands(api
|
|
15
|
+
setApiKey(apiKey);
|
|
16
|
+
registerAllTools(api);
|
|
17
|
+
registerCommands(api);
|
|
18
18
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
+
import { getClient } from "./../client";
|
|
1
2
|
import { definitions } from "./definitions";
|
|
2
|
-
import type { Bereach } from "bereach";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Registers all 33 BeReach tools with the OpenClaw agent.
|
|
6
|
-
*
|
|
6
|
+
* Format per https://docs.openclaw.ai/plugins/agent-tools
|
|
7
|
+
* optional: true — tools have side effects (messages, connections) and require BEREACH_API_KEY
|
|
7
8
|
*/
|
|
8
|
-
export function registerAllTools(api: any
|
|
9
|
+
export function registerAllTools(api: any) {
|
|
9
10
|
for (const def of definitions) {
|
|
10
11
|
api.registerTool(
|
|
11
|
-
def.name,
|
|
12
12
|
{
|
|
13
|
+
name: def.name,
|
|
13
14
|
description: def.description,
|
|
14
15
|
parameters: def.parameters,
|
|
16
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
17
|
+
const client = getClient();
|
|
18
|
+
const [resource, method] = def.handler.split(".");
|
|
19
|
+
const result = await (client as any)[resource][method](params);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
22
|
+
};
|
|
23
|
+
},
|
|
15
24
|
},
|
|
16
|
-
|
|
17
|
-
const [resource, method] = def.handler.split(".");
|
|
18
|
-
const result = await (client as any)[resource][method](params);
|
|
19
|
-
return result;
|
|
20
|
-
},
|
|
25
|
+
{ optional: true },
|
|
21
26
|
);
|
|
22
27
|
}
|
|
23
28
|
}
|