refactor!: rename chat providers to channels

This commit is contained in:
Peter Steinberger
2026-01-13 06:16:43 +00:00
parent 0cd632ba84
commit 90342a4f3a
393 changed files with 8004 additions and 6737 deletions

View File

@@ -14,7 +14,7 @@ export type ResolvedIMessageAccount = {
};
function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] {
const accounts = cfg.imessage?.accounts;
const accounts = cfg.channels?.imessage?.accounts;
if (!accounts || typeof accounts !== "object") return [];
return Object.keys(accounts).filter(Boolean);
}
@@ -35,7 +35,7 @@ function resolveAccountConfig(
cfg: ClawdbotConfig,
accountId: string,
): IMessageAccountConfig | undefined {
const accounts = cfg.imessage?.accounts;
const accounts = cfg.channels?.imessage?.accounts;
if (!accounts || typeof accounts !== "object") return undefined;
return accounts[accountId] as IMessageAccountConfig | undefined;
}
@@ -44,7 +44,7 @@ function mergeIMessageAccountConfig(
cfg: ClawdbotConfig,
accountId: string,
): IMessageAccountConfig {
const { accounts: _ignored, ...base } = (cfg.imessage ??
const { accounts: _ignored, ...base } = (cfg.channels?.imessage ??
{}) as IMessageAccountConfig & { accounts?: unknown };
const account = resolveAccountConfig(cfg, accountId) ?? {};
return { ...base, ...account };
@@ -55,7 +55,7 @@ export function resolveIMessageAccount(params: {
accountId?: string | null;
}): ResolvedIMessageAccount {
const accountId = normalizeAccountId(params.accountId);
const baseEnabled = params.cfg.imessage?.enabled !== false;
const baseEnabled = params.cfg.channels?.imessage?.enabled !== false;
const merged = mergeIMessageAccountConfig(params.cfg, accountId);
const accountEnabled = merged.enabled !== false;
const configured = Boolean(

View File

@@ -33,9 +33,9 @@ vi.mock("./send.js", () => ({
}));
vi.mock("../pairing/pairing-store.js", () => ({
readProviderAllowFromStore: (...args: unknown[]) =>
readChannelAllowFromStore: (...args: unknown[]) =>
readAllowFromStoreMock(...args),
upsertProviderPairingRequest: (...args: unknown[]) =>
upsertChannelPairingRequest: (...args: unknown[]) =>
upsertPairingRequestMock(...args),
}));
@@ -72,10 +72,12 @@ async function waitForSubscribe() {
beforeEach(() => {
config = {
imessage: {
dmPolicy: "open",
allowFrom: ["*"],
groups: { "*": { requireMention: true } },
channels: {
imessage: {
dmPolicy: "open",
allowFrom: ["*"],
groups: { "*": { requireMention: true } },
},
},
session: { mainKey: "main" },
messages: {
@@ -129,9 +131,13 @@ describe("monitorIMessageProvider", () => {
it("allows group messages when imessage groups default disables mention gating", async () => {
config = {
...config,
imessage: {
groupPolicy: "open",
groups: { "*": { requireMention: false } },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
},
};
const run = monitorIMessageProvider();
@@ -162,9 +168,13 @@ describe("monitorIMessageProvider", () => {
config = {
...config,
messages: { groupChat: { mentionPatterns: [] } },
imessage: {
groupPolicy: "open",
groups: { "*": { requireMention: true } },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
},
};
const run = monitorIMessageProvider();
@@ -194,7 +204,13 @@ describe("monitorIMessageProvider", () => {
it("blocks group messages when imessage.groups is set without a wildcard", async () => {
config = {
...config,
imessage: { groups: { "99": { requireMention: false } } },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
groups: { "99": { requireMention: false } },
},
},
};
const run = monitorIMessageProvider();
await waitForSubscribe();
@@ -224,10 +240,14 @@ describe("monitorIMessageProvider", () => {
it("treats configured chat_id as a group session even when is_group is false", async () => {
config = {
...config,
imessage: {
dmPolicy: "open",
allowFrom: ["*"],
groups: { "2": { requireMention: false } },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
dmPolicy: "open",
allowFrom: ["*"],
groups: { "2": { requireMention: false } },
},
},
};
@@ -299,10 +319,14 @@ describe("monitorIMessageProvider", () => {
it("defaults to dmPolicy=pairing behavior when allowFrom is empty", async () => {
config = {
...config,
imessage: {
dmPolicy: "pairing",
allowFrom: [],
groups: { "*": { requireMention: true } },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
dmPolicy: "pairing",
allowFrom: [],
groups: { "*": { requireMention: true } },
},
},
};
const run = monitorIMessageProvider();
@@ -372,9 +396,13 @@ describe("monitorIMessageProvider", () => {
it("honors group allowlist when groupPolicy is allowlist", async () => {
config = {
...config,
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:101"],
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:101"],
},
},
};
const run = monitorIMessageProvider();
@@ -404,7 +432,13 @@ describe("monitorIMessageProvider", () => {
it("blocks group messages when groupPolicy is disabled", async () => {
config = {
...config,
imessage: { groupPolicy: "disabled" },
channels: {
...config.channels,
imessage: {
...config.channels?.imessage,
groupPolicy: "disabled",
},
},
};
const run = monitorIMessageProvider();
await waitForSubscribe();
@@ -455,7 +489,7 @@ describe("monitorIMessageProvider", () => {
expect(updateLastRouteMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "imessage",
channel: "imessage",
to: "chat_id:7",
}),
);

View File

@@ -21,16 +21,16 @@ import type { ReplyPayload } from "../auto-reply/types.js";
import type { ClawdbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js";
import {
resolveProviderGroupPolicy,
resolveProviderGroupRequireMention,
resolveChannelGroupPolicy,
resolveChannelGroupRequireMention,
} from "../config/group-policy.js";
import { resolveStorePath, updateLastRoute } from "../config/sessions.js";
import { danger, logVerbose, shouldLogVerbose } from "../globals.js";
import { mediaKindFromMime } from "../media/constants.js";
import { buildPairingReply } from "../pairing/pairing-messages.js";
import {
readProviderAllowFromStore,
upsertProviderPairingRequest,
readChannelAllowFromStore,
upsertChannelPairingRequest,
} from "../pairing/pairing-store.js";
import { resolveAgentRoute } from "../routing/resolve-route.js";
import type { RuntimeEnv } from "../runtime.js";
@@ -189,9 +189,9 @@ export async function monitorIMessageProvider(
const groupIdCandidate = chatId !== undefined ? String(chatId) : undefined;
const groupListPolicy = groupIdCandidate
? resolveProviderGroupPolicy({
? resolveChannelGroupPolicy({
cfg,
provider: "imessage",
channel: "imessage",
accountId: accountInfo.accountId,
groupId: groupIdCandidate,
})
@@ -216,7 +216,7 @@ export async function monitorIMessageProvider(
if (isGroup && !chatId) return;
const groupId = isGroup ? groupIdCandidate : undefined;
const storeAllowFrom = await readProviderAllowFromStore("imessage").catch(
const storeAllowFrom = await readChannelAllowFromStore("imessage").catch(
() => [],
);
const effectiveDmAllowFrom = Array.from(
@@ -282,8 +282,8 @@ export async function monitorIMessageProvider(
if (!dmAuthorized) {
if (dmPolicy === "pairing") {
const senderId = normalizeIMessageHandle(sender);
const { code, created } = await upsertProviderPairingRequest({
provider: "imessage",
const { code, created } = await upsertChannelPairingRequest({
channel: "imessage",
id: senderId,
meta: {
sender: senderId,
@@ -296,7 +296,7 @@ export async function monitorIMessageProvider(
await sendMessageIMessage(
sender,
buildPairingReply({
provider: "imessage",
channel: "imessage",
idLine: `Your iMessage sender id: ${senderId}`,
code,
}),
@@ -324,7 +324,7 @@ export async function monitorIMessageProvider(
const route = resolveAgentRoute({
cfg,
provider: "imessage",
channel: "imessage",
accountId: accountInfo.accountId,
peer: {
kind: isGroup ? "group" : "dm",
@@ -338,9 +338,9 @@ export async function monitorIMessageProvider(
const mentioned = isGroup
? matchesMentionPatterns(messageText, mentionRegexes)
: true;
const requireMention = resolveProviderGroupRequireMention({
const requireMention = resolveChannelGroupRequireMention({
cfg,
provider: "imessage",
channel: "imessage",
accountId: accountInfo.accountId,
groupId,
requireMentionOverride: opts.requireMention,
@@ -399,7 +399,7 @@ export async function monitorIMessageProvider(
? Date.parse(message.created_at)
: undefined;
const body = formatAgentEnvelope({
provider: "iMessage",
channel: "iMessage",
from: fromLabel,
timestamp: createdAt,
body: bodyText,
@@ -422,7 +422,7 @@ export async function monitorIMessageProvider(
currentMessage: combinedBody,
formatEntry: (entry) =>
formatAgentEnvelope({
provider: "iMessage",
channel: "iMessage",
from: fromLabel,
timestamp: entry.timestamp,
body: `${entry.sender}: ${entry.body}${
@@ -472,7 +472,7 @@ export async function monitorIMessageProvider(
await updateLastRoute({
storePath,
sessionKey: route.mainSessionKey,
provider: "imessage",
channel: "imessage",
to,
accountId: route.accountId,
});

View File

@@ -9,8 +9,8 @@ export type IMessageProbe = {
export async function probeIMessage(timeoutMs = 2000): Promise<IMessageProbe> {
const cfg = loadConfig();
const cliPath = cfg.imessage?.cliPath?.trim() || "imsg";
const dbPath = cfg.imessage?.dbPath?.trim();
const cliPath = cfg.channels?.imessage?.cliPath?.trim() || "imsg";
const dbPath = cfg.channels?.imessage?.dbPath?.trim();
const detected = await detectBinary(cliPath);
if (!detected) {
return { ok: false, error: `imsg not found (${cliPath})` };