@openclaw/bluebubbles 2026.3.2 → 2026.3.7

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/src/onboarding.ts CHANGED
@@ -4,20 +4,21 @@ import type {
4
4
  OpenClawConfig,
5
5
  DmPolicy,
6
6
  WizardPrompter,
7
- } from "openclaw/plugin-sdk";
7
+ } from "openclaw/plugin-sdk/bluebubbles";
8
8
  import {
9
9
  DEFAULT_ACCOUNT_ID,
10
- addWildcardAllowFrom,
11
10
  formatDocsLink,
12
11
  mergeAllowFromEntries,
13
12
  normalizeAccountId,
14
- promptAccountId,
15
- } from "openclaw/plugin-sdk";
13
+ resolveAccountIdForConfigure,
14
+ setTopLevelChannelDmPolicyWithAllowFrom,
15
+ } from "openclaw/plugin-sdk/bluebubbles";
16
16
  import {
17
17
  listBlueBubblesAccountIds,
18
18
  resolveBlueBubblesAccount,
19
19
  resolveDefaultBlueBubblesAccountId,
20
20
  } from "./accounts.js";
21
+ import { applyBlueBubblesConnectionConfig } from "./config-apply.js";
21
22
  import { hasConfiguredSecretInput, normalizeSecretInputString } from "./secret-input.js";
22
23
  import { parseBlueBubblesAllowTarget } from "./targets.js";
23
24
  import { normalizeBlueBubblesServerUrl } from "./types.js";
@@ -25,19 +26,11 @@ import { normalizeBlueBubblesServerUrl } from "./types.js";
25
26
  const channel = "bluebubbles" as const;
26
27
 
27
28
  function setBlueBubblesDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig {
28
- const allowFrom =
29
- dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.bluebubbles?.allowFrom) : undefined;
30
- return {
31
- ...cfg,
32
- channels: {
33
- ...cfg.channels,
34
- bluebubbles: {
35
- ...cfg.channels?.bluebubbles,
36
- dmPolicy,
37
- ...(allowFrom ? { allowFrom } : {}),
38
- },
39
- },
40
- };
29
+ return setTopLevelChannelDmPolicyWithAllowFrom({
30
+ cfg,
31
+ channel: "bluebubbles",
32
+ dmPolicy,
33
+ });
41
34
  }
42
35
 
43
36
  function setBlueBubblesAllowFrom(
@@ -159,21 +152,16 @@ export const blueBubblesOnboardingAdapter: ChannelOnboardingAdapter = {
159
152
  };
160
153
  },
