bereach-openclaw 1.6.3 → 1.6.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.
@@ -198,10 +198,6 @@ export interface PluginConfig {
198
198
  writePacingMax?: number;
199
199
  /** Minimum minutes between direct DM sends (default 5). Override via dm_pacing_minutes context. */
200
200
  dmPacingMinutes?: number;
201
- /** Gateway URL for webhook execution */
202
- gatewayUrl?: string;
203
- /** Gateway hooks auth token */
204
- hooksToken?: string;
205
201
  }
206
202
 
207
203
  // ---------------------------------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "bereach-openclaw",
3
3
  "name": "BeReach",
4
- "version": "1.6.3",
4
+ "version": "1.6.4",
5
5
  "description": "LinkedIn outreach automation — 75+ tools, hook-based enforcement, dynamic context",
6
6
  "configSchema": {
7
7
  "type": "object",
@@ -66,14 +66,6 @@
66
66
  "maximum": 30,
67
67
  "default": 10,
68
68
  "description": "Maximum delay (seconds) before write operations (default: 10s)"
69
- },
70
- "gatewayUrl": {
71
- "type": "string",
72
- "description": "Gateway URL for webhook execution (default: http://localhost:18789)"
73
- },
74
- "hooksToken": {
75
- "type": "string",
76
- "description": "Gateway hooks auth token (set by /bereach setup)"
77
69
  }
78
70
  }
79
71
  },
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "bereach-openclaw",
3
- "version": "1.6.3",
3
+ "version": "1.6.4",
4
4
  "description": "BeReach LinkedIn automation plugin for OpenClaw",
5
5
  "license": "AGPL-3.0",
6
6
  "exports": {
7
7
  ".": "./src/index.ts"
8
8
  },
