bereach-openclaw 1.5.4 → 1.5.5
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 +1 -1
- package/src/hooks/context.ts +4 -4
- package/src/index.ts +90 -0
- package/src/tools/index.ts +1 -0
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/hooks/context.ts
CHANGED
|
@@ -374,7 +374,7 @@ function formatCampaignDispatch(
|
|
|
374
374
|
const shown = Math.min(ranked.length, 7);
|
|
375
375
|
for (let i = 0; i < shown; i++) {
|
|
376
376
|
const { campaign: c } = ranked[i];
|
|
377
|
-
const pipelineUrl = `${CHAT_BASE}/
|
|
377
|
+
const pipelineUrl = `${CHAT_BASE}/campaigns/${c.id}/pipeline`;
|
|
378
378
|
const s = c.stageCounts;
|
|
379
379
|
const funnel = s && (s.lead || s.qualified || s.approved || s.rejected)
|
|
380
380
|
? ` — ${s.lead ?? 0}L / ${s.qualified ?? 0}Q / ${s.approved ?? 0}A / ${s.rejected ?? 0}R`
|
|
@@ -479,7 +479,7 @@ function formatOnboardingDirective(state: SessionState, data: CacheStore, apiKey
|
|
|
479
479
|
"",
|
|
480
480
|
"Search 5 matching prospects, visit top 3. Present: name, title, company, why they fit, suggested approach.",
|
|
481
481
|
"Offer first action: connect, warm up (like/comment), or save to campaign.",
|
|
482
|
-
"Execute, then link: [
|
|
482
|
+
"Execute, then link: [Campaigns](${CHAT_BASE}/campaigns)",
|
|
483
483
|
'- `bereach_state_patch({ key: "onboarding", data: { quickWinDone: true } })`',
|
|
484
484
|
"",
|
|
485
485
|
);
|
|
@@ -576,7 +576,7 @@ function formatLiveStatus(state: SessionState, data: CacheStore, apiKey?: string
|
|
|
576
576
|
lines.push("");
|
|
577
577
|
}
|
|
578
578
|
lines.push("### Dashboard Links");
|
|
579
|
-
lines.push(`[
|
|
579
|
+
lines.push(`[Campaigns](${CHAT_BASE}/campaigns) | [Activity](${CHAT_BASE}/activity) | [Context](${CHAT_BASE}/context) | [Settings](${CHAT_BASE}/settings) | [Pricing](${PRICING_URL})`);
|
|
580
580
|
lines.push("");
|
|
581
581
|
log(`live-status: ONBOARDING mode (${lines.join("\n").length} chars)`);
|
|
582
582
|
return lines.join("\n");
|
|
@@ -733,7 +733,7 @@ function formatLiveStatus(state: SessionState, data: CacheStore, apiKey?: string
|
|
|
733
733
|
if (upgradeBlock) lines.push(upgradeBlock);
|
|
734
734
|
|
|
735
735
|
// Dashboard links
|
|
736
|
-
lines.push(`**Links**: [
|
|
736
|
+
lines.push(`**Links**: [Campaigns](${CHAT_BASE}/campaigns) | [Activity](${CHAT_BASE}/activity) | [Context](${CHAT_BASE}/context) | [Settings](${CHAT_BASE}/settings) | [Pricing](${PRICING_URL})`);
|
|
737
737
|
lines.push("");
|
|
738
738
|
|
|
739
739
|
log(`live-status: ACTIVE mode, campaigns=${hasCampaigns} contacts=${hasContacts} pending=${data.pendingDrafts > 0 || data.failedDrafts > 0 || data.unreadDMs > 0} (${lines.join("\n").length} chars)`);
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ const log = createLogger("init");
|
|
|
17
17
|
/** Guard against multiple register() calls with the same api instance */
|
|
18
18
|
const registeredApis = new WeakSet<object>();
|
|
19
19
|
let registeredCount = 0;
|
|
20
|
+
let autoDetectDone = false;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Resolve the BeReach API key from all supported sources (in priority order):
|
|
@@ -32,6 +33,86 @@ export function resolveApiKey(api: any): string | undefined {
|
|
|
32
33
|
return typeof key === "string" && key.trim().length > 0 ? key.trim() : undefined;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Auto-detect LLM provider from available API keys and set workspace defaults.
|
|
38
|
+
*
|
|
39
|
+
* Runs once at install time. If the workspace still has Anthropic defaults but
|
|
40
|
+
* no ANTHROPIC_API_KEY is available (only GEMINI_API_KEY), switches to Gemini
|
|
41
|
+
* models automatically. This prevents tasks from failing because the default
|
|
42
|
+
* models require an API key the user doesn't have.
|
|
43
|
+
*
|
|
44
|
+
* Does nothing if:
|
|
45
|
+
* - Already ran this session (idempotent guard)
|
|
46
|
+
* - ANTHROPIC_API_KEY is available (defaults are correct)
|
|
47
|
+
* - No GEMINI_API_KEY available (nothing to switch to)
|
|
48
|
+
* - User already changed models from defaults (respects manual choice)
|
|
49
|
+
*/
|
|
50
|
+
async function autoDetectModels(apiKey: string): Promise<void> {
|
|
51
|
+
if (autoDetectDone) return;
|
|
52
|
+
autoDetectDone = true;
|
|
53
|
+
|
|
54
|
+
const hasAnthropic = !!readEnv("ANTHROPIC_API_KEY");
|
|
55
|
+
const hasGemini = !!(readEnv("GEMINI_API_KEY") || readEnv("GOOGLE_API_KEY"));
|
|
56
|
+
|
|
57
|
+
// If Anthropic key exists, defaults are already correct
|
|
58
|
+
if (hasAnthropic) {
|
|
59
|
+
log("model auto-detect: Anthropic key available, keeping defaults");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If no Gemini key either, nothing we can switch to
|
|
64
|
+
if (!hasGemini) {
|
|
65
|
+
log("model auto-detect: no LLM provider keys found, skipping");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Gemini key available, no Anthropic key - check if we need to switch
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(`${API_BASE}/me/settings`, {
|
|
72
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
73
|
+
signal: AbortSignal.timeout(5000),
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
log(`model auto-detect: settings GET failed (HTTP ${res.status}), skipping`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = await res.json() as {
|
|
81
|
+
aiModels?: { fast?: string; creative?: string };
|
|
82
|
+
};
|
|
83
|
+
const currentFast = data.aiModels?.fast;
|
|
84
|
+
const currentCreative = data.aiModels?.creative;
|
|
85
|
+
|
|
86
|
+
// Only auto-switch if still on Anthropic defaults (respect manual choices)
|
|
87
|
+
if (
|
|
88
|
+
currentFast === "anthropic/claude-haiku-4-5" &&
|
|
89
|
+
currentCreative === "anthropic/claude-sonnet-4-6"
|
|
90
|
+
) {
|
|
91
|
+
const patchRes = await fetch(`${API_BASE}/me/settings`, {
|
|
92
|
+
method: "PATCH",
|
|
93
|
+
headers: {
|
|
94
|
+
Authorization: `Bearer ${apiKey}`,
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
aiFastModel: "google/gemini-2.5-flash",
|
|
99
|
+
aiCreativeModel: "google/gemini-2.5-pro",
|
|
100
|
+
}),
|
|
101
|
+
signal: AbortSignal.timeout(5000),
|
|
102
|
+
});
|
|
103
|
+
if (patchRes.ok) {
|
|
104
|
+
log("model auto-detect: no Anthropic key, Gemini key found - switched to Gemini Flash (fast) + Gemini Pro (creative)");
|
|
105
|
+
} else {
|
|
106
|
+
log(`model auto-detect: PATCH failed (HTTP ${patchRes.status})`);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
log("model auto-detect: models already customized, skipping");
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
log(`model auto-detect: ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
35
116
|
export default function register(api: any) {
|
|
36
117
|
if (api && typeof api === "object" && registeredApis.has(api)) {
|
|
37
118
|
log(`skip duplicate register() call (same api instance, call #${registeredCount + 1})`);
|
|
@@ -127,6 +208,15 @@ export default function register(api: any) {
|
|
|
127
208
|
}
|
|
128
209
|
}
|
|
129
210
|
|
|
211
|
+
// Auto-detect LLM provider and set workspace model defaults.
|
|
212
|
+
// Runs before connector start so tasks use the correct models from the first poll.
|
|
213
|
+
// Fire-and-forget — non-critical, must not block registration.
|
|
214
|
+
if (apiKey) {
|
|
215
|
+
autoDetectModels(apiKey).catch((err) => {
|
|
216
|
+
log(`model auto-detect error: ${err instanceof Error ? err.message : String(err)}`);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
130
220
|
// Auto-start connector if API key is present and connector is enabled.
|
|
131
221
|
// Skip when running as an agent child process (openclaw agent) — each child
|
|
132
222
|
// would start its own polling loop, stealing tasks from the main connector.
|
package/src/tools/index.ts
CHANGED
|
@@ -155,6 +155,7 @@ export function registerAllTools(api: any, taskType?: string) {
|
|
|
155
155
|
content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: "Invalid key format. Must start with brc_. Get your key at https://bereach.ai/token" }) }],
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
+
|
|
158
159
|
if (typeof api?.config?.set !== "function") {
|
|
159
160
|
return {
|
|
160
161
|
content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: "Config API not available. Set BEREACH_API_KEY as an environment variable instead." }) }],
|