161
154
  configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds }) => {
162
- const blueBubblesOverride = accountOverrides.bluebubbles?.trim();
163
155
  const defaultAccountId = resolveDefaultBlueBubblesAccountId(cfg);
164
- let accountId = blueBubblesOverride
165
- ? normalizeAccountId(blueBubblesOverride)
166
- : defaultAccountId;
167
- if (shouldPromptAccountIds && !blueBubblesOverride) {
168
- accountId = await promptAccountId({
169
- cfg,
170
- prompter,
171
- label: "BlueBubbles",
172
- currentId: accountId,
173
- listAccountIds: listBlueBubblesAccountIds,
174
- defaultAccountId,
175
- });
176
- }
156
+ const accountId = await resolveAccountIdForConfigure({
157
+ cfg,
158
+ prompter,
159
+ label: "BlueBubbles",
160
+ accountOverride: accountOverrides.bluebubbles,
161
+ shouldPromptAccountIds,
162
+ listAccountIds: listBlueBubblesAccountIds,
163
+ defaultAccountId,
164
+ });
177
165
 
178
166
  let next = cfg;
179
167
  const resolvedAccount = resolveBlueBubblesAccount({ cfg: next, accountId });
@@ -283,42 +271,16 @@ export const blueBubblesOnboardingAdapter: ChannelOnboardingAdapter = {
283
271
  }
284
272
 
285
273
  // Apply config
286
- if (accountId === DEFAULT_ACCOUNT_ID) {
287
- next = {
288
- ...next,
289
- channels: {
290
- ...next.channels,
291
- bluebubbles: {
292
- ...next.channels?.bluebubbles,
293
- enabled: true,
294
- serverUrl,
295
- password,
296
- webhookPath,
297
- },
298
- },
299
- };
300
- } else {
301
- next = {
302
- ...next,
303
- channels: {
304
- ...next.channels,
305
- bluebubbles: {
306
- ...next.channels?.bluebubbles,
307
- enabled: true,
308
- accounts: {
309
- ...next.channels?.bluebubbles?.accounts,
310
- [accountId]: {
311
- ...next.channels?.bluebubbles?.accounts?.[accountId],
312
- enabled: next.channels?.bluebubbles?.accounts?.[accountId]?.enabled ?? true,
313
- serverUrl,
314
- password,
315
- webhookPath,
316
- },
317
- },
318
- },
319
- },
320
- };
321
- }
274
+ next = applyBlueBubblesConnectionConfig({
275
+ cfg: next,
276
+ accountId,
277
+ patch: {
278
+ serverUrl,
279
+ password,
280
+ webhookPath,
281
+ },
282
+ accountEnabled: "preserve-or-true",
283
+ });
322
284
 
323
285
  await prompter.note(
324
286
  [
package/src/probe.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BaseProbeResult } from "openclaw/plugin-sdk";
1
+ import type { BaseProbeResult } from "openclaw/plugin-sdk/bluebubbles";
2
2
  import { normalizeSecretInputString } from "./secret-input.js";
3
3
  import { buildBlueBubblesApiUrl, blueBubblesFetchWithTimeout } from "./types.js";
4
4
 
package/src/reactions.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
1
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
2
2
  import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
3
3
  import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
4
4
  import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
@@ -1,12 +1 @@
1
- export function resolveRequestUrl(input: RequestInfo | URL): string {
2
- if (typeof input === "string") {
3
- return input;
4
- }
5
- if (input instanceof URL) {
6
- return input.toString();
7
- }
8
- if (typeof input === "object" && input && "url" in input && typeof input.url === "string") {
9
- return input.url;
10
- }
11
- return String(input);
12
- }
1
+ export { resolveRequestUrl } from "openclaw/plugin-sdk/bluebubbles";
package/src/runtime.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
2
2
 
3
3
  let runtime: PluginRuntime | null = null;
4
4
  type LegacyRuntimeLogShape = { log?: (message: string) => void };
@@ -1,19 +1,13 @@
1
1
  import {
2
+ buildSecretInputSchema,
2
3
  hasConfiguredSecretInput,
3
4
  normalizeResolvedSecretInputString,
4
5
  normalizeSecretInputString,
5
- } from "openclaw/plugin-sdk";
6
- import { z } from "zod";
6
+ } from "openclaw/plugin-sdk/bluebubbles";
7
7
 
8
- export { hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString };
9
-
10
- export function buildSecretInputSchema() {
11
- return z.union([
12
- z.string(),
13
- z.object({
14
- source: z.enum(["env", "file", "exec"]),
15
- provider: z.string().min(1),
16
- id: z.string().min(1),
17
- }),
18
- ]);
19
- }
8
+ export {
9
+ buildSecretInputSchema,
10
+ hasConfiguredSecretInput,
11
+ normalizeResolvedSecretInputString,
12
+ normalizeSecretInputString,
13
+ };
package/src/send.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
2
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import "./test-mocks.js";
4
4
  import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
package/src/send.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import crypto from "node:crypto";
2
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
3
- import { stripMarkdown } from "openclaw/plugin-sdk";
2
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
3
+ import { stripMarkdown } from "openclaw/plugin-sdk/bluebubbles";
4
4
  import { resolveBlueBubblesAccount } from "./accounts.js";
5
5
  import {
6
6
  getCachedBlueBubblesPrivateApiStatus,
@@ -108,6 +108,19 @@ function resolvePrivateApiDecision(params: {
108
108
  };
109
109
  }
110
110
 
111
+ async function parseBlueBubblesMessageResponse(res: Response): Promise<BlueBubblesSendResult> {
112
+ const body = await res.text();
113
+ if (!body) {
114
+ return { messageId: "ok" };
115
+ }
116
+ try {
117
+ const parsed = JSON.parse(body) as unknown;
118
+ return { messageId: extractBlueBubblesMessageId(parsed) };
119
+ } catch {
120
+ return { messageId: "ok" };
121
+ }
122
+ }
123
+
111
124
  type BlueBubblesChatRecord = Record<string, unknown>;
112
125
 
113
126
  function extractChatGuid(chat: BlueBubblesChatRecord): string | null {
@@ -342,16 +355,7 @@ async function createNewChatWithMessage(params: {
342
355
  }
343
356
  throw new Error(`BlueBubbles create chat failed (${res.status}): ${errorText || "unknown"}`);
344
357
  }
345
- const body = await res.text();
346
- if (!body) {
347
- return { messageId: "ok" };
348
- }
349
- try {
350
- const parsed = JSON.parse(body) as unknown;
351
- return { messageId: extractBlueBubblesMessageId(parsed) };
352
- } catch {
353
- return { messageId: "ok" };
354
- }
358
+ return parseBlueBubblesMessageResponse(res);
355
359
  }
356
360
 
357
361
  export async function sendMessageBlueBubbles(
@@ -464,14 +468,5 @@ export async function sendMessageBlueBubbles(
464
468
  const errorText = await res.text();
465
469
  throw new Error(`BlueBubbles send failed (${res.status}): ${errorText || "unknown"}`);
466
470
  }
467
- const body = await res.text();
468
- if (!body) {
469
- return { messageId: "ok" };
470
- }
471
- try {
472
- const parsed = JSON.parse(body) as unknown;
473
- return { messageId: extractBlueBubblesMessageId(parsed) };
474
- } catch {
475
- return { messageId: "ok" };
476
- }
471
+ return parseBlueBubblesMessageResponse(res);
477
472
  }
package/src/targets.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  type ParsedChatTarget,
6
6
  resolveServicePrefixedAllowTarget,
7
7
  resolveServicePrefixedTarget,
8
- } from "openclaw/plugin-sdk";
8
+ } from "openclaw/plugin-sdk/bluebubbles";
9
9
 
10
10
  export type BlueBubblesService = "imessage" | "sms" | "auto";
11
11
 
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk";
1
+ import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk/bluebubbles";
2
2
 
3
- export type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk";
3
+ export type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk/bluebubbles";
4
4
 
5
5
  export type BlueBubblesGroupConfig = {
6
6
  /** If true, only respond in this group when mentioned. */