9
- "bin": {
10
- "bereach-openclaw": "./src/connector-cli.ts"
11
- },
12
9
  "files": [
13
10
  "src/",
14
11
  "skills/",
@@ -1,5 +1,4 @@
1
1
  import { errMsg } from "@bereach/tools/utils";
2
- import { randomBytes } from "node:crypto";
3
2
  import { API_BASE } from "../hooks/utils";
4
3
  import { isApiBaseConfigured } from "../hooks/cache";
5
4
  import { readEnv } from "../env";
@@ -50,66 +49,6 @@ export function registerSetupCommand(api: any) {
50
49
  issues.push("API Key: NOT configured. Set BEREACH_API_KEY in plugin settings or environment.");
51
50
  }
52
51
 
53
- // 3. Gateway hooks configuration (auto-configure if missing)
54
- let hooksConfigured = false;
55
- try {
56
- if (typeof api?.config?.get === "function") {
57
- const hooksEnabled = api.config.get("hooks.enabled");
58
- const hooksToken = api.config.get("hooks.token");
59
- if (hooksEnabled && hooksToken) {
60
- ok.push("Gateway Hooks: enabled (token set)");
61
- hooksConfigured = true;
62
- } else if (hooksEnabled) {
63
- const token = randomBytes(24).toString("hex");
64
- api.config.set("hooks.token", token);
65
- ok.push(`Gateway Hooks: enabled (token auto-generated: ${token.slice(0, 8)}...)`);
66
- hooksConfigured = true;
67
- } else {
68
- api.config.set("hooks.enabled", true);
69
- const token = randomBytes(24).toString("hex");
70
- api.config.set("hooks.token", token);
71
- ok.push(`Gateway Hooks: auto-enabled (token: ${token.slice(0, 8)}...). Restart gateway to apply.`);
72
- hooksConfigured = true;
73
- }
74
- } else {
75
- issues.push("Gateway Hooks: cannot check (api.config not available).");
76
- }
77
- } catch (err) {
78
- issues.push(`Gateway Hooks: error (${errMsg(err)})`);
79
- }
80
-
81
- // 4. Gateway webhook probe
82
- if (hooksConfigured) {
83
- const gatewayUrl = api?.pluginConfig?.gatewayUrl
84
- ?? readEnv("OPENCLAW_GATEWAY_URL")
85
- ?? "http://localhost:18789";
86
- const hooksToken = api?.config?.get?.("hooks.token")
87
- ?? api?.pluginConfig?.hooksToken
88
- ?? readEnv("OPENCLAW_HOOKS_TOKEN");
89
-
90
- if (hooksToken) {
91
- try {
92
- const res = await fetch(`${gatewayUrl}/hooks/agent`, {
93
- method: "POST",
94
- headers: {
95
- Authorization: `Bearer ${hooksToken}`,
96
- "Content-Type": "application/json",
97
- },
98
- body: JSON.stringify({ message: "ping", model: "echo" }),
99
- signal: AbortSignal.timeout(5000),
100
- });
101
- if (res.status === 401 || res.status === 403) {
102
- issues.push(`Webhook probe: auth rejected (HTTP ${res.status}). Token mismatch between plugin and gateway config.`);
103
- } else {
104
- ok.push(`Webhook endpoint: reachable at ${gatewayUrl}`);
105
- }
106
- } catch {
107
- // Gateway not up yet is OK during initial setup
108
- ok.push(`Webhook endpoint: ${gatewayUrl} (not reachable yet - gateway may need restart)`);
109
- }
110
- }
111
- }
112
-
113
52
  // Build output
114
53
  const lines: string[] = [
115
54
  "BeReach Setup",
@@ -3,7 +3,7 @@
3
3
  * These functions take data and return strings — no side effects, no API calls.
4
4
  */
5
5
 
6
- import { cacheSet, type CacheStore, type ContextEntry, type OnboardingState, type RecentEvent } from "../cache";
6
+ import { cacheSet, type CacheStore, type OnboardingState, type RecentEvent } from "../cache";
7
7
  import { type DbCampaign, type SessionState } from "../types";
8
8
  import { errMsg, createLogger, CHAT_BASE, PRICING_URL, apiFetch } from "../utils";
9
9
  import { readEnv } from "../../env";
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { getOrFetch, sessionStart, type CacheStore, type ContextEntry } from "../cache";
10
10
  import { SOUL_TEMPLATE } from "../../soul-template-content";
11
- import { readEnv } from "../../env";
11
+
12
12
  import { type SessionState, detectTaskMode } from "../types";
13
13
  import {
14
14
  errMsg,
@@ -237,10 +237,8 @@ export function registerContextHook(api: any, apiKey: string | undefined, state:
237
237
  const metadata: Record<string, unknown> = { ...(promptCtx?.metadata ?? {}) };
238
238
 
239
239
  // Parse [TASK_META: ...] from the prompt message (used by Docker test runner
240
- // and connector CLI to pass task metadata when env vars can't reach the gateway).
241
- // Only applied when no taskType is already set from metadata/env, so the connector's
242
- // env-var path always takes priority. In production chat, users don't type TASK_META.
243
- if (!metadata.taskType && !readEnv("BEREACH_TASK_TYPE")) {
240
+ // to pass task metadata). In production chat, users don't type TASK_META.
241
+ if (!metadata.taskType) {
244
242
  const msgs = Array.isArray(promptCtx?.messages) ? promptCtx.messages : [];
245
243
  const lastUserMsg = msgs.filter((m: any) => m?.role === "user").pop();
246
244
  const msgText = promptCtx?.prompt
@@ -329,5 +327,3 @@ export function registerContextHook(api: any, apiKey: string | undefined, state:
329
327
  export const _checkBusinessHours = checkBusinessHours;
330
328
  /** @internal — exported for testing only */
331
329
  export { rankCampaigns as _rankCampaigns } from "./formatters";
332
- /** @internal — exported for testing only */
333
- export const _formatAnthropicKeyWarning = formatAnthropicKeyWarning;
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import type { TaskModeInfo } from "@bereach/tools/enforcement-types";
7
- import { readEnv } from "../env";
8
7
 
9
8
  /** Safely parse maxCredits from any input (string, number, undefined). */
10
9
  function parseMaxCredits(value: unknown): number {
@@ -18,7 +17,7 @@ function parseMaxCredits(value: unknown): number {
18
17
 
19
18
  /**
20
19
  * Detect task mode from the sessionKey convention: "hook:{userId}:{campaignId}:{type}"
21
- * or from metadata / environment variables (for CLI-based connector execution).
20
+ * or from metadata fields (passed via webhook or promptCtx).
22
21
  * Returns TaskModeInfo if this is a task-driven session, null for interactive.
23
22
  */
24
23
  export function detectTaskMode(sessionKey: string | undefined | null, metadata?: Record<string, unknown>): TaskModeInfo | null {
@@ -53,16 +52,5 @@ export function detectTaskMode(sessionKey: string | undefined | null, metadata?:
53
52
  };
54
53
  }
55
54
 
56
- // Strategy 3: environment variables (set when spawning CLI)
57
- const envTaskType = readEnv("BEREACH_TASK_TYPE");
58
- if (envTaskType) {
59
- return {
60
- taskId: readEnv("BEREACH_TASK_ID") || fallbackTaskId(),
61
- taskType: envTaskType,
62
- campaignId: readEnv("BEREACH_TASK_CAMPAIGN_ID") ?? null,
63
- maxCredits: parseMaxCredits(readEnv("BEREACH_TASK_MAX_CREDITS")),
64
- };
65
- }
66
-
67
55
  return null;
68
56
  }
@@ -2,7 +2,7 @@
2
2
  * Shared types, constants, and tool categories for BeReach OpenClaw hooks.
3
3
  *
4
4
  * Re-exports from @bereach/tools (single source of truth).
5
- * detectTaskMode stays local (reads environment vars).
5
+ * detectTaskMode stays local (OpenClaw-specific session detection).
6
6
  */
7
7
 
8
8
  // Re-export everything from shared package
@@ -25,5 +25,5 @@ export type {
25
25
  DbCampaign,
26
26
  } from "@bereach/tools/enforcement-types";
27
27
 
28
- // Keep detectTaskMode locally (reads environment vars — OpenClaw-specific)
28
+ // Keep detectTaskMode locally (OpenClaw-specific session/metadata detection)
29
29
  export { detectTaskMode } from "./detect-task-mode";
package/src/index.ts CHANGED
@@ -9,11 +9,6 @@ import { setTtl, isApiBaseConfigured } from "./hooks/cache";
9
9
  import { createSessionState, type PluginConfig } from "./hooks/types";
10
10
  import { errMsg, createLogger } from "./hooks/utils";
11
11
  import { autoDetectModels } from "./auto-detect-models";
12
- import { randomBytes } from "node:crypto";
13
- import { readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
14
- import { join } from "node:path";
15
- import { homedir } from "node:os";
16
- import { spawn } from "node:child_process";
17
12
  import { readEnv } from "./env";
18
13
 
19
14
  const log = createLogger("init");
@@ -77,12 +72,7 @@ export default function register(api: any) {
77
72
  setTtl(config.contextCacheTtlMs);
78
73
  }
79
74
 
80
- // When running as a direct agent child process (openclaw agent), BEREACH_TASK_TYPE
81
- // is set per-task. Register only whitelisted tools to reduce token count (~19K savings).
82
- // In gateway mode (webhook), register all tools; per-request filtering
83
- // is handled via allowedTools in the before_prompt_build hook return.
84
- const execFileTaskType = readEnv("BEREACH_TASK_TYPE");
85
- registerAllTools(api, execFileTaskType || undefined);
75
+ registerAllTools(api);
86
76
  registerCommands(api, state, apiKey);
87
77
  registerSetupCommand(api);
88
78
 
@@ -117,67 +107,6 @@ export default function register(api: any) {
117
107
  const masked = apiKey ? `...${apiKey.slice(-6)}` : "NOT SET";
118
108
  log(`registered: tools, commands, hooks (${registered.join(", ")}), API key: ${masked}`);
119
109
 
120
- // Auto-configure hooks in openclaw.json so the gateway exposes /hooks/agent.
121
- // The OpenClaw plugin API config accessors are undefined, so we read/write
122
- // the config file directly - same approach as Docker entrypoint.sh.
123
- let hooksToken = config.hooksToken || readEnv("OPENCLAW_HOOKS_TOKEN");
124
- let needsRestart = false;
125
- if (!hooksToken) {
126
- try {
127
- const configPath = join(homedir(), ".openclaw", "openclaw.json");
128
- if (existsSync(configPath)) {
129
- const raw = readFileSync(configPath, "utf8");
130
- const cfg = JSON.parse(raw);
131
- if (cfg.hooks?.enabled && typeof cfg.hooks.token === "string" && cfg.hooks.token) {
132
- // Hooks already configured - grab the existing token
133
- hooksToken = cfg.hooks.token;
134
- log("discovered hooks token from openclaw.json");
135
- } else {
136
- // Hooks not configured - generate token and enable them.
137
- // The gateway reads config at startup before loading plugins,
138
- // so this change requires a restart to take effect.
139
- const token = randomBytes(24).toString("hex");
140
- cfg.hooks = { ...cfg.hooks, enabled: true, token };
141
- writeFileSync(configPath, JSON.stringify(cfg, null, 2));
142
- hooksToken = token;
143
- needsRestart = true;
144
- log("auto-configured hooks in openclaw.json — gateway restart required");
145
- }
146
- }
147
- } catch (err) {
148
- log(`hooks auto-config: ${errMsg(err)}`);
149
- }
150
- }
151
-
152
- // If hooks were just written to config, the gateway must restart to load them.
153
- // Spawn a detached process that waits, cleans lock/pid, then restarts the gateway.
154
- if (needsRestart) {
155
- const ocDir = join(homedir(), ".openclaw");
156
- const lockFile = join(ocDir, "gateway.lock");
157
- const pidFile = join(ocDir, "gateway.pid");
158
-
159
- try {
160
- // Clean up lock + pid files so the gateway can start fresh
161
- if (existsSync(lockFile)) unlinkSync(lockFile);
162
- if (existsSync(pidFile)) unlinkSync(pidFile);
163
- } catch { /* best effort */ }
164
-
165
- try {
166
- const child = spawn("sh", ["-c", "sleep 3 && openclaw gateway start"], {
167
- detached: true,
168
- stdio: "ignore",
169
- });
170
- child.unref();
171
- log("hooks config written — spawned detached restart, exiting in 3s");
172
- } catch (err) {
173
- log(`restart spawn failed: ${errMsg(err)}`);
174
- }
175
-
176
- setTimeout(() => {
177
- process.exit(0);
178
- }, 3000);
179
- }
180
-
181
110
  // Auto-detect LLM provider and set workspace model defaults.
182
111
  // Fire-and-forget — non-critical, must not block registration.
183
112
  if (apiKey) {
@@ -2,20 +2,15 @@ import { definitions } from "@bereach/tools";
2
2
  import { executeApiCall } from "@bereach/tools/api-client";
3
3
  import { cacheSet } from "../hooks/cache";
4
4
  import { resolveApiKey } from "../index";
5
- import { getTaskToolWhitelist } from "@bereach/tools/task-tool-whitelist";
6
5
 
7
6
  const API_BASE = "https://api.bereach.ai";
8
7
 
9
8
  /**
10
- * Register tools with OpenClaw.
11
- * In task mode (execFile), pass a taskType to only register whitelisted tools.
12
- * In gateway mode (webhook), all tools are registered; per-request filtering
13
- * is handled via allowedTools in the hook return.
9
+ * Register all tools with OpenClaw.
10
+ * Per-request filtering is handled via allowedTools in the before_prompt_build hook.
14
11
  */
15
- export function registerAllTools(api: any, taskType?: string) {
16
- const whitelist = taskType ? getTaskToolWhitelist(taskType) : null;
12
+ export function registerAllTools(api: any) {
17
13
  let registered = 0;
18
- let skipped = 0;
19
14
 
20
15
  for (const def of definitions) {
21
16
  if (!def.apiPath && def.handler !== "__local__" && def.handler !== "__set_api_key__") {
@@ -23,12 +18,6 @@ export function registerAllTools(api: any, taskType?: string) {
23
18
  continue;
24
19
  }
25
20
 
26
- // Skip tools not in the whitelist for this task type
27
- if (whitelist && !whitelist.has(def.name)) {
28
- skipped++;
29
- continue;
30
- }
31
-
32
21
  registered++;
33
22
  api.registerTool({
34
23
  name: def.name,
@@ -82,7 +71,4 @@ export function registerAllTools(api: any, taskType?: string) {
82
71
  });
83
72
  }
84
73
 
85
- if (whitelist) {
86
- console.log(`[bereach:tools] Task mode (${taskType}): registered ${registered} tools, pruned ${skipped}`);
87
- }
88
74
  }