refactor: rename to openclaw

This commit is contained in:
Peter Steinberger
2026-01-30 03:15:10 +01:00
parent 4583f88626
commit 9a7160786a
2357 changed files with 16688 additions and 16788 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { bluebubblesPlugin } from "./src/channel.js";
import { handleBlueBubblesWebhookRequest } from "./src/monitor.js";
@@ -10,7 +10,7 @@ const plugin = {
name: "BlueBubbles",
description: "BlueBubbles channel plugin (macOS app)",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setBlueBubblesRuntime(api.runtime);
api.registerChannel({ plugin: bluebubblesPlugin });
api.registerHttpHandler(handleBlueBubblesWebhookRequest);
+5 -5
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/bluebubbles",
"version": "2026.1.29",
"name": "@openclaw/bluebubbles",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot BlueBubbles channel plugin",
"moltbot": {
"description": "OpenClaw BlueBubbles channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
],
@@ -25,7 +25,7 @@
"order": 75
},
"install": {
"npmSpec": "@moltbot/bluebubbles",
"npmSpec": "@openclaw/bluebubbles",
"localPath": "extensions/bluebubbles",
"defaultChoice": "npm"
}
+9 -9
View File
@@ -1,5 +1,5 @@
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
import { normalizeBlueBubblesServerUrl, type BlueBubblesAccountConfig } from "./types.js";
export type ResolvedBlueBubblesAccount = {
@@ -11,26 +11,26 @@ export type ResolvedBlueBubblesAccount = {
baseUrl?: string;
};
function listConfiguredAccountIds(cfg: MoltbotConfig): string[] {
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
const accounts = cfg.channels?.bluebubbles?.accounts;
if (!accounts || typeof accounts !== "object") return [];
return Object.keys(accounts).filter(Boolean);
}
export function listBlueBubblesAccountIds(cfg: MoltbotConfig): string[] {
export function listBlueBubblesAccountIds(cfg: OpenClawConfig): string[] {
const ids = listConfiguredAccountIds(cfg);
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
return ids.sort((a, b) => a.localeCompare(b));
}
export function resolveDefaultBlueBubblesAccountId(cfg: MoltbotConfig): string {
export function resolveDefaultBlueBubblesAccountId(cfg: OpenClawConfig): string {
const ids = listBlueBubblesAccountIds(cfg);
if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID;
return ids[0] ?? DEFAULT_ACCOUNT_ID;
}
function resolveAccountConfig(
cfg: MoltbotConfig,
cfg: OpenClawConfig,
accountId: string,
): BlueBubblesAccountConfig | undefined {
const accounts = cfg.channels?.bluebubbles?.accounts;
@@ -39,7 +39,7 @@ function resolveAccountConfig(
}
function mergeBlueBubblesAccountConfig(
cfg: MoltbotConfig,
cfg: OpenClawConfig,
accountId: string,
): BlueBubblesAccountConfig {
const base = (cfg.channels?.bluebubbles ?? {}) as BlueBubblesAccountConfig & {
@@ -52,7 +52,7 @@ function mergeBlueBubblesAccountConfig(
}
export function resolveBlueBubblesAccount(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
accountId?: string | null;
}): ResolvedBlueBubblesAccount {
const accountId = normalizeAccountId(params.accountId);
@@ -73,7 +73,7 @@ export function resolveBlueBubblesAccount(params: {
};
}
export function listEnabledBlueBubblesAccounts(cfg: MoltbotConfig): ResolvedBlueBubblesAccount[] {
export function listEnabledBlueBubblesAccounts(cfg: OpenClawConfig): ResolvedBlueBubblesAccount[] {
return listBlueBubblesAccountIds(cfg)
.map((accountId) => resolveBlueBubblesAccount({ cfg, accountId }))
.filter((account) => account.enabled);
+22 -22
View File
@@ -1,7 +1,7 @@
import { describe, expect, it, vi, beforeEach } from "vitest";
import { bluebubblesMessageActions } from "./actions.js";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
vi.mock("./accounts.js", () => ({
resolveBlueBubblesAccount: vi.fn(({ cfg, accountId }) => {
@@ -49,7 +49,7 @@ describe("bluebubblesMessageActions", () => {
describe("listActions", () => {
it("returns empty array when account is not enabled", () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: { bluebubbles: { enabled: false } },
};
const actions = bluebubblesMessageActions.listActions({ cfg });
@@ -57,7 +57,7 @@ describe("bluebubblesMessageActions", () => {
});
it("returns empty array when account is not configured", () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: { bluebubbles: { enabled: true } },
};
const actions = bluebubblesMessageActions.listActions({ cfg });
@@ -65,7 +65,7 @@ describe("bluebubblesMessageActions", () => {
});
it("returns react action when enabled and configured", () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
enabled: true,
@@ -79,7 +79,7 @@ describe("bluebubblesMessageActions", () => {
});
it("excludes react action when reactions are gated off", () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
enabled: true,
@@ -153,7 +153,7 @@ describe("bluebubblesMessageActions", () => {
describe("handleAction", () => {
it("throws for unsupported actions", async () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -172,7 +172,7 @@ describe("bluebubblesMessageActions", () => {
});
it("throws when emoji is missing for react action", async () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -191,7 +191,7 @@ describe("bluebubblesMessageActions", () => {
});
it("throws when messageId is missing", async () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -213,7 +213,7 @@ describe("bluebubblesMessageActions", () => {
const { resolveChatGuidForTarget } = await import("./send.js");
vi.mocked(resolveChatGuidForTarget).mockResolvedValueOnce(null);
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -234,7 +234,7 @@ describe("bluebubblesMessageActions", () => {
it("sends reaction successfully with chatGuid", async () => {
const { sendBlueBubblesReaction } = await import("./reactions.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -269,7 +269,7 @@ describe("bluebubblesMessageActions", () => {
it("sends reaction removal successfully", async () => {
const { sendBlueBubblesReaction } = await import("./reactions.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -305,7 +305,7 @@ describe("bluebubblesMessageActions", () => {
const { resolveChatGuidForTarget } = await import("./send.js");
vi.mocked(resolveChatGuidForTarget).mockResolvedValueOnce("iMessage;-;+15559876543");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -335,7 +335,7 @@ describe("bluebubblesMessageActions", () => {
it("passes partIndex when provided", async () => {
const { sendBlueBubblesReaction } = await import("./reactions.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -367,7 +367,7 @@ describe("bluebubblesMessageActions", () => {
const { resolveChatGuidForTarget } = await import("./send.js");
vi.mocked(resolveChatGuidForTarget).mockResolvedValueOnce("iMessage;-;+15550001111");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -405,7 +405,7 @@ describe("bluebubblesMessageActions", () => {
const { sendBlueBubblesReaction } = await import("./reactions.js");
vi.mocked(resolveBlueBubblesMessageId).mockReturnValueOnce("resolved-uuid");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -439,7 +439,7 @@ describe("bluebubblesMessageActions", () => {
throw new Error("short id expired");
});
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -465,7 +465,7 @@ describe("bluebubblesMessageActions", () => {
it("accepts message param for edit action", async () => {
const { editBlueBubblesMessage } = await import("./chat.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -491,7 +491,7 @@ describe("bluebubblesMessageActions", () => {
it("accepts message/target aliases for sendWithEffect", async () => {
const { sendMessageBlueBubbles } = await import("./send.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -524,7 +524,7 @@ describe("bluebubblesMessageActions", () => {
it("passes asVoice through sendAttachment", async () => {
const { sendBlueBubblesAttachment } = await import("./attachments.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -558,7 +558,7 @@ describe("bluebubblesMessageActions", () => {
});
it("throws when buffer is missing for setGroupIcon", async () => {
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -580,7 +580,7 @@ describe("bluebubblesMessageActions", () => {
it("sets group icon successfully with chatGuid and buffer", async () => {
const { setGroupIconBlueBubbles } = await import("./chat.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
@@ -619,7 +619,7 @@ describe("bluebubblesMessageActions", () => {
it("uses default filename when not provided for setGroupIcon", async () => {
const { setGroupIconBlueBubbles } = await import("./chat.js");
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
+6 -6
View File
@@ -9,8 +9,8 @@ import {
type ChannelMessageActionAdapter,
type ChannelMessageActionName,
type ChannelToolSend,
type MoltbotConfig,
} from "clawdbot/plugin-sdk";
type OpenClawConfig,
} from "openclaw/plugin-sdk";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { resolveBlueBubblesMessageId } from "./monitor.js";
@@ -66,9 +66,9 @@ const SUPPORTED_ACTIONS = new Set<ChannelMessageActionName>(BLUEBUBBLES_ACTION_N
export const bluebubblesMessageActions: ChannelMessageActionAdapter = {
listActions: ({ cfg }) => {
const account = resolveBlueBubblesAccount({ cfg: cfg as MoltbotConfig });
const account = resolveBlueBubblesAccount({ cfg: cfg as OpenClawConfig });
if (!account.enabled || !account.configured) return [];
const gate = createActionGate((cfg as MoltbotConfig).channels?.bluebubbles?.actions);
const gate = createActionGate((cfg as OpenClawConfig).channels?.bluebubbles?.actions);
const actions = new Set<ChannelMessageActionName>();
const macOS26 = isMacOS26OrHigher(account.accountId);
for (const action of BLUEBUBBLES_ACTION_NAMES) {
@@ -90,12 +90,12 @@ export const bluebubblesMessageActions: ChannelMessageActionAdapter = {
},
handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
const account = resolveBlueBubblesAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId: accountId ?? undefined,
});
const baseUrl = account.config.serverUrl?.trim();
const password = account.config.password?.trim();
const opts = { cfg: cfg as MoltbotConfig, accountId: accountId ?? undefined };
const opts = { cfg: cfg as OpenClawConfig, accountId: accountId ?? undefined };
// Helper to resolve chatGuid from various params or session context
const resolveChatGuid = async (): Promise<string> => {
+2 -2
View File
@@ -1,6 +1,6 @@
import crypto from "node:crypto";
import path from "node:path";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { resolveChatGuidForTarget } from "./send.js";
import { parseBlueBubblesTarget, normalizeBlueBubblesHandle } from "./targets.js";
@@ -16,7 +16,7 @@ export type BlueBubblesAttachmentOpts = {
password?: string;
accountId?: string;
timeoutMs?: number;
cfg?: MoltbotConfig;
cfg?: OpenClawConfig;
};
const DEFAULT_ATTACHMENT_MAX_BYTES = 8 * 1024 * 1024;
+17 -17
View File
@@ -1,4 +1,4 @@
import type { ChannelAccountSnapshot, ChannelPlugin, MoltbotConfig } from "clawdbot/plugin-sdk";
import type { ChannelAccountSnapshot, ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
import {
applyAccountNameToChannelSection,
buildChannelConfigSchema,
@@ -12,7 +12,7 @@ import {
resolveBlueBubblesGroupRequireMention,
resolveBlueBubblesGroupToolPolicy,
setAccountEnabledInConfigSection,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import {
listBlueBubblesAccountIds,
@@ -78,13 +78,13 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
configSchema: buildChannelConfigSchema(BlueBubblesConfigSchema),
onboarding: blueBubblesOnboardingAdapter,
config: {
listAccountIds: (cfg) => listBlueBubblesAccountIds(cfg as MoltbotConfig),
listAccountIds: (cfg) => listBlueBubblesAccountIds(cfg as OpenClawConfig),
resolveAccount: (cfg, accountId) =>
resolveBlueBubblesAccount({ cfg: cfg as MoltbotConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultBlueBubblesAccountId(cfg as MoltbotConfig),
resolveBlueBubblesAccount({ cfg: cfg as OpenClawConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultBlueBubblesAccountId(cfg as OpenClawConfig),
setAccountEnabled: ({ cfg, accountId, enabled }) =>
setAccountEnabledInConfigSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
sectionKey: "bluebubbles",
accountId,
enabled,
@@ -92,7 +92,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
}),
deleteAccount: ({ cfg, accountId }) =>
deleteAccountFromConfigSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
sectionKey: "bluebubbles",
accountId,
clearBaseFields: ["serverUrl", "password", "name", "webhookPath"],
@@ -106,7 +106,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
baseUrl: account.baseUrl,
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveBlueBubblesAccount({ cfg: cfg as MoltbotConfig, accountId }).config.allowFrom ??
(resolveBlueBubblesAccount({ cfg: cfg as OpenClawConfig, accountId }).config.allowFrom ??
[]).map(
(entry) => String(entry),
),
@@ -122,7 +122,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
resolveDmPolicy: ({ cfg, accountId, account }) => {
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
const useAccountPath = Boolean(
(cfg as MoltbotConfig).channels?.bluebubbles?.accounts?.[resolvedAccountId],
(cfg as OpenClawConfig).channels?.bluebubbles?.accounts?.[resolvedAccountId],
);
const basePath = useAccountPath
? `channels.bluebubbles.accounts.${resolvedAccountId}.`
@@ -207,7 +207,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
applyAccountName: ({ cfg, accountId, name }) =>
applyAccountNameToChannelSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
channelKey: "bluebubbles",
accountId,
name,
@@ -222,7 +222,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
},
applyAccountConfig: ({ cfg, accountId, input }) => {
const namedConfig = applyAccountNameToChannelSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
channelKey: "bluebubbles",
accountId,
name: input.name,
@@ -247,7 +247,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
...(input.webhookPath ? { webhookPath: input.webhookPath } : {}),
},
},
} as MoltbotConfig;
} as OpenClawConfig;
}
return {
...next,
@@ -268,7 +268,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
},
},
},
} as MoltbotConfig;
} as OpenClawConfig;
},
},
pairing: {
@@ -276,7 +276,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
normalizeAllowEntry: (entry) => normalizeBlueBubblesHandle(entry.replace(/^bluebubbles:/i, "")),
notifyApproval: async ({ cfg, id }) => {
await sendMessageBlueBubbles(id, PAIRING_APPROVED_MESSAGE, {
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
});
},
},
@@ -300,7 +300,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
? resolveBlueBubblesMessageId(rawReplyToId, { requireKnownShortId: true })
: "";
const result = await sendMessageBlueBubbles(to, text, {
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId: accountId ?? undefined,
replyToMessageGuid: replyToMessageGuid || undefined,
});
@@ -317,7 +317,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
};
const resolvedCaption = caption ?? text;
const result = await sendBlueBubblesMedia({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
to,
mediaUrl,
mediaPath,
@@ -388,7 +388,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount> = {
ctx.log?.info(`[${account.accountId}] starting provider (webhook=${webhookPath})`);
return monitorBlueBubblesProvider({
account,
config: ctx.cfg as MoltbotConfig,
config: ctx.cfg as OpenClawConfig,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
statusSink: (patch) => ctx.setStatus({ accountId: ctx.accountId, ...patch }),
+2 -2
View File
@@ -1,6 +1,6 @@
import crypto from "node:crypto";
import { resolveBlueBubblesAccount } from "./accounts.js";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
export type BlueBubblesChatOpts = {
@@ -8,7 +8,7 @@ export type BlueBubblesChatOpts = {
password?: string;
accountId?: string;
timeoutMs?: number;
cfg?: MoltbotConfig;
cfg?: OpenClawConfig;
};
function resolveAccount(params: BlueBubblesChatOpts) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { MarkdownConfigSchema, ToolPolicySchema } from "clawdbot/plugin-sdk";
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk";
import { z } from "zod";
const allowFromEntry = z.union([z.string(), z.number()]);
+2 -2
View File
@@ -1,7 +1,7 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { resolveChannelMediaMaxBytes, type MoltbotConfig } from "clawdbot/plugin-sdk";
import { resolveChannelMediaMaxBytes, type OpenClawConfig } from "openclaw/plugin-sdk";
import { sendBlueBubblesAttachment } from "./attachments.js";
import { resolveBlueBubblesMessageId } from "./monitor.js";
@@ -49,7 +49,7 @@ function resolveFilenameFromSource(source?: string): string | undefined {
}
export async function sendBlueBubblesMedia(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
to: string;
mediaUrl?: string;
mediaPath?: string;
+48 -48
View File
@@ -2,8 +2,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { IncomingMessage, ServerResponse } from "node:http";
import { EventEmitter } from "node:events";
import { removeAckReactionAfterReply, shouldAckReaction } from "clawdbot/plugin-sdk";
import type { MoltbotConfig, PluginRuntime } from "clawdbot/plugin-sdk";
import { removeAckReactionAfterReply, shouldAckReaction } from "openclaw/plugin-sdk";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
import {
handleBlueBubblesWebhookRequest,
registerBlueBubblesWebhookTarget,
@@ -178,7 +178,7 @@ function createMockRuntime(): PluginRuntime {
})) as unknown as PluginRuntime["logging"]["getChildLogger"],
},
state: {
resolveStateDir: vi.fn(() => "/tmp/moltbot") as unknown as PluginRuntime["state"]["resolveStateDir"],
resolveStateDir: vi.fn(() => "/tmp/openclaw") as unknown as PluginRuntime["state"]["resolveStateDir"],
},
};
}
@@ -264,7 +264,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("webhook parsing + auth handling", () => {
it("rejects non-POST requests", async () => {
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -287,7 +287,7 @@ describe("BlueBubbles webhook monitor", () => {
it("accepts POST requests with valid JSON payload", async () => {
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -323,7 +323,7 @@ describe("BlueBubbles webhook monitor", () => {
it("rejects requests with invalid JSON", async () => {
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -346,7 +346,7 @@ describe("BlueBubbles webhook monitor", () => {
it("authenticates via password query parameter", async () => {
const account = createMockAccount({ password: "secret-token" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -380,7 +380,7 @@ describe("BlueBubbles webhook monitor", () => {
it("authenticates via x-password header", async () => {
const account = createMockAccount({ password: "secret-token" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -418,7 +418,7 @@ describe("BlueBubbles webhook monitor", () => {
it("rejects unauthorized requests with wrong password", async () => {
const account = createMockAccount({ password: "secret-token" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -451,7 +451,7 @@ describe("BlueBubbles webhook monitor", () => {
it("allows localhost requests without authentication", async () => {
const account = createMockAccount({ password: "secret-token" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -497,7 +497,7 @@ describe("BlueBubbles webhook monitor", () => {
vi.mocked(resolveChatGuidForTarget).mockClear();
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -545,7 +545,7 @@ describe("BlueBubbles webhook monitor", () => {
});
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -591,7 +591,7 @@ describe("BlueBubbles webhook monitor", () => {
dmPolicy: "allowlist",
allowFrom: ["+15551234567"],
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -632,7 +632,7 @@ describe("BlueBubbles webhook monitor", () => {
dmPolicy: "allowlist",
allowFrom: ["+15559999999"], // Different number
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -673,7 +673,7 @@ describe("BlueBubbles webhook monitor", () => {
dmPolicy: "pairing",
allowFrom: ["+15559999999"], // Different number than sender
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -716,7 +716,7 @@ describe("BlueBubbles webhook monitor", () => {
dmPolicy: "pairing",
allowFrom: ["+15559999999"], // Different number than sender
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -757,7 +757,7 @@ describe("BlueBubbles webhook monitor", () => {
dmPolicy: "open",
allowFrom: [],
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -794,7 +794,7 @@ describe("BlueBubbles webhook monitor", () => {
const account = createMockAccount({
dmPolicy: "disabled",
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -833,7 +833,7 @@ describe("BlueBubbles webhook monitor", () => {
const account = createMockAccount({
groupPolicy: "open",
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -871,7 +871,7 @@ describe("BlueBubbles webhook monitor", () => {
const account = createMockAccount({
groupPolicy: "disabled",
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -910,7 +910,7 @@ describe("BlueBubbles webhook monitor", () => {
groupPolicy: "allowlist",
dmPolicy: "open",
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -949,7 +949,7 @@ describe("BlueBubbles webhook monitor", () => {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_guid:iMessage;+;chat123456"],
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -990,7 +990,7 @@ describe("BlueBubbles webhook monitor", () => {
mockMatchesMentionPatterns.mockReturnValue(true);
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1031,7 +1031,7 @@ describe("BlueBubbles webhook monitor", () => {
mockMatchesMentionPatterns.mockReturnValue(false);
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1069,7 +1069,7 @@ describe("BlueBubbles webhook monitor", () => {
mockResolveRequireMention.mockReturnValue(false);
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1107,7 +1107,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("group metadata", () => {
it("includes group subject + members in ctx", async () => {
const account = createMockAccount({ groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1153,7 +1153,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("reply metadata", () => {
it("surfaces reply fields in ctx when provided", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1201,7 +1201,7 @@ describe("BlueBubbles webhook monitor", () => {
it("preserves part index prefixes in reply tags when short IDs are unavailable", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1246,7 +1246,7 @@ describe("BlueBubbles webhook monitor", () => {
it("hydrates missing reply sender/body from the recent-message cache", async () => {
const account = createMockAccount({ dmPolicy: "open", groupPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1316,7 +1316,7 @@ describe("BlueBubbles webhook monitor", () => {
it("falls back to threadOriginatorGuid when reply metadata is absent", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1357,7 +1357,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("tapback text parsing", () => {
it("does not rewrite tapback-like text without metadata", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1397,7 +1397,7 @@ describe("BlueBubbles webhook monitor", () => {
it("parses tapback text with custom emoji when metadata is present", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1442,7 +1442,7 @@ describe("BlueBubbles webhook monitor", () => {
vi.mocked(sendBlueBubblesReaction).mockClear();
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {
const config: OpenClawConfig = {
messages: {
ackReaction: "❤️",
ackReactionScope: "direct",
@@ -1500,7 +1500,7 @@ describe("BlueBubbles webhook monitor", () => {
groupPolicy: "open",
allowFrom: ["+15551234567"],
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1543,7 +1543,7 @@ describe("BlueBubbles webhook monitor", () => {
groupPolicy: "open",
allowFrom: [], // No one authorized
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1586,7 +1586,7 @@ describe("BlueBubbles webhook monitor", () => {
const account = createMockAccount({
sendReadReceipts: true,
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1627,7 +1627,7 @@ describe("BlueBubbles webhook monitor", () => {
const account = createMockAccount({
sendReadReceipts: false,
});
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1666,7 +1666,7 @@ describe("BlueBubbles webhook monitor", () => {
vi.mocked(sendBlueBubblesTyping).mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1714,7 +1714,7 @@ describe("BlueBubbles webhook monitor", () => {
vi.mocked(sendBlueBubblesTyping).mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1763,7 +1763,7 @@ describe("BlueBubbles webhook monitor", () => {
vi.mocked(sendBlueBubblesTyping).mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1813,7 +1813,7 @@ describe("BlueBubbles webhook monitor", () => {
});
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1859,7 +1859,7 @@ describe("BlueBubbles webhook monitor", () => {
mockEnqueueSystemEvent.mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1899,7 +1899,7 @@ describe("BlueBubbles webhook monitor", () => {
mockEnqueueSystemEvent.mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1939,7 +1939,7 @@ describe("BlueBubbles webhook monitor", () => {
mockEnqueueSystemEvent.mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -1976,7 +1976,7 @@ describe("BlueBubbles webhook monitor", () => {
mockEnqueueSystemEvent.mockClear();
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -2017,7 +2017,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("short message ID mapping", () => {
it("assigns sequential short IDs to messages", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -2057,7 +2057,7 @@ describe("BlueBubbles webhook monitor", () => {
it("resolves short ID back to UUID", async () => {
const account = createMockAccount({ dmPolicy: "open" });
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
@@ -2110,7 +2110,7 @@ describe("BlueBubbles webhook monitor", () => {
describe("fromMe messages", () => {
it("ignores messages from self (fromMe=true)", async () => {
const account = createMockAccount();
const config: MoltbotConfig = {};
const config: OpenClawConfig = {};
const core = createMockRuntime();
setBlueBubblesRuntime(core);
+6 -6
View File
@@ -1,13 +1,13 @@
import type { IncomingMessage, ServerResponse } from "node:http";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import {
logAckFailure,
logInboundDrop,
logTypingFailure,
resolveAckReaction,
resolveControlCommandGate,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { markBlueBubblesChatRead, sendBlueBubblesTyping } from "./chat.js";
import { resolveChatGuidForTarget, sendMessageBlueBubbles } from "./send.js";
import { downloadBlueBubblesAttachment } from "./attachments.js";
@@ -26,7 +26,7 @@ export type BlueBubblesRuntimeEnv = {
export type BlueBubblesMonitorOptions = {
account: ResolvedBlueBubblesAccount;
config: MoltbotConfig;
config: OpenClawConfig;
runtime: BlueBubblesRuntimeEnv;
abortSignal: AbortSignal;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
@@ -243,7 +243,7 @@ function logGroupAllowlistHint(params: {
type WebhookTarget = {
account: ResolvedBlueBubblesAccount;
config: MoltbotConfig;
config: OpenClawConfig;
runtime: BlueBubblesRuntimeEnv;
core: BlueBubblesCoreRuntime;
path: string;
@@ -340,7 +340,7 @@ const targetDebouncers = new Map<
>();
function resolveBlueBubblesDebounceMs(
config: MoltbotConfig,
config: OpenClawConfig,
core: BlueBubblesCoreRuntime,
): number {
const inbound = config.messages?.inbound;
@@ -940,7 +940,7 @@ function maskSecret(value: string): string {
}
function resolveBlueBubblesAckReaction(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
agentId: string;
core: BlueBubblesCoreRuntime;
runtime: BlueBubblesRuntimeEnv;
+9 -9
View File
@@ -1,17 +1,17 @@
import type {
ChannelOnboardingAdapter,
ChannelOnboardingDmPolicy,
MoltbotConfig,
OpenClawConfig,
DmPolicy,
WizardPrompter,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import {
DEFAULT_ACCOUNT_ID,
addWildcardAllowFrom,
formatDocsLink,
normalizeAccountId,
promptAccountId,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import {
listBlueBubblesAccountIds,
resolveBlueBubblesAccount,
@@ -22,7 +22,7 @@ import { parseBlueBubblesAllowTarget, normalizeBlueBubblesHandle } from "./targe
const channel = "bluebubbles" as const;
function setBlueBubblesDmPolicy(cfg: MoltbotConfig, dmPolicy: DmPolicy): MoltbotConfig {
function setBlueBubblesDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig {
const allowFrom =
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.bluebubbles?.allowFrom) : undefined;
return {
@@ -39,10 +39,10 @@ function setBlueBubblesDmPolicy(cfg: MoltbotConfig, dmPolicy: DmPolicy): Moltbot
}
function setBlueBubblesAllowFrom(
cfg: MoltbotConfig,
cfg: OpenClawConfig,
accountId: string,
allowFrom: string[],
): MoltbotConfig {
): OpenClawConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
@@ -81,10 +81,10 @@ function parseBlueBubblesAllowFromInput(raw: string): string[] {
}
async function promptBlueBubblesAllowFrom(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
prompter: WizardPrompter;
accountId?: string;
}): Promise<MoltbotConfig> {
}): Promise<OpenClawConfig> {
const accountId =
params.accountId && normalizeAccountId(params.accountId)
? (normalizeAccountId(params.accountId) ?? DEFAULT_ACCOUNT_ID)
@@ -318,7 +318,7 @@ export const blueBubblesOnboardingAdapter: ChannelOnboardingAdapter = {
[
"Configure the webhook URL in BlueBubbles Server:",
"1. Open BlueBubbles Server → Settings → Webhooks",
"2. Add your Moltbot gateway URL + webhook path",
"2. Add your OpenClaw gateway URL + webhook path",
" Example: https://your-gateway-host:3000/bluebubbles-webhook",
"3. Enable the webhook and save",
"",
+2 -2
View File
@@ -1,5 +1,5 @@
import { resolveBlueBubblesAccount } from "./accounts.js";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
export type BlueBubblesReactionOpts = {
@@ -7,7 +7,7 @@ export type BlueBubblesReactionOpts = {
password?: string;
accountId?: string;
timeoutMs?: number;
cfg?: MoltbotConfig;
cfg?: OpenClawConfig;
};
const REACTION_TYPES = new Set([
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
let runtime: PluginRuntime | null = null;
+2 -2
View File
@@ -6,7 +6,7 @@ import {
normalizeBlueBubblesHandle,
parseBlueBubblesTarget,
} from "./targets.js";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import {
blueBubblesFetchWithTimeout,
buildBlueBubblesApiUrl,
@@ -18,7 +18,7 @@ export type BlueBubblesSendOpts = {
password?: string;
accountId?: string;
timeoutMs?: number;
cfg?: MoltbotConfig;
cfg?: OpenClawConfig;
/** Message GUID to reply to (reply threading) */
replyToMessageGuid?: string;
/** Part index for reply (default: 0) */
+3 -3
View File
@@ -1,4 +1,4 @@
# Copilot Proxy (Clawdbot plugin)
# Copilot Proxy (OpenClaw plugin)
Provider plugin for the **Copilot Proxy** VS Code extension.
@@ -7,7 +7,7 @@ Provider plugin for the **Copilot Proxy** VS Code extension.
Bundled plugins are disabled by default. Enable this one:
```bash
clawdbot plugins enable copilot-proxy
openclaw plugins enable copilot-proxy
```
Restart the Gateway after enabling.
@@ -15,7 +15,7 @@ Restart the Gateway after enabling.
## Authenticate
```bash
clawdbot models auth login --provider copilot-proxy --set-default
openclaw models auth login --provider copilot-proxy --set-default
```
## Notes
+1 -1
View File
@@ -1,4 +1,4 @@
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
const DEFAULT_BASE_URL = "http://localhost:3000/v1";
const DEFAULT_API_KEY = "n/a";
+4 -4
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/copilot-proxy",
"version": "2026.1.29",
"name": "@openclaw/copilot-proxy",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Copilot Proxy provider plugin",
"moltbot": {
"description": "OpenClaw Copilot Proxy provider plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { createDiagnosticsOtelService } from "./src/service.js";
@@ -8,7 +8,7 @@ const plugin = {
name: "Diagnostics OpenTelemetry",
description: "Export diagnostics events to OpenTelemetry",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
api.registerService(createDiagnosticsOtelService());
},
};
+4 -4
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/diagnostics-otel",
"version": "2026.1.29",
"name": "@openclaw/diagnostics-otel",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot diagnostics OpenTelemetry exporter",
"moltbot": {
"description": "OpenClaw diagnostics OpenTelemetry exporter",
"openclaw": {
"extensions": [
"./index.ts"
]
+15 -15
View File
@@ -95,8 +95,8 @@ vi.mock("@opentelemetry/semantic-conventions", () => ({
},
}));
vi.mock("clawdbot/plugin-sdk", async () => {
const actual = await vi.importActual<typeof import("clawdbot/plugin-sdk")>("clawdbot/plugin-sdk");
vi.mock("openclaw/plugin-sdk", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk")>("openclaw/plugin-sdk");
return {
...actual,
registerLogTransport: registerLogTransportMock,
@@ -104,7 +104,7 @@ vi.mock("clawdbot/plugin-sdk", async () => {
});
import { createDiagnosticsOtelService } from "./service.js";
import { emitDiagnosticEvent } from "clawdbot/plugin-sdk";
import { emitDiagnosticEvent } from "openclaw/plugin-sdk";
describe("diagnostics-otel service", () => {
beforeEach(() => {
@@ -191,20 +191,20 @@ describe("diagnostics-otel service", () => {
attempt: 2,
});
expect(telemetryState.counters.get("moltbot.webhook.received")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("moltbot.webhook.duration_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("moltbot.message.queued")?.add).toHaveBeenCalled();
expect(telemetryState.counters.get("moltbot.message.processed")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("moltbot.message.duration_ms")?.record).toHaveBeenCalled();
expect(telemetryState.histograms.get("moltbot.queue.wait_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("moltbot.session.stuck")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("moltbot.session.stuck_age_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("moltbot.run.attempt")?.add).toHaveBeenCalled();
expect(telemetryState.counters.get("openclaw.webhook.received")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("openclaw.webhook.duration_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("openclaw.message.queued")?.add).toHaveBeenCalled();
expect(telemetryState.counters.get("openclaw.message.processed")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("openclaw.message.duration_ms")?.record).toHaveBeenCalled();
expect(telemetryState.histograms.get("openclaw.queue.wait_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("openclaw.session.stuck")?.add).toHaveBeenCalled();
expect(telemetryState.histograms.get("openclaw.session.stuck_age_ms")?.record).toHaveBeenCalled();
expect(telemetryState.counters.get("openclaw.run.attempt")?.add).toHaveBeenCalled();
const spanNames = telemetryState.tracer.startSpan.mock.calls.map((call) => call[0]);
expect(spanNames).toContain("moltbot.webhook.processed");
expect(spanNames).toContain("moltbot.message.processed");
expect(spanNames).toContain("moltbot.session.stuck");
expect(spanNames).toContain("openclaw.webhook.processed");
expect(spanNames).toContain("openclaw.message.processed");
expect(spanNames).toContain("openclaw.session.stuck");
expect(registerLogTransportMock).toHaveBeenCalledTimes(1);
expect(registeredTransports).toHaveLength(1);
+85 -85
View File
@@ -10,10 +10,10 @@ import { NodeSDK } from "@opentelemetry/sdk-node";
import { ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
import type { MoltbotPluginService, DiagnosticEventPayload } from "clawdbot/plugin-sdk";
import { onDiagnosticEvent, registerLogTransport } from "clawdbot/plugin-sdk";
import type { DiagnosticEventPayload, OpenClawPluginService } from "openclaw/plugin-sdk";
import { onDiagnosticEvent, registerLogTransport } from "openclaw/plugin-sdk";
const DEFAULT_SERVICE_NAME = "moltbot";
const DEFAULT_SERVICE_NAME = "openclaw";
function normalizeEndpoint(endpoint?: string): string | undefined {
const trimmed = endpoint?.trim();
@@ -32,7 +32,7 @@ function resolveSampleRate(value: number | undefined): number | undefined {
return value;
}
export function createDiagnosticsOtelService(): MoltbotPluginService {
export function createDiagnosticsOtelService(): OpenClawPluginService {
let sdk: NodeSDK | null = null;
let logProvider: LoggerProvider | null = null;
let stopLogTransport: (() => void) | null = null;
@@ -118,78 +118,78 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
FATAL: 21 as SeverityNumber,
};
const meter = metrics.getMeter("moltbot");
const tracer = trace.getTracer("moltbot");
const meter = metrics.getMeter("openclaw");
const tracer = trace.getTracer("openclaw");
const tokensCounter = meter.createCounter("moltbot.tokens", {
const tokensCounter = meter.createCounter("openclaw.tokens", {
unit: "1",
description: "Token usage by type",
});
const costCounter = meter.createCounter("moltbot.cost.usd", {
const costCounter = meter.createCounter("openclaw.cost.usd", {
unit: "1",
description: "Estimated model cost (USD)",
});
const durationHistogram = meter.createHistogram("moltbot.run.duration_ms", {
const durationHistogram = meter.createHistogram("openclaw.run.duration_ms", {
unit: "ms",
description: "Agent run duration",
});
const contextHistogram = meter.createHistogram("moltbot.context.tokens", {
const contextHistogram = meter.createHistogram("openclaw.context.tokens", {
unit: "1",
description: "Context window size and usage",
});
const webhookReceivedCounter = meter.createCounter("moltbot.webhook.received", {
const webhookReceivedCounter = meter.createCounter("openclaw.webhook.received", {
unit: "1",
description: "Webhook requests received",
});
const webhookErrorCounter = meter.createCounter("moltbot.webhook.error", {
const webhookErrorCounter = meter.createCounter("openclaw.webhook.error", {
unit: "1",
description: "Webhook processing errors",
});
const webhookDurationHistogram = meter.createHistogram("moltbot.webhook.duration_ms", {
const webhookDurationHistogram = meter.createHistogram("openclaw.webhook.duration_ms", {
unit: "ms",
description: "Webhook processing duration",
});
const messageQueuedCounter = meter.createCounter("moltbot.message.queued", {
const messageQueuedCounter = meter.createCounter("openclaw.message.queued", {
unit: "1",
description: "Messages queued for processing",
});
const messageProcessedCounter = meter.createCounter("moltbot.message.processed", {
const messageProcessedCounter = meter.createCounter("openclaw.message.processed", {
unit: "1",
description: "Messages processed by outcome",
});
const messageDurationHistogram = meter.createHistogram("moltbot.message.duration_ms", {
const messageDurationHistogram = meter.createHistogram("openclaw.message.duration_ms", {
unit: "ms",
description: "Message processing duration",
});
const queueDepthHistogram = meter.createHistogram("moltbot.queue.depth", {
const queueDepthHistogram = meter.createHistogram("openclaw.queue.depth", {
unit: "1",
description: "Queue depth on enqueue/dequeue",
});
const queueWaitHistogram = meter.createHistogram("moltbot.queue.wait_ms", {
const queueWaitHistogram = meter.createHistogram("openclaw.queue.wait_ms", {
unit: "ms",
description: "Queue wait time before execution",
});
const laneEnqueueCounter = meter.createCounter("moltbot.queue.lane.enqueue", {
const laneEnqueueCounter = meter.createCounter("openclaw.queue.lane.enqueue", {
unit: "1",
description: "Command queue lane enqueue events",
});
const laneDequeueCounter = meter.createCounter("moltbot.queue.lane.dequeue", {
const laneDequeueCounter = meter.createCounter("openclaw.queue.lane.dequeue", {
unit: "1",
description: "Command queue lane dequeue events",
});
const sessionStateCounter = meter.createCounter("moltbot.session.state", {
const sessionStateCounter = meter.createCounter("openclaw.session.state", {
unit: "1",
description: "Session state transitions",
});
const sessionStuckCounter = meter.createCounter("moltbot.session.stuck", {
const sessionStuckCounter = meter.createCounter("openclaw.session.stuck", {
unit: "1",
description: "Sessions stuck in processing",
});
const sessionStuckAgeHistogram = meter.createHistogram("moltbot.session.stuck_age_ms", {
const sessionStuckAgeHistogram = meter.createHistogram("openclaw.session.stuck_age_ms", {
unit: "ms",
description: "Age of stuck sessions",
});
const runAttemptCounter = meter.createCounter("moltbot.run.attempt", {
const runAttemptCounter = meter.createCounter("openclaw.run.attempt", {
unit: "1",
description: "Run attempts",
});
@@ -207,7 +207,7 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
: {}),
}),
);
const otelLogger = logProvider.getLogger("moltbot");
const otelLogger = logProvider.getLogger("openclaw");
stopLogTransport = registerLogTransport((logObj) => {
const safeStringify = (value: unknown) => {
@@ -265,29 +265,29 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
}
const attributes: Record<string, string | number | boolean> = {
"moltbot.log.level": logLevelName,
"openclaw.log.level": logLevelName,
};
if (meta?.name) attributes["moltbot.logger"] = meta.name;
if (meta?.name) attributes["openclaw.logger"] = meta.name;
if (meta?.parentNames?.length) {
attributes["moltbot.logger.parents"] = meta.parentNames.join(".");
attributes["openclaw.logger.parents"] = meta.parentNames.join(".");
}
if (bindings) {
for (const [key, value] of Object.entries(bindings)) {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
attributes[`moltbot.${key}`] = value;
attributes[`openclaw.${key}`] = value;
} else if (value != null) {
attributes[`moltbot.${key}`] = safeStringify(value);
attributes[`openclaw.${key}`] = safeStringify(value);
}
}
}
if (numericArgs.length > 0) {
attributes["moltbot.log.args"] = safeStringify(numericArgs);
attributes["openclaw.log.args"] = safeStringify(numericArgs);
}
if (meta?.path?.filePath) attributes["code.filepath"] = meta.path.filePath;
if (meta?.path?.fileLine) attributes["code.lineno"] = Number(meta.path.fileLine);
if (meta?.path?.method) attributes["code.function"] = meta.path.method;
if (meta?.path?.filePathWithLine) {
attributes["moltbot.code.location"] = meta.path.filePathWithLine;
attributes["openclaw.code.location"] = meta.path.filePathWithLine;
}
otelLogger.emit({
@@ -316,48 +316,48 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
const recordModelUsage = (evt: Extract<DiagnosticEventPayload, { type: "model.usage" }>) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.provider": evt.provider ?? "unknown",
"moltbot.model": evt.model ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.provider": evt.provider ?? "unknown",
"openclaw.model": evt.model ?? "unknown",
};
const usage = evt.usage;
if (usage.input) tokensCounter.add(usage.input, { ...attrs, "moltbot.token": "input" });
if (usage.output) tokensCounter.add(usage.output, { ...attrs, "moltbot.token": "output" });
if (usage.input) tokensCounter.add(usage.input, { ...attrs, "openclaw.token": "input" });
if (usage.output) tokensCounter.add(usage.output, { ...attrs, "openclaw.token": "output" });
if (usage.cacheRead)
tokensCounter.add(usage.cacheRead, { ...attrs, "moltbot.token": "cache_read" });
tokensCounter.add(usage.cacheRead, { ...attrs, "openclaw.token": "cache_read" });
if (usage.cacheWrite)
tokensCounter.add(usage.cacheWrite, { ...attrs, "moltbot.token": "cache_write" });
tokensCounter.add(usage.cacheWrite, { ...attrs, "openclaw.token": "cache_write" });
if (usage.promptTokens)
tokensCounter.add(usage.promptTokens, { ...attrs, "moltbot.token": "prompt" });
if (usage.total) tokensCounter.add(usage.total, { ...attrs, "moltbot.token": "total" });
tokensCounter.add(usage.promptTokens, { ...attrs, "openclaw.token": "prompt" });
if (usage.total) tokensCounter.add(usage.total, { ...attrs, "openclaw.token": "total" });
if (evt.costUsd) costCounter.add(evt.costUsd, attrs);
if (evt.durationMs) durationHistogram.record(evt.durationMs, attrs);
if (evt.context?.limit)
contextHistogram.record(evt.context.limit, {
...attrs,
"moltbot.context": "limit",
"openclaw.context": "limit",
});
if (evt.context?.used)
contextHistogram.record(evt.context.used, {
...attrs,
"moltbot.context": "used",
"openclaw.context": "used",
});
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = {
...attrs,
"moltbot.sessionKey": evt.sessionKey ?? "",
"moltbot.sessionId": evt.sessionId ?? "",
"moltbot.tokens.input": usage.input ?? 0,
"moltbot.tokens.output": usage.output ?? 0,
"moltbot.tokens.cache_read": usage.cacheRead ?? 0,
"moltbot.tokens.cache_write": usage.cacheWrite ?? 0,
"moltbot.tokens.total": usage.total ?? 0,
"openclaw.sessionKey": evt.sessionKey ?? "",
"openclaw.sessionId": evt.sessionId ?? "",
"openclaw.tokens.input": usage.input ?? 0,
"openclaw.tokens.output": usage.output ?? 0,
"openclaw.tokens.cache_read": usage.cacheRead ?? 0,
"openclaw.tokens.cache_write": usage.cacheWrite ?? 0,
"openclaw.tokens.total": usage.total ?? 0,
};
const span = spanWithDuration("moltbot.model.usage", spanAttrs, evt.durationMs);
const span = spanWithDuration("openclaw.model.usage", spanAttrs, evt.durationMs);
span.end();
};
@@ -365,8 +365,8 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
evt: Extract<DiagnosticEventPayload, { type: "webhook.received" }>,
) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.webhook": evt.updateType ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.webhook": evt.updateType ?? "unknown",
};
webhookReceivedCounter.add(1, attrs);
};
@@ -375,16 +375,16 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
evt: Extract<DiagnosticEventPayload, { type: "webhook.processed" }>,
) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.webhook": evt.updateType ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.webhook": evt.updateType ?? "unknown",
};
if (typeof evt.durationMs === "number") {
webhookDurationHistogram.record(evt.durationMs, attrs);
}
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = { ...attrs };
if (evt.chatId !== undefined) spanAttrs["moltbot.chatId"] = String(evt.chatId);
const span = spanWithDuration("moltbot.webhook.processed", spanAttrs, evt.durationMs);
if (evt.chatId !== undefined) spanAttrs["openclaw.chatId"] = String(evt.chatId);
const span = spanWithDuration("openclaw.webhook.processed", spanAttrs, evt.durationMs);
span.end();
};
@@ -392,17 +392,17 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
evt: Extract<DiagnosticEventPayload, { type: "webhook.error" }>,
) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.webhook": evt.updateType ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.webhook": evt.updateType ?? "unknown",
};
webhookErrorCounter.add(1, attrs);
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = {
...attrs,
"moltbot.error": evt.error,
"openclaw.error": evt.error,
};
if (evt.chatId !== undefined) spanAttrs["moltbot.chatId"] = String(evt.chatId);
const span = tracer.startSpan("moltbot.webhook.error", {
if (evt.chatId !== undefined) spanAttrs["openclaw.chatId"] = String(evt.chatId);
const span = tracer.startSpan("openclaw.webhook.error", {
attributes: spanAttrs,
});
span.setStatus({ code: SpanStatusCode.ERROR, message: evt.error });
@@ -413,8 +413,8 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
evt: Extract<DiagnosticEventPayload, { type: "message.queued" }>,
) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.source": evt.source ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.source": evt.source ?? "unknown",
};
messageQueuedCounter.add(1, attrs);
if (typeof evt.queueDepth === "number") {
@@ -426,8 +426,8 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
evt: Extract<DiagnosticEventPayload, { type: "message.processed" }>,
) => {
const attrs = {
"moltbot.channel": evt.channel ?? "unknown",
"moltbot.outcome": evt.outcome ?? "unknown",
"openclaw.channel": evt.channel ?? "unknown",
"openclaw.outcome": evt.outcome ?? "unknown",
};
messageProcessedCounter.add(1, attrs);
if (typeof evt.durationMs === "number") {
@@ -435,12 +435,12 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
}
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = { ...attrs };
if (evt.sessionKey) spanAttrs["moltbot.sessionKey"] = evt.sessionKey;
if (evt.sessionId) spanAttrs["moltbot.sessionId"] = evt.sessionId;
if (evt.chatId !== undefined) spanAttrs["moltbot.chatId"] = String(evt.chatId);
if (evt.messageId !== undefined) spanAttrs["moltbot.messageId"] = String(evt.messageId);
if (evt.reason) spanAttrs["moltbot.reason"] = evt.reason;
const span = spanWithDuration("moltbot.message.processed", spanAttrs, evt.durationMs);
if (evt.sessionKey) spanAttrs["openclaw.sessionKey"] = evt.sessionKey;
if (evt.sessionId) spanAttrs["openclaw.sessionId"] = evt.sessionId;
if (evt.chatId !== undefined) spanAttrs["openclaw.chatId"] = String(evt.chatId);
if (evt.messageId !== undefined) spanAttrs["openclaw.messageId"] = String(evt.messageId);
if (evt.reason) spanAttrs["openclaw.reason"] = evt.reason;
const span = spanWithDuration("openclaw.message.processed", spanAttrs, evt.durationMs);
if (evt.outcome === "error") {
span.setStatus({ code: SpanStatusCode.ERROR, message: evt.error });
}
@@ -450,7 +450,7 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
const recordLaneEnqueue = (
evt: Extract<DiagnosticEventPayload, { type: "queue.lane.enqueue" }>,
) => {
const attrs = { "moltbot.lane": evt.lane };
const attrs = { "openclaw.lane": evt.lane };
laneEnqueueCounter.add(1, attrs);
queueDepthHistogram.record(evt.queueSize, attrs);
};
@@ -458,7 +458,7 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
const recordLaneDequeue = (
evt: Extract<DiagnosticEventPayload, { type: "queue.lane.dequeue" }>,
) => {
const attrs = { "moltbot.lane": evt.lane };
const attrs = { "openclaw.lane": evt.lane };
laneDequeueCounter.add(1, attrs);
queueDepthHistogram.record(evt.queueSize, attrs);
if (typeof evt.waitMs === "number") {
@@ -469,38 +469,38 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
const recordSessionState = (
evt: Extract<DiagnosticEventPayload, { type: "session.state" }>,
) => {
const attrs: Record<string, string> = { "moltbot.state": evt.state };
if (evt.reason) attrs["moltbot.reason"] = evt.reason;
const attrs: Record<string, string> = { "openclaw.state": evt.state };
if (evt.reason) attrs["openclaw.reason"] = evt.reason;
sessionStateCounter.add(1, attrs);
};
const recordSessionStuck = (
evt: Extract<DiagnosticEventPayload, { type: "session.stuck" }>,
) => {
const attrs: Record<string, string> = { "moltbot.state": evt.state };
const attrs: Record<string, string> = { "openclaw.state": evt.state };
sessionStuckCounter.add(1, attrs);
if (typeof evt.ageMs === "number") {
sessionStuckAgeHistogram.record(evt.ageMs, attrs);
}
if (!tracesEnabled) return;
const spanAttrs: Record<string, string | number> = { ...attrs };
if (evt.sessionKey) spanAttrs["moltbot.sessionKey"] = evt.sessionKey;
if (evt.sessionId) spanAttrs["moltbot.sessionId"] = evt.sessionId;
spanAttrs["moltbot.queueDepth"] = evt.queueDepth ?? 0;
spanAttrs["moltbot.ageMs"] = evt.ageMs;
const span = tracer.startSpan("moltbot.session.stuck", { attributes: spanAttrs });
if (evt.sessionKey) spanAttrs["openclaw.sessionKey"] = evt.sessionKey;
if (evt.sessionId) spanAttrs["openclaw.sessionId"] = evt.sessionId;
spanAttrs["openclaw.queueDepth"] = evt.queueDepth ?? 0;
spanAttrs["openclaw.ageMs"] = evt.ageMs;
const span = tracer.startSpan("openclaw.session.stuck", { attributes: spanAttrs });
span.setStatus({ code: SpanStatusCode.ERROR, message: "session stuck" });
span.end();
};
const recordRunAttempt = (evt: Extract<DiagnosticEventPayload, { type: "run.attempt" }>) => {
runAttemptCounter.add(1, { "moltbot.attempt": evt.attempt });
runAttemptCounter.add(1, { "openclaw.attempt": evt.attempt });
};
const recordHeartbeat = (
evt: Extract<DiagnosticEventPayload, { type: "diagnostic.heartbeat" }>,
) => {
queueDepthHistogram.record(evt.queued, { "moltbot.channel": "heartbeat" });
queueDepthHistogram.record(evt.queued, { "openclaw.channel": "heartbeat" });
};
unsubscribe = onDiagnosticEvent((evt: DiagnosticEventPayload) => {
@@ -562,5 +562,5 @@ export function createDiagnosticsOtelService(): MoltbotPluginService {
sdk = null;
}
},
} satisfies MoltbotPluginService;
} satisfies OpenClawPluginService;
}
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { discordPlugin } from "./src/channel.js";
import { setDiscordRuntime } from "./src/runtime.js";
@@ -9,7 +9,7 @@ const plugin = {
name: "Discord",
description: "Discord channel plugin",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setDiscordRuntime(api.runtime);
api.registerChannel({ plugin: discordPlugin });
},
+4 -4
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/discord",
"version": "2026.1.29",
"name": "@openclaw/discord",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Discord channel plugin",
"moltbot": {
"description": "OpenClaw Discord channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+1 -1
View File
@@ -25,7 +25,7 @@ import {
type ChannelMessageActionAdapter,
type ChannelPlugin,
type ResolvedDiscordAccount,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { getDiscordRuntime } from "./runtime.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
let runtime: PluginRuntime | null = null;
+3 -3
View File
@@ -1,4 +1,4 @@
# Google Antigravity Auth (Clawdbot plugin)
# Google Antigravity Auth (OpenClaw plugin)
OAuth provider plugin for **Google Antigravity** (Cloud Code Assist).
@@ -7,7 +7,7 @@ OAuth provider plugin for **Google Antigravity** (Cloud Code Assist).
Bundled plugins are disabled by default. Enable this one:
```bash
clawdbot plugins enable google-antigravity-auth
openclaw plugins enable google-antigravity-auth
```
Restart the Gateway after enabling.
@@ -15,7 +15,7 @@ Restart the Gateway after enabling.
## Authenticate
```bash
clawdbot models auth login --provider google-antigravity --set-default
openclaw models auth login --provider google-antigravity --set-default
```
## Notes
+2 -2
View File
@@ -1,7 +1,7 @@
import { createHash, randomBytes } from "node:crypto";
import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
// OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync
const decode = (s: string) => Buffer.from(s, "base64").toString();
@@ -32,7 +32,7 @@ const RESPONSE_PAGE = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moltbot Antigravity OAuth</title>
<title>OpenClaw Antigravity OAuth</title>
</head>
<body>
<main>
@@ -1,9 +1,9 @@
{
"name": "@moltbot/google-antigravity-auth",
"version": "2026.1.29",
"name": "@openclaw/google-antigravity-auth",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Google Antigravity OAuth provider plugin",
"moltbot": {
"description": "OpenClaw Google Antigravity OAuth provider plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+5 -5
View File
@@ -1,4 +1,4 @@
# Google Gemini CLI Auth (Clawdbot plugin)
# Google Gemini CLI Auth (OpenClaw plugin)
OAuth provider plugin for **Gemini CLI** (Google Code Assist).
@@ -7,7 +7,7 @@ OAuth provider plugin for **Gemini CLI** (Google Code Assist).
Bundled plugins are disabled by default. Enable this one:
```bash
clawdbot plugins enable google-gemini-cli-auth
openclaw plugins enable google-gemini-cli-auth
```
Restart the Gateway after enabling.
@@ -15,7 +15,7 @@ Restart the Gateway after enabling.
## Authenticate
```bash
clawdbot models auth login --provider google-gemini-cli --set-default
openclaw models auth login --provider google-gemini-cli --set-default
```
## Requirements
@@ -31,5 +31,5 @@ brew install gemini-cli
Override auto-detected credentials with:
- `CLAWDBOT_GEMINI_OAUTH_CLIENT_ID` / `GEMINI_CLI_OAUTH_CLIENT_ID`
- `CLAWDBOT_GEMINI_OAUTH_CLIENT_SECRET` / `GEMINI_CLI_OAUTH_CLIENT_SECRET`
- `OPENCLAW_GEMINI_OAUTH_CLIENT_ID` / `GEMINI_CLI_OAUTH_CLIENT_ID`
- `OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET` / `GEMINI_CLI_OAUTH_CLIENT_SECRET`
+3 -3
View File
@@ -1,4 +1,4 @@
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { loginGeminiCliOAuth } from "./oauth.js";
@@ -6,8 +6,8 @@ const PROVIDER_ID = "google-gemini-cli";
const PROVIDER_LABEL = "Gemini CLI OAuth";
const DEFAULT_MODEL = "google-gemini-cli/gemini-3-pro-preview";
const ENV_VARS = [
"CLAWDBOT_GEMINI_OAUTH_CLIENT_ID",
"CLAWDBOT_GEMINI_OAUTH_CLIENT_SECRET",
"OPENCLAW_GEMINI_OAUTH_CLIENT_ID",
"OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET",
"GEMINI_CLI_OAUTH_CLIENT_ID",
"GEMINI_CLI_OAUTH_CLIENT_SECRET",
];
+4 -4
View File
@@ -3,9 +3,9 @@ import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
import { createServer } from "node:http";
import { delimiter, dirname, join } from "node:path";
const CLIENT_ID_KEYS = ["CLAWDBOT_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"];
const CLIENT_ID_KEYS = ["OPENCLAW_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"];
const CLIENT_SECRET_KEYS = [
"CLAWDBOT_GEMINI_OAUTH_CLIENT_SECRET",
"OPENCLAW_GEMINI_OAUTH_CLIENT_SECRET",
"GEMINI_CLI_OAUTH_CLIENT_SECRET",
];
const REDIRECT_URI = "http://localhost:8085/oauth2callback";
@@ -262,7 +262,7 @@ async function waitForLocalCallback(params: {
res.end(
"<!doctype html><html><head><meta charset='utf-8'/></head>" +
"<body><h2>Gemini CLI OAuth complete</h2>" +
"<p>You can close this window and return to Moltbot.</p></body></html>",
"<p>You can close this window and return to OpenClaw.</p></body></html>",
);
finish(undefined, { code, state });
@@ -367,7 +367,7 @@ async function discoverProject(accessToken: string): Promise<string> {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "gl-node/moltbot",
"X-Goog-Api-Client": "gl-node/openclaw",
};
const loadBody = {
@@ -1,9 +1,9 @@
{
"name": "@moltbot/google-gemini-cli-auth",
"version": "2026.1.29",
"name": "@openclaw/google-gemini-cli-auth",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Gemini CLI OAuth provider plugin",
"moltbot": {
"description": "OpenClaw Gemini CLI OAuth provider plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+4 -4
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { googlechatDock, googlechatPlugin } from "./src/channel.js";
import { handleGoogleChatWebhookRequest } from "./src/monitor.js";
@@ -8,9 +8,9 @@ import { setGoogleChatRuntime } from "./src/runtime.js";
const plugin = {
id: "googlechat",
name: "Google Chat",
description: "Moltbot Google Chat channel plugin",
description: "OpenClaw Google Chat channel plugin",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setGoogleChatRuntime(api.runtime);
api.registerChannel({ plugin: googlechatPlugin, dock: googlechatDock });
api.registerHttpHandler(handleGoogleChatWebhookRequest);
+7 -7
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/googlechat",
"version": "2026.1.29",
"name": "@openclaw/googlechat",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Google Chat channel plugin",
"moltbot": {
"description": "OpenClaw Google Chat channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
],
@@ -22,7 +22,7 @@
"order": 55
},
"install": {
"npmSpec": "@moltbot/googlechat",
"npmSpec": "@openclaw/googlechat",
"localPath": "extensions/googlechat",
"defaultChoice": "npm"
}
@@ -31,9 +31,9 @@
"google-auth-library": "^10.5.0"
},
"devDependencies": {
"moltbot": "workspace:*"
"openclaw": "workspace:*"
},
"peerDependencies": {
"moltbot": ">=2026.1.26"
"openclaw": ">=2026.1.26"
}
}
+9 -9
View File
@@ -1,5 +1,5 @@
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
import type { GoogleChatAccountConfig, GoogleChatConfig } from "./types.config.js";
@@ -18,19 +18,19 @@ export type ResolvedGoogleChatAccount = {
const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
function listConfiguredAccountIds(cfg: MoltbotConfig): string[] {
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
const accounts = (cfg.channels?.["googlechat"] as GoogleChatConfig | undefined)?.accounts;
if (!accounts || typeof accounts !== "object") return [];
return Object.keys(accounts).filter(Boolean);
}
export function listGoogleChatAccountIds(cfg: MoltbotConfig): string[] {
export function listGoogleChatAccountIds(cfg: OpenClawConfig): string[] {
const ids = listConfiguredAccountIds(cfg);
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
return ids.sort((a, b) => a.localeCompare(b));
}
export function resolveDefaultGoogleChatAccountId(cfg: MoltbotConfig): string {
export function resolveDefaultGoogleChatAccountId(cfg: OpenClawConfig): string {
const channel = cfg.channels?.["googlechat"] as GoogleChatConfig | undefined;
if (channel?.defaultAccount?.trim()) return channel.defaultAccount.trim();
const ids = listGoogleChatAccountIds(cfg);
@@ -39,7 +39,7 @@ export function resolveDefaultGoogleChatAccountId(cfg: MoltbotConfig): string {
}
function resolveAccountConfig(
cfg: MoltbotConfig,
cfg: OpenClawConfig,
accountId: string,
): GoogleChatAccountConfig | undefined {
const accounts = (cfg.channels?.["googlechat"] as GoogleChatConfig | undefined)?.accounts;
@@ -48,7 +48,7 @@ function resolveAccountConfig(
}
function mergeGoogleChatAccountConfig(
cfg: MoltbotConfig,
cfg: OpenClawConfig,
accountId: string,
): GoogleChatAccountConfig {
const raw = (cfg.channels?.["googlechat"] ?? {}) as GoogleChatConfig;
@@ -104,7 +104,7 @@ function resolveCredentialsFromConfig(params: {
}
export function resolveGoogleChatAccount(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
accountId?: string | null;
}): ResolvedGoogleChatAccount {
const accountId = normalizeAccountId(params.accountId);
@@ -126,7 +126,7 @@ export function resolveGoogleChatAccount(params: {
};
}
export function listEnabledGoogleChatAccounts(cfg: MoltbotConfig): ResolvedGoogleChatAccount[] {
export function listEnabledGoogleChatAccounts(cfg: OpenClawConfig): ResolvedGoogleChatAccount[] {
return listGoogleChatAccountIds(cfg)
.map((accountId) => resolveGoogleChatAccount({ cfg, accountId }))
.filter((account) => account.enabled);
+8 -8
View File
@@ -1,15 +1,15 @@
import type {
ChannelMessageActionAdapter,
ChannelMessageActionName,
MoltbotConfig,
} from "clawdbot/plugin-sdk";
OpenClawConfig,
} from "openclaw/plugin-sdk";
import {
createActionGate,
jsonResult,
readNumberParam,
readReactionParams,
readStringParam,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { listEnabledGoogleChatAccounts, resolveGoogleChatAccount } from "./accounts.js";
import {
@@ -24,13 +24,13 @@ import { resolveGoogleChatOutboundSpace } from "./targets.js";
const providerId = "googlechat";
function listEnabledAccounts(cfg: MoltbotConfig) {
function listEnabledAccounts(cfg: OpenClawConfig) {
return listEnabledGoogleChatAccounts(cfg).filter(
(account) => account.enabled && account.credentialSource !== "none",
);
}
function isReactionsEnabled(accounts: ReturnType<typeof listEnabledAccounts>, cfg: MoltbotConfig) {
function isReactionsEnabled(accounts: ReturnType<typeof listEnabledAccounts>, cfg: OpenClawConfig) {
for (const account of accounts) {
const gate = createActionGate(
(account.config.actions ?? (cfg.channels?.["googlechat"] as { actions?: unknown })?.actions) as Record<
@@ -49,11 +49,11 @@ function resolveAppUserNames(account: { config: { botUser?: string | null } }) {
export const googlechatMessageActions: ChannelMessageActionAdapter = {
listActions: ({ cfg }) => {
const accounts = listEnabledAccounts(cfg as MoltbotConfig);
const accounts = listEnabledAccounts(cfg as OpenClawConfig);
if (accounts.length === 0) return [];
const actions = new Set<ChannelMessageActionName>([]);
actions.add("send");
if (isReactionsEnabled(accounts, cfg as MoltbotConfig)) {
if (isReactionsEnabled(accounts, cfg as OpenClawConfig)) {
actions.add("react");
actions.add("reactions");
}
@@ -69,7 +69,7 @@ export const googlechatMessageActions: ChannelMessageActionAdapter = {
},
handleAction: async ({ action, params, cfg, accountId }) => {
const account = resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
});
if (account.credentialSource === "none") {
+1 -1
View File
@@ -153,7 +153,7 @@ export async function uploadGoogleChatAttachment(params: {
contentType?: string;
}): Promise<{ attachmentUploadToken?: string }> {
const { account, space, filename, buffer, contentType } = params;
const boundary = `moltbot-${crypto.randomUUID()}`;
const boundary = `openclaw-${crypto.randomUUID()}`;
const metadata = JSON.stringify({ filename });
const header = `--${boundary}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n${metadata}\r\n`;
const mediaHeader = `--${boundary}\r\nContent-Type: ${contentType ?? "application/octet-stream"}\r\n\r\n`;
+23 -23
View File
@@ -15,9 +15,9 @@ import {
type ChannelDock,
type ChannelMessageActionAdapter,
type ChannelPlugin,
type MoltbotConfig,
} from "clawdbot/plugin-sdk";
import { GoogleChatConfigSchema } from "clawdbot/plugin-sdk";
type OpenClawConfig,
} from "openclaw/plugin-sdk";
import { GoogleChatConfigSchema } from "openclaw/plugin-sdk";
import {
listGoogleChatAccountIds,
@@ -59,7 +59,7 @@ export const googlechatDock: ChannelDock = {
outbound: { textChunkLimit: 4000 },
config: {
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveGoogleChatAccount({ cfg: cfg as MoltbotConfig, accountId }).config.dm?.allowFrom ??
(resolveGoogleChatAccount({ cfg: cfg as OpenClawConfig, accountId }).config.dm?.allowFrom ??
[]
).map((entry) => String(entry)),
formatAllowFrom: ({ allowFrom }) =>
@@ -103,7 +103,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
idLabel: "googlechatUserId",
normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
notifyApproval: async ({ cfg, id }) => {
const account = resolveGoogleChatAccount({ cfg: cfg as MoltbotConfig });
const account = resolveGoogleChatAccount({ cfg: cfg as OpenClawConfig });
if (account.credentialSource === "none") return;
const user = normalizeGoogleChatTarget(id) ?? id;
const target = isGoogleChatUserTarget(user) ? user : `users/${user}`;
@@ -129,13 +129,13 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
reload: { configPrefixes: ["channels.googlechat"] },
configSchema: buildChannelConfigSchema(GoogleChatConfigSchema),
config: {
listAccountIds: (cfg) => listGoogleChatAccountIds(cfg as MoltbotConfig),
listAccountIds: (cfg) => listGoogleChatAccountIds(cfg as OpenClawConfig),
resolveAccount: (cfg, accountId) =>
resolveGoogleChatAccount({ cfg: cfg as MoltbotConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultGoogleChatAccountId(cfg as MoltbotConfig),
resolveGoogleChatAccount({ cfg: cfg as OpenClawConfig, accountId }),
defaultAccountId: (cfg) => resolveDefaultGoogleChatAccountId(cfg as OpenClawConfig),
setAccountEnabled: ({ cfg, accountId, enabled }) =>
setAccountEnabledInConfigSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
sectionKey: "googlechat",
accountId,
enabled,
@@ -143,7 +143,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
}),
deleteAccount: ({ cfg, accountId }) =>
deleteAccountFromConfigSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
sectionKey: "googlechat",
accountId,
clearBaseFields: [
@@ -167,7 +167,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
}),
resolveAllowFrom: ({ cfg, accountId }) =>
(resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
}).config.dm?.allowFrom ?? []
).map((entry) => String(entry)),
@@ -181,7 +181,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
resolveDmPolicy: ({ cfg, accountId, account }) => {
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
const useAccountPath = Boolean(
(cfg as MoltbotConfig).channels?.["googlechat"]?.accounts?.[resolvedAccountId],
(cfg as OpenClawConfig).channels?.["googlechat"]?.accounts?.[resolvedAccountId],
);
const allowFromPath = useAccountPath
? `channels.googlechat.accounts.${resolvedAccountId}.dm.`
@@ -231,7 +231,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
self: async () => null,
listPeers: async ({ cfg, accountId, query, limit }) => {
const account = resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
});
const q = query?.trim().toLowerCase() || "";
@@ -251,7 +251,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
},
listGroups: async ({ cfg, accountId, query, limit }) => {
const account = resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
});
const groups = account.config.groups ?? {};
@@ -291,7 +291,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
applyAccountName: ({ cfg, accountId, name }) =>
applyAccountNameToChannelSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
channelKey: "googlechat",
accountId,
name,
@@ -307,7 +307,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
},
applyAccountConfig: ({ cfg, accountId, input }) => {
const namedConfig = applyAccountNameToChannelSection({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
channelKey: "googlechat",
accountId,
name: input.name,
@@ -315,7 +315,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
const next =
accountId !== DEFAULT_ACCOUNT_ID
? migrateBaseNameToDefaultAccount({
cfg: namedConfig as MoltbotConfig,
cfg: namedConfig as OpenClawConfig,
channelKey: "googlechat",
})
: namedConfig;
@@ -348,7 +348,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
...configPatch,
},
},
} as MoltbotConfig;
} as OpenClawConfig;
}
return {
...next,
@@ -367,7 +367,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
},
},
},
} as MoltbotConfig;
} as OpenClawConfig;
},
},
outbound: {
@@ -414,7 +414,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
},
sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
const account = resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
});
const space = await resolveGoogleChatOutboundSpace({ account, target: to });
@@ -436,14 +436,14 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
throw new Error("Google Chat mediaUrl is required.");
}
const account = resolveGoogleChatAccount({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
accountId,
});
const space = await resolveGoogleChatOutboundSpace({ account, target: to });
const thread = (threadId ?? replyToId ?? undefined) as string | undefined;
const runtime = getGoogleChatRuntime();
const maxBytes = resolveChannelMediaMaxBytes({
cfg: cfg as MoltbotConfig,
cfg: cfg as OpenClawConfig,
resolveChannelLimitMb: ({ cfg, accountId }) =>
(cfg.channels?.["googlechat"] as { accounts?: Record<string, { mediaMaxMb?: number }>; mediaMaxMb?: number } | undefined)
?.accounts?.[accountId]?.mediaMaxMb ??
@@ -560,7 +560,7 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
});
const unregister = await startGoogleChatMonitor({
account,
config: ctx.cfg as MoltbotConfig,
config: ctx.cfg as OpenClawConfig,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
webhookPath: account.config.webhookPath,
+9 -9
View File
@@ -1,7 +1,7 @@
import type { IncomingMessage, ServerResponse } from "node:http";
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import { resolveMentionGatingWithBypass } from "clawdbot/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { resolveMentionGatingWithBypass } from "openclaw/plugin-sdk";
import {
type ResolvedGoogleChatAccount
@@ -30,7 +30,7 @@ export type GoogleChatRuntimeEnv = {
export type GoogleChatMonitorOptions = {
account: ResolvedGoogleChatAccount;
config: MoltbotConfig;
config: OpenClawConfig;
runtime: GoogleChatRuntimeEnv;
abortSignal: AbortSignal;
webhookPath?: string;
@@ -42,7 +42,7 @@ type GoogleChatCoreRuntime = ReturnType<typeof getGoogleChatRuntime>;
type WebhookTarget = {
account: ResolvedGoogleChatAccount;
config: MoltbotConfig;
config: OpenClawConfig;
runtime: GoogleChatRuntimeEnv;
core: GoogleChatCoreRuntime;
path: string;
@@ -357,24 +357,24 @@ function extractMentionInfo(annotations: GoogleChatAnnotation[], botUser?: strin
* Resolve bot display name with fallback chain:
* 1. Account config name
* 2. Agent name from config
* 3. "Moltbot" as generic fallback
* 3. "OpenClaw" as generic fallback
*/
function resolveBotDisplayName(params: {
accountName?: string;
agentId: string;
config: MoltbotConfig;
config: OpenClawConfig;
}): string {
const { accountName, agentId, config } = params;
if (accountName?.trim()) return accountName.trim();
const agent = config.agents?.list?.find((a) => a.id === agentId);
if (agent?.name?.trim()) return agent.name.trim();
return "Moltbot";
return "OpenClaw";
}
async function processMessageWithPipeline(params: {
event: GoogleChatEvent;
account: ResolvedGoogleChatAccount;
config: MoltbotConfig;
config: OpenClawConfig;
runtime: GoogleChatRuntimeEnv;
core: GoogleChatCoreRuntime;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
@@ -726,7 +726,7 @@ async function deliverGoogleChatReply(params: {
spaceId: string;
runtime: GoogleChatRuntimeEnv;
core: GoogleChatCoreRuntime;
config: MoltbotConfig;
config: OpenClawConfig;
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
typingMessageName?: string;
}): Promise<void> {
+11 -11
View File
@@ -1,4 +1,4 @@
import type { MoltbotConfig, DmPolicy } from "clawdbot/plugin-sdk";
import type { OpenClawConfig, DmPolicy } from "openclaw/plugin-sdk";
import {
addWildcardAllowFrom,
formatDocsLink,
@@ -9,7 +9,7 @@ import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
migrateBaseNameToDefaultAccount,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import {
listGoogleChatAccountIds,
@@ -22,7 +22,7 @@ const channel = "googlechat" as const;
const ENV_SERVICE_ACCOUNT = "GOOGLE_CHAT_SERVICE_ACCOUNT";
const ENV_SERVICE_ACCOUNT_FILE = "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE";
function setGoogleChatDmPolicy(cfg: MoltbotConfig, policy: DmPolicy) {
function setGoogleChatDmPolicy(cfg: OpenClawConfig, policy: DmPolicy) {
const allowFrom =
policy === "open"
? addWildcardAllowFrom(cfg.channels?.["googlechat"]?.dm?.allowFrom)
@@ -51,9 +51,9 @@ function parseAllowFromInput(raw: string): string[] {
}
async function promptAllowFrom(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
prompter: WizardPrompter;
}): Promise<MoltbotConfig> {
}): Promise<OpenClawConfig> {
const current = params.cfg.channels?.["googlechat"]?.dm?.allowFrom ?? [];
const entry = await params.prompter.text({
message: "Google Chat allowFrom (user id or email)",
@@ -91,10 +91,10 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
};
function applyAccountConfig(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
accountId: string;
patch: Record<string, unknown>;
}): MoltbotConfig {
}): OpenClawConfig {
const { cfg, accountId, patch } = params;
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
@@ -130,10 +130,10 @@ function applyAccountConfig(params: {
}
async function promptCredentials(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
prompter: WizardPrompter;
accountId: string;
}): Promise<MoltbotConfig> {
}): Promise<OpenClawConfig> {
const { cfg, prompter, accountId } = params;
const envReady =
accountId === DEFAULT_ACCOUNT_ID &&
@@ -184,10 +184,10 @@ async function promptCredentials(params: {
}
async function promptAudience(params: {
cfg: MoltbotConfig;
cfg: OpenClawConfig;
prompter: WizardPrompter;
accountId: string;
}): Promise<MoltbotConfig> {
}): Promise<OpenClawConfig> {
const account = resolveGoogleChatAccount({
cfg: params.cfg,
accountId: params.accountId,
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
let runtime: PluginRuntime | null = null;
+1 -1
View File
@@ -1,3 +1,3 @@
import type { GoogleChatAccountConfig, GoogleChatConfig } from "clawdbot/plugin-sdk";
import type { GoogleChatAccountConfig, GoogleChatConfig } from "openclaw/plugin-sdk";
export type { GoogleChatAccountConfig, GoogleChatConfig };
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { imessagePlugin } from "./src/channel.js";
import { setIMessageRuntime } from "./src/runtime.js";
@@ -9,7 +9,7 @@ const plugin = {
name: "iMessage",
description: "iMessage channel plugin",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setIMessageRuntime(api.runtime);
api.registerChannel({ plugin: imessagePlugin });
},
+4 -4
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/imessage",
"version": "2026.1.29",
"name": "@openclaw/imessage",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot iMessage channel plugin",
"moltbot": {
"description": "OpenClaw iMessage channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+1 -1
View File
@@ -21,7 +21,7 @@ import {
setAccountEnabledInConfigSection,
type ChannelPlugin,
type ResolvedIMessageAccount,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { getIMessageRuntime } from "./runtime.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
let runtime: PluginRuntime | null = null;
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { linePlugin } from "./src/channel.js";
import { registerLineCardCommand } from "./src/card-command.js";
@@ -10,7 +10,7 @@ const plugin = {
name: "LINE",
description: "LINE Messaging API channel plugin",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setLineRuntime(api.runtime);
api.registerChannel({ plugin: linePlugin });
registerLineCardCommand(api);
+6 -6
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/line",
"version": "2026.1.29",
"name": "@openclaw/line",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot LINE channel plugin",
"moltbot": {
"description": "OpenClaw LINE channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
],
@@ -18,12 +18,12 @@
"quickstartAllowFrom": true
},
"install": {
"npmSpec": "@moltbot/line",
"npmSpec": "@openclaw/line",
"localPath": "extensions/line",
"defaultChoice": "npm"
}
},
"devDependencies": {
"moltbot": "workspace:*"
"openclaw": "workspace:*"
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
import type { MoltbotPluginApi, LineChannelData, ReplyPayload } from "clawdbot/plugin-sdk";
import type { LineChannelData, OpenClawPluginApi, ReplyPayload } from "openclaw/plugin-sdk";
import {
createActionCard,
createImageCard,
@@ -7,7 +7,7 @@ import {
createReceiptCard,
type CardAction,
type ListItem,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
const CARD_USAGE = `Usage: /card <type> "title" "body" [options]
@@ -150,7 +150,7 @@ function parseCardArgs(argsStr: string): {
return result;
}
export function registerLineCardCommand(api: MoltbotPluginApi): void {
export function registerLineCardCommand(api: OpenClawPluginApi): void {
api.registerCommand({
name: "card",
description: "Send a rich card message (LINE).",
+4 -4
View File
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { MoltbotConfig, PluginRuntime } from "clawdbot/plugin-sdk";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";
@@ -12,7 +12,7 @@ type LineRuntimeMocks = {
function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } {
const writeConfigFile = vi.fn(async () => {});
const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: MoltbotConfig; accountId?: string }) => {
const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => {
const lineConfig = (cfg.channels?.line ?? {}) as {
tokenFile?: string;
secretFile?: string;
@@ -48,7 +48,7 @@ describe("linePlugin gateway.logoutAccount", () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
line: {
tokenFile: "/tmp/token",
@@ -71,7 +71,7 @@ describe("linePlugin gateway.logoutAccount", () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg: MoltbotConfig = {
const cfg: OpenClawConfig = {
channels: {
line: {
accounts: {
@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import type { MoltbotConfig, PluginRuntime } from "clawdbot/plugin-sdk";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";
@@ -33,7 +33,7 @@ function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } {
const sendMessageLine = vi.fn(async () => ({ messageId: "m-media", chatId: "c1" }));
const chunkMarkdownText = vi.fn((text: string) => [text]);
const resolveTextChunkLimit = vi.fn(() => 123);
const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: MoltbotConfig; accountId?: string }) => {
const resolveLineAccount = vi.fn(({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => {
const resolved = accountId ?? "default";
const lineConfig = (cfg.channels?.line ?? {}) as {
accounts?: Record<string, Record<string, unknown>>;
@@ -90,7 +90,7 @@ describe("linePlugin outbound.sendPayload", () => {
it("sends flex message without dropping text", async () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg = { channels: { line: {} } } as MoltbotConfig;
const cfg = { channels: { line: {} } } as OpenClawConfig;
const payload = {
text: "Now playing:",
@@ -121,7 +121,7 @@ describe("linePlugin outbound.sendPayload", () => {
it("sends template message without dropping text", async () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg = { channels: { line: {} } } as MoltbotConfig;
const cfg = { channels: { line: {} } } as OpenClawConfig;
const payload = {
text: "Choose one:",
@@ -157,7 +157,7 @@ describe("linePlugin outbound.sendPayload", () => {
it("attaches quick replies when no text chunks are present", async () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg = { channels: { line: {} } } as MoltbotConfig;
const cfg = { channels: { line: {} } } as OpenClawConfig;
const payload = {
channelData: {
@@ -197,7 +197,7 @@ describe("linePlugin outbound.sendPayload", () => {
it("sends media before quick-reply text so buttons stay visible", async () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg = { channels: { line: {} } } as MoltbotConfig;
const cfg = { channels: { line: {} } } as OpenClawConfig;
const payload = {
text: "Hello",
@@ -235,7 +235,7 @@ describe("linePlugin outbound.sendPayload", () => {
it("uses configured text chunk limit for payloads", async () => {
const { runtime, mocks } = createRuntime();
setLineRuntime(runtime);
const cfg = { channels: { line: { textChunkLimit: 123 } } } as MoltbotConfig;
const cfg = { channels: { line: { textChunkLimit: 123 } } } as OpenClawConfig;
const payload = {
text: "Hello world",
@@ -295,7 +295,7 @@ describe("linePlugin groups.resolveRequireMention", () => {
},
},
},
} as MoltbotConfig;
} as OpenClawConfig;
const requireMention = linePlugin.groups.resolveRequireMention({
cfg,
+5 -5
View File
@@ -4,11 +4,11 @@ import {
LineConfigSchema,
processLineMessage,
type ChannelPlugin,
type MoltbotConfig,
type OpenClawConfig,
type LineConfig,
type LineChannelData,
type ResolvedLineAccount,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { getLineRuntime } from "./runtime.js";
@@ -53,7 +53,7 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
if (!account.channelAccessToken) {
throw new Error("LINE channel access token not configured");
}
await line.pushMessageLine(id, "Moltbot: your access has been approved.", {
await line.pushMessageLine(id, "OpenClaw: your access has been approved.", {
channelAccessToken: account.channelAccessToken,
});
},
@@ -164,7 +164,7 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
allowFrom: account.config.allowFrom ?? [],
policyPath: `${basePath}dmPolicy`,
allowFromPath: basePath,
approveHint: "moltbot pairing approve line <code>",
approveHint: "openclaw pairing approve line <code>",
normalizeEntry: (raw) => raw.replace(/^line:(?:user:)?/i, ""),
};
},
@@ -639,7 +639,7 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = {
},
logoutAccount: async ({ accountId, cfg }) => {
const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() ?? "";
const nextCfg = { ...cfg } as MoltbotConfig;
const nextCfg = { ...cfg } as OpenClawConfig;
const lineConfig = (cfg.channels?.line ?? {}) as LineConfig;
const nextLine = { ...lineConfig };
let cleared = false;
+1 -1
View File
@@ -1,4 +1,4 @@
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
let runtime: PluginRuntime | null = null;
+4 -4
View File
@@ -4,7 +4,7 @@ Adds an **optional** agent tool `llm-task` for running **JSON-only** LLM tasks
(drafting, summarizing, classifying) with optional JSON Schema validation.
Designed to be called from workflow engines (for example, Lobster via
`clawd.invoke --each`) without adding new Clawdbot code per workflow.
`openclaw.invoke --each`) without adding new OpenClaw code per workflow.
## Enable
@@ -89,9 +89,9 @@ Returns `details.json` containing the parsed JSON (and validates against
## Bundled extension note
This extension depends on Clawdbot internal modules (the embedded agent runner).
It is intended to ship as a **bundled** Clawdbot extension (like `lobster`) and
This extension depends on OpenClaw internal modules (the embedded agent runner).
It is intended to ship as a **bundled** OpenClaw extension (like `lobster`) and
be enabled via `plugins.entries` + tool allowlists.
It is **not** currently designed to be copied into
`~/.clawdbot/extensions` as a standalone plugin directory.
`~/.openclaw/extensions` as a standalone plugin directory.
+2 -2
View File
@@ -1,7 +1,7 @@
import type { MoltbotPluginApi } from "../../src/plugins/types.js";
import type { OpenClawPluginApi } from "../../src/plugins/types.js";
import { createLlmTaskTool } from "./src/llm-task-tool.js";
export default function register(api: MoltbotPluginApi) {
export default function register(api: OpenClawPluginApi) {
api.registerTool(createLlmTaskTool(api), { optional: true });
}
+4 -4
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/llm-task",
"version": "2026.1.29",
"name": "@openclaw/llm-task",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot JSON-only LLM task plugin",
"moltbot": {
"description": "OpenClaw JSON-only LLM task plugin",
"openclaw": {
"extensions": [
"./index.ts"
]
+6 -6
View File
@@ -5,12 +5,12 @@ import fs from "node:fs/promises";
import Ajv from "ajv";
import { Type } from "@sinclair/typebox";
// NOTE: This extension is intended to be bundled with Moltbot.
// When running from source (tests/dev), Moltbot internals live under src/.
// NOTE: This extension is intended to be bundled with OpenClaw.
// When running from source (tests/dev), OpenClaw internals live under src/.
// When running from a built install, internals live under dist/ (no src/ tree).
// So we resolve internal imports dynamically with src-first, dist-fallback.
import type { MoltbotPluginApi } from "../../../src/plugins/types.js";
import type { OpenClawPluginApi } from "../../../src/plugins/types.js";
type RunEmbeddedPiAgentFn = (params: Record<string, unknown>) => Promise<unknown>;
@@ -61,11 +61,11 @@ type PluginCfg = {
timeoutMs?: number;
};
export function createLlmTaskTool(api: MoltbotPluginApi) {
export function createLlmTaskTool(api: OpenClawPluginApi) {
return {
name: "llm-task",
description:
"Run a generic JSON-only LLM task and return schema-validated JSON. Designed for orchestration from Lobster workflows via clawd.invoke.",
"Run a generic JSON-only LLM task and return schema-validated JSON. Designed for orchestration from Lobster workflows via openclaw.invoke.",
parameters: Type.Object({
prompt: Type.String({ description: "Task instruction for the LLM." }),
input: Type.Optional(Type.Unknown({ description: "Optional input payload for the task." })),
@@ -154,7 +154,7 @@ export function createLlmTaskTool(api: MoltbotPluginApi) {
let tmpDir: string | null = null;
try {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-llm-task-"));
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-llm-task-"));
const sessionId = `llm-task-${Date.now()}`;
const sessionFile = path.join(tmpDir, "session.json");
+7 -7
View File
@@ -5,7 +5,7 @@ Adds the `lobster` agent tool as an **optional** plugin tool.
## What this is
- Lobster is a standalone workflow shell (typed JSON-first pipelines + approvals/resume).
- This plugin integrates Lobster with Clawdbot *without core changes*.
- This plugin integrates Lobster with OpenClaw *without core changes*.
## Enable
@@ -30,19 +30,19 @@ Enable it in an agent allowlist:
}
```
## Using `clawd.invoke` (Lobster → Clawdbot tools)
## Using `openclaw.invoke` (Lobster → OpenClaw tools)
Some Lobster pipelines may include a `clawd.invoke` step to call back into Clawdbot tools/plugins (for example: `gog` for Google Workspace, `gh` for GitHub, `message.send`, etc.).
Some Lobster pipelines may include a `openclaw.invoke` step to call back into OpenClaw tools/plugins (for example: `gog` for Google Workspace, `gh` for GitHub, `message.send`, etc.).
For this to work, the Clawdbot Gateway must expose the tool bridge endpoint and the target tool must be allowed by policy:
For this to work, the OpenClaw Gateway must expose the tool bridge endpoint and the target tool must be allowed by policy:
- Clawdbot provides an HTTP endpoint: `POST /tools/invoke`.
- OpenClaw provides an HTTP endpoint: `POST /tools/invoke`.
- The request is gated by **gateway auth** (e.g. `Authorization: Bearer …` when token auth is enabled).
- The invoked tool is gated by **tool policy** (global + per-agent + provider + group policy). If the tool is not allowed, Clawdbot returns `404 Tool not available`.
- The invoked tool is gated by **tool policy** (global + per-agent + provider + group policy). If the tool is not allowed, OpenClaw returns `404 Tool not available`.
### Allowlisting recommended
To avoid letting workflows call arbitrary tools, set a tight allowlist on the agent that will be used by `clawd.invoke`.
To avoid letting workflows call arbitrary tools, set a tight allowlist on the agent that will be used by `openclaw.invoke`.
Example (allow only a small set of tools):
+2 -2
View File
@@ -1,8 +1,8 @@
import type { MoltbotPluginApi } from "../../src/plugins/types.js";
import type { OpenClawPluginApi } from "../../src/plugins/types.js";
import { createLobsterTool } from "./src/lobster-tool.js";
export default function register(api: MoltbotPluginApi) {
export default function register(api: OpenClawPluginApi) {
api.registerTool(
(ctx) => {
if (ctx.sandboxed) return null;
+3 -3
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/lobster",
"version": "2026.1.29",
"name": "@openclaw/lobster",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
"moltbot": {
"openclaw": {
"extensions": [
"./index.ts"
]
+7 -7
View File
@@ -4,10 +4,10 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
import type { MoltbotPluginApi, MoltbotPluginToolContext } from "../../../src/plugins/types.js";
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../../../src/plugins/types.js";
import { createLobsterTool } from "./lobster-tool.js";
async function writeFakeLobsterScript(scriptBody: string, prefix = "moltbot-lobster-plugin-") {
async function writeFakeLobsterScript(scriptBody: string, prefix = "openclaw-lobster-plugin-") {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
const isWindows = process.platform === "win32";
@@ -33,7 +33,7 @@ async function writeFakeLobster(params: { payload: unknown }) {
return await writeFakeLobsterScript(scriptBody);
}
function fakeApi(): MoltbotPluginApi {
function fakeApi(): OpenClawPluginApi {
return {
id: "lobster",
name: "lobster",
@@ -52,7 +52,7 @@ function fakeApi(): MoltbotPluginApi {
};
}
function fakeCtx(overrides: Partial<MoltbotPluginToolContext> = {}): MoltbotPluginToolContext {
function fakeCtx(overrides: Partial<OpenClawPluginToolContext> = {}): OpenClawPluginToolContext {
return {
config: {} as any,
workspaceDir: "/tmp",
@@ -89,7 +89,7 @@ describe("lobster plugin tool", () => {
`const payload = ${JSON.stringify(payload)};\n` +
`console.log("noise before json");\n` +
`process.stdout.write(JSON.stringify(payload));\n`,
"moltbot-lobster-plugin-noisy-",
"openclaw-lobster-plugin-noisy-",
);
const tool = createLobsterTool(fakeApi());
@@ -117,7 +117,7 @@ describe("lobster plugin tool", () => {
it("rejects invalid JSON from lobster", async () => {
const { binPath } = await writeFakeLobsterScript(
`process.stdout.write("nope");\n`,
"moltbot-lobster-plugin-bad-",
"openclaw-lobster-plugin-bad-",
);
const tool = createLobsterTool(fakeApi());
@@ -132,7 +132,7 @@ describe("lobster plugin tool", () => {
it("can be gated off in sandboxed contexts", async () => {
const api = fakeApi();
const factoryTool = (ctx: MoltbotPluginToolContext) => {
const factoryTool = (ctx: OpenClawPluginToolContext) => {
if (ctx.sandboxed) return null;
return createLobsterTool(api);
};
+2 -2
View File
@@ -2,7 +2,7 @@ import { Type } from "@sinclair/typebox";
import { spawn } from "node:child_process";
import path from "node:path";
import type { MoltbotPluginApi } from "../../../src/plugins/types.js";
import type { OpenClawPluginApi } from "../../../src/plugins/types.js";
type LobsterEnvelope =
| {
@@ -168,7 +168,7 @@ function parseEnvelope(stdout: string): LobsterEnvelope {
throw new Error("lobster returned invalid JSON envelope");
}
export function createLobsterTool(api: MoltbotPluginApi) {
export function createLobsterTool(api: OpenClawPluginApi) {
return {
name: "lobster",
description:
+10 -10
View File
@@ -3,52 +3,52 @@
## 2026.1.29
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.23
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.22
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.21
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.20
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.17-1
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.17
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.16
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.15
### Changes
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
## 2026.1.14
### Features
- Version alignment with core Moltbot release numbers.
- Version alignment with core OpenClaw release numbers.
- Matrix channel plugin with homeserver + user ID auth (access token or password login with device name).
- Direct messages with pairing/allowlist/open/disabled policies and allowFrom support.
- Group/room controls: allowlist policy, per-room config, mention gating, auto-reply, per-room skills/system prompts.
+3 -3
View File
@@ -1,5 +1,5 @@
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import { matrixPlugin } from "./src/channel.js";
import { setMatrixRuntime } from "./src/runtime.js";
@@ -9,7 +9,7 @@ const plugin = {
name: "Matrix",
description: "Matrix channel plugin (matrix-js-sdk)",
configSchema: emptyPluginConfigSchema(),
register(api: MoltbotPluginApi) {
register(api: OpenClawPluginApi) {
setMatrixRuntime(api.runtime);
api.registerChannel({ plugin: matrixPlugin });
},
+6 -6
View File
@@ -1,9 +1,9 @@
{
"name": "@moltbot/matrix",
"version": "2026.1.29",
"name": "@openclaw/matrix",
"version": "2026.1.27-beta.1",
"type": "module",
"description": "Moltbot Matrix channel plugin",
"moltbot": {
"description": "OpenClaw Matrix channel plugin",
"openclaw": {
"extensions": [
"./index.ts"
],
@@ -18,7 +18,7 @@
"quickstartAllowFrom": true
},
"install": {
"npmSpec": "@moltbot/matrix",
"npmSpec": "@openclaw/matrix",
"localPath": "extensions/matrix",
"defaultChoice": "npm"
}
@@ -31,6 +31,6 @@
"zod": "^4.3.6"
},
"devDependencies": {
"moltbot": "workspace:*"
"openclaw": "workspace:*"
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
type ChannelMessageActionContext,
type ChannelMessageActionName,
type ChannelToolSend,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { resolveMatrixAccount } from "./matrix/accounts.js";
import { handleMatrixAction } from "./tool-actions.js";
import type { CoreConfig } from "./types.js";
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it } from "vitest";
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { CoreConfig } from "./types.js";
import { matrixPlugin } from "./channel.js";
+1 -1
View File
@@ -8,7 +8,7 @@ import {
PAIRING_APPROVED_MESSAGE,
setAccountEnabledInConfigSection,
type ChannelPlugin,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { matrixMessageActions } from "./actions.js";
import { MatrixConfigSchema } from "./config-schema.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import { MarkdownConfigSchema, ToolPolicySchema } from "clawdbot/plugin-sdk";
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk";
import { z } from "zod";
const allowFromEntry = z.union([z.string(), z.number()]);
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ChannelDirectoryEntry } from "clawdbot/plugin-sdk";
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk";
import { resolveMatrixAuth } from "./matrix/client.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ChannelGroupContext, GroupToolPolicyConfig } from "clawdbot/plugin-sdk";
import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk";
import { resolveMatrixRoomConfig } from "./matrix/monitor/rooms.js";
import type { CoreConfig } from "./types.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
import type { CoreConfig, MatrixConfig } from "../types.js";
import { resolveMatrixConfig } from "./client.js";
import { credentialsMatchConfig, loadMatrixCredentials } from "./credentials.js";
@@ -22,7 +22,7 @@ export async function resolveActionClient(
if (opts.client) return { client: opts.client, stopOnDone: false };
const active = getActiveMatrixClient();
if (active) return { client: active, stopOnDone: false };
const shouldShareClient = Boolean(process.env.CLAWDBOT_GATEWAY_PORT);
const shouldShareClient = Boolean(process.env.OPENCLAW_GATEWAY_PORT);
if (shouldShareClient) {
const client = await resolveSharedMatrixClient({
cfg: getMatrixRuntime().config.loadConfig() as CoreConfig,
@@ -125,7 +125,7 @@ export async function resolveMatrixAuth(params?: {
type: "m.login.password",
identifier: { type: "m.id.user", user: resolved.userId },
password: resolved.password,
initial_device_display_name: resolved.deviceName ?? "Moltbot Gateway",
initial_device_display_name: resolved.deviceName ?? "OpenClaw Gateway",
}),
});
+1 -1
View File
@@ -3,7 +3,7 @@ import path from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import type { RuntimeEnv } from "clawdbot/plugin-sdk";
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import { getMatrixRuntime } from "../runtime.js";
const MATRIX_SDK_PACKAGE = "@vector-im/matrix-bot-sdk";
@@ -1,4 +1,4 @@
import type { AllowlistMatch } from "clawdbot/plugin-sdk";
import type { AllowlistMatch } from "openclaw/plugin-sdk";
function normalizeAllowList(list?: Array<string | number>) {
return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);
@@ -1,7 +1,7 @@
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
import { AutojoinRoomsMixin } from "@vector-im/matrix-bot-sdk";
import type { RuntimeEnv } from "clawdbot/plugin-sdk";
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import type { CoreConfig } from "../../types.js";
import { getMatrixRuntime } from "../../runtime.js";
@@ -1,5 +1,5 @@
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { MatrixAuth } from "../client.js";
import type { MatrixRawEvent } from "./types.js";
@@ -8,7 +8,7 @@ import {
logTypingFailure,
resolveControlCommandGate,
type RuntimeEnv,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import type { CoreConfig, ReplyToMode } from "../../types.js";
import {
formatPollAsText,
@@ -37,7 +37,7 @@ export type MatrixMonitorHandlerParams = {
logging: {
shouldLogVerbose: () => boolean;
};
channel: typeof import("clawdbot/plugin-sdk")["channel"];
channel: typeof import("openclaw/plugin-sdk")["channel"];
system: {
enqueueSystemEvent: (
text: string,
@@ -59,7 +59,7 @@ export type MatrixMonitorHandlerParams = {
: Record<string, unknown> | undefined
: Record<string, unknown> | undefined;
mentionRegexes: ReturnType<
typeof import("clawdbot/plugin-sdk")["channel"]["mentions"]["buildMentionRegexes"]
typeof import("openclaw/plugin-sdk")["channel"]["mentions"]["buildMentionRegexes"]
>;
groupPolicy: "open" | "allowlist" | "disabled";
replyToMode: ReplyToMode;
@@ -252,12 +252,12 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
await sendMessageMatrix(
`room:${roomId}`,
[
"Moltbot: access not configured.",
"OpenClaw: access not configured.",
"",
`Pairing code: ${code}`,
"",
"Ask the bot owner to approve with:",
"moltbot pairing approve matrix <code>",
"openclaw pairing approve matrix <code>",
].join("\n"),
{ client },
);
@@ -4,7 +4,7 @@ import {
mergeAllowlist,
summarizeMapping,
type RuntimeEnv,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import type { CoreConfig, ReplyToMode } from "../../types.js";
import { setActiveMatrixClient } from "../active-client.js";
import {
@@ -4,7 +4,7 @@ import {
formatLocationText,
toLocationContext,
type NormalizedLocation,
} from "clawdbot/plugin-sdk";
} from "openclaw/plugin-sdk";
import { EventType } from "./types.js";
export type MatrixLocationPayload = {
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
import { setMatrixRuntime } from "../../runtime.js";
import { downloadMatrixMedia } from "./media.js";
@@ -1,6 +1,6 @@
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "clawdbot/plugin-sdk";
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk";
import { sendMessageMatrix } from "../send.js";
import { getMatrixRuntime } from "../../runtime.js";
@@ -1,5 +1,5 @@
import type { MatrixRoomConfig } from "../../types.js";
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "clawdbot/plugin-sdk";
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk";
export type MatrixRoomConfigResolved = {
allowed: boolean;
+1 -1
View File
@@ -7,7 +7,7 @@
* - m.poll.end - Closes a poll
*/
import type { PollInput } from "clawdbot/plugin-sdk";
import type { PollInput } from "openclaw/plugin-sdk";
export const M_POLL_START = "m.poll.start" as const;
export const M_POLL_RESPONSE = "m.poll.response" as const;
+1 -1
View File
@@ -1,6 +1,6 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginRuntime } from "clawdbot/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk";
import { setMatrixRuntime } from "../runtime.js";
vi.mock("@vector-im/matrix-bot-sdk", () => ({

Some files were not shown because too many files have changed in this diff Show More