{"version":3,"file":"index.cjs","names":[],"sources":["../src/rocketchat/ddp/ws_transport.ts","../src/rocketchat/ddp/auth.ts","../src/rocketchat/ddp/methods.ts","../src/rocketchat/ddp/protocol.ts","../src/rocketchat/ddp/subscriptions.ts","../src/rocketchat/client.ts","../src/rocketchat/security/revocation.ts","../src/rocketchat/security/revalidate.ts","../src/openclaw/runtime/account_runner.ts","../src/rocketchat/inbound/classify_chat_type.ts","../src/rocketchat/inbound/decode_message.ts","../src/rocketchat/policy/allowlist.ts","../src/rocketchat/policy/mentions.ts","../src/rocketchat/policy/policy.ts","../src/rocketchat/inbound/process_inbound.ts","../src/rocketchat/config/defaults.ts","../src/rocketchat/config/resolve_account.ts","../src/channel.ts","../src/rocketchat/ddp/fake_transport.ts","../src/rocketchat/inbound/session_keys.ts","../src/rocketchat/rest/post_message.ts","../src/rocketchat/rest/react.ts","../src/rocketchat/rest/me.ts","../src/rocketchat/commands/parse.ts","../src/rocketchat/commands/dispatch.ts","../src/index.ts"],"sourcesContent":["import type { DdpTransport } from './transport'\n\ntype CloseInfo = { code?: number; reason?: string }\n\n// Real WebSocket-backed DDP transport for Node runtimes.\n//\n// Rocket.Chat DDP uses JSON frames over WebSocket at /websocket.\nexport class WebSocketDdpTransport implements DdpTransport {\n  private readonly ws: WebSocket\n  private messageHandlers: Array<(msg: unknown) => void> = []\n  private closeHandlers: Array<(info?: CloseInfo) => void> = []\n  private closed = false\n  private open = false\n  private readonly pendingSends: string[] = []\n\n  constructor(url: string) {\n    // Node 22+ provides global WebSocket (undici). We rely on OpenClaw's Node.\n    // If this ever runs in an environment without WebSocket, it will throw\n    // immediately, which is preferable to a silent no-op transport.\n    this.ws = new WebSocket(url)\n\n    // DDP sends frames immediately after creating the socket. Undici's WebSocket\n    // throws if you call send() before the connection is open.\n    this.ws.addEventListener('open', () => {\n      this.open = true\n      while (this.pendingSends.length > 0 && !this.closed) {\n        const payload = this.pendingSends.shift()\n        if (payload === undefined) break\n        this.ws.send(payload)\n      }\n    })\n\n    this.ws.addEventListener('message', (evt) => {\n      // evt.data is typically a string for JSON frames.\n      const raw = (evt as MessageEvent).data\n      if (typeof raw !== 'string') return\n\n      try {\n        const msg = JSON.parse(raw)\n        for (const h of this.messageHandlers) h(msg)\n      } catch {\n        // Ignore malformed frames; DDP protocol should be JSON.\n      }\n    })\n\n    this.ws.addEventListener('close', (evt) => {\n      this.closed = true\n      const info: CloseInfo = {\n        code: (evt as CloseEvent).code,\n        reason: (evt as CloseEvent).reason,\n      }\n      for (const h of this.closeHandlers) h(info)\n    })\n\n    this.ws.addEventListener('error', (evt) => {\n      // Provide a reason to consumers even if the implementation does not\n      // follow with a close event.\n      const msg = (evt as any)?.message\n      for (const h of this.closeHandlers) {\n        h({ code: 1011, reason: typeof msg === 'string' ? msg : 'websocket error' })\n      }\n    })\n  }\n\n  onMessage(cb: (msg: unknown) => void): void {\n    this.messageHandlers.push(cb)\n  }\n\n  onClose(cb: (info?: CloseInfo) => void): void {\n    this.closeHandlers.push(cb)\n  }\n\n  send(msg: unknown): void {\n    if (this.closed) return\n    const payload = JSON.stringify(msg)\n\n    if (!this.open) {\n      // Prevent unbounded growth if the socket never opens.\n      if (this.pendingSends.length >= 1000) {\n        this.close({ code: 1011, reason: 'send buffer overflow before open' })\n        return\n      }\n      this.pendingSends.push(payload)\n      return\n    }\n\n    this.ws.send(payload)\n  }\n\n  close(info?: CloseInfo): void {\n    if (this.closed) return\n    this.closed = true\n    this.ws.close(info?.code ?? 1000, info?.reason)\n  }\n}\n","import type { DdpMethodClient } from './methods'\n\n/**\n * Log in using a resume token.\n *\n * In v1 we treat the Rocket.Chat PAT as the resume token for DDP login.\n */\nexport async function ddpLoginWithResume(params: {\n  ddp: DdpMethodClient\n  resumeToken: string\n}): Promise<unknown> {\n  return await params.ddp.call('login', [{ resume: params.resumeToken }])\n}\n","import type { DdpTransport } from './transport'\n\nexport interface DdpMethodFrame {\n  msg: 'method'\n  method: string\n  id: string\n  params?: unknown[]\n}\n\nexport interface DdpResultFrame {\n  msg: 'result'\n  id: string\n  result?: unknown\n  error?: unknown\n}\n\nfunction isResultFrame(msg: unknown): msg is DdpResultFrame {\n  if (!msg || typeof msg !== 'object') return false\n  const m = msg as Record<string, unknown>\n  return m.msg === 'result' && typeof m.id === 'string'\n}\n\n/**\n * Correlates DDP method calls with result frames.\n */\nexport class DdpMethodClient {\n  private nextId = 1\n  private pending = new Map<\n    string,\n    { resolve: (value: unknown) => void; reject: (err: unknown) => void }\n  >()\n\n  constructor(private readonly transport: DdpTransport) {\n    this.transport.onMessage((msg) => {\n      if (!isResultFrame(msg)) return\n      const handler = this.pending.get(msg.id)\n      if (!handler) return\n      this.pending.delete(msg.id)\n\n      if (msg.error !== undefined) {\n        handler.reject(msg.error)\n        return\n      }\n      handler.resolve(msg.result)\n    })\n\n    this.transport.onClose((info) => {\n      // Fail all pending calls on close.\n      const err = new Error(\n        `DDP transport closed${info?.code ? ` (code=${info.code})` : ''}${\n          info?.reason ? ` reason=${info.reason}` : ''\n        }`\n      )\n      for (const [, handler] of this.pending) handler.reject(err)\n      this.pending.clear()\n    })\n  }\n\n  /**\n   * Call a DDP method.\n   */\n  call(method: string, params?: unknown[]): Promise<unknown> {\n    const id = String(this.nextId++)\n    const frame: DdpMethodFrame = { msg: 'method', method, id }\n    if (params) frame.params = params\n\n    const promise = new Promise<unknown>((resolve, reject) => {\n      this.pending.set(id, { resolve, reject })\n    })\n\n    this.transport.send(frame)\n    return promise\n  }\n}\n","import type { DdpTransport } from './transport'\n\n/** DDP connect frame. */\nexport interface DdpConnectFrame {\n  msg: 'connect'\n  version: '1'\n  support: ['1']\n}\n\n/** DDP ping frame. */\nexport interface DdpPingFrame {\n  msg: 'ping'\n}\n\n/** DDP pong frame. */\nexport interface DdpPongFrame {\n  msg: 'pong'\n}\n\n/**\n * Create the initial DDP connect frame.\n */\nexport function makeConnectFrame(): DdpConnectFrame {\n  return { msg: 'connect', version: '1', support: ['1'] }\n}\n\n/**\n * Install baseline protocol handlers.\n *\n * Responsibilities:\n * - send initial connect\n * - respond to ping with pong\n */\nexport function installDdpProtocol(params: { transport: DdpTransport }): {\n  sendConnect: () => void\n} {\n  params.transport.onMessage((msg) => {\n    const m = msg as Partial<DdpPingFrame> | null\n    if (m && m.msg === 'ping') {\n      const pong: DdpPongFrame = { msg: 'pong' }\n      params.transport.send(pong)\n    }\n  })\n\n  return {\n    sendConnect: () => {\n      params.transport.send(makeConnectFrame())\n    },\n  }\n}\n","import type { DdpTransport } from './transport'\n\nexport interface DdpSubFrame {\n  msg: 'sub'\n  id: string\n  name: string\n  params: unknown[]\n}\n\nexport interface DdpReadyFrame {\n  msg: 'ready'\n  subs: string[]\n}\n\nfunction isReadyFrame(msg: unknown): msg is DdpReadyFrame {\n  if (!msg || typeof msg !== 'object') return false\n  const m = msg as Record<string, unknown>\n  return m.msg === 'ready' && Array.isArray(m.subs)\n}\n\n/**\n * Minimal subscription client.\n */\nexport class DdpSubscriptionClient {\n  private nextId = 1\n\n  constructor(private readonly transport: DdpTransport) {}\n\n  /**\n   * Subscribe to a DDP stream.\n   *\n   * Resolves once the server acknowledges the subscription via a `ready` frame.\n   */\n  subscribe(name: string, params: unknown[]): Promise<{ subId: string }> {\n    const subId = String(this.nextId++)\n    const frame: DdpSubFrame = { msg: 'sub', id: subId, name, params }\n\n    const promise = new Promise<{ subId: string }>((resolve) => {\n      const handler = (msg: unknown) => {\n        if (!isReadyFrame(msg)) return\n        if (!msg.subs.includes(subId)) return\n        resolve({ subId })\n      }\n      this.transport.onMessage(handler)\n    })\n\n    this.transport.send(frame)\n    return promise\n  }\n\n  /**\n   * Subscribe to the stream of room messages.\n   */\n  subscribeRoomMessages(roomId: string): Promise<{ subId: string }> {\n    return this.subscribe('stream-room-messages', [roomId, false])\n  }\n}\n","import { ddpLoginWithResume } from './ddp/auth'\nimport { DdpMethodClient } from './ddp/methods'\nimport { installDdpProtocol } from './ddp/protocol'\nimport { DdpSubscriptionClient } from './ddp/subscriptions'\nimport type { DdpTransport } from './ddp/transport'\n\n/**\n * Minimal Rocket.Chat client used by the OpenClaw channel plugin.\n *\n * v1 scope:\n * - DDP connect + ping/pong\n * - DDP login using resume token (PAT)\n * - subscribe to stream-room-messages\n *\n * This class is intentionally transport-agnostic so it can be unit-tested.\n */\nexport class RocketChatClient {\n  private readonly ddp: DdpMethodClient\n  private readonly subs: DdpSubscriptionClient\n  private readonly protocol: ReturnType<typeof installDdpProtocol>\n\n  constructor(transport: DdpTransport) {\n    this.ddp = new DdpMethodClient(transport)\n    this.subs = new DdpSubscriptionClient(transport)\n    this.protocol = installDdpProtocol({ transport })\n  }\n\n  /**\n   * Start the DDP session by sending the connect frame.\n   */\n  connect(): void {\n    this.protocol.sendConnect()\n  }\n\n  /**\n   * Authenticate the DDP session.\n   */\n  async loginWithResume(resumeToken: string): Promise<void> {\n    await ddpLoginWithResume({ ddp: this.ddp, resumeToken })\n\n    // Best-effort presence: Rocket.Chat may not show the user as online unless\n    // the client explicitly sets a presence status.\n    //\n    // Do not await this: a presence method mismatch should never block the\n    // channel from subscribing/processing messages.\n    void this.setPresenceOnline()\n  }\n\n  /** Send a message to a room via DDP. */\n  async sendMessage(params: { roomId: string; text: string }): Promise<unknown> {\n    return await this.ddp.call('sendMessage', [{ rid: params.roomId, msg: params.text }])\n  }\n\n  /**\n   * Best-effort presence update.\n   *\n   * Rocket.Chat's internal method names can vary across versions; we try a few\n   * known variants and treat failures as non-fatal.\n   */\n  private async setPresenceOnline(): Promise<void> {\n    const candidates: Array<{ method: string; params: unknown[] }> = [\n      // Common in Rocket.Chat Meteor apps.\n      { method: 'UserPresence:setDefaultStatus', params: ['online'] },\n      { method: 'UserPresence:setStatus', params: ['online'] },\n      // Some builds accept an object.\n      { method: 'UserPresence:setStatus', params: [{ status: 'online' }] },\n    ]\n\n    const callWithTimeout = async (method: string, params: unknown[]): Promise<void> => {\n      await Promise.race([\n        this.ddp.call(method, params).then(() => undefined),\n        new Promise<void>((_, reject) =>\n          setTimeout(() => reject(new Error('presence-timeout')), 2000)\n        ),\n      ])\n    }\n\n    let lastErr: unknown = null\n    for (const c of candidates) {\n      try {\n        await callWithTimeout(c.method, c.params)\n        return\n      } catch (err) {\n        lastErr = err\n      }\n    }\n\n    // Swallow failures: presence is optional.\n    void lastErr\n  }\n\n  /**\n   * Subscribe to inbound room message streams for the provided rooms.\n   */\n  async subscribeRoomMessages(roomIds: string[]): Promise<void> {\n    for (const roomId of roomIds) {\n      await this.subs.subscribeRoomMessages(roomId)\n    }\n  }\n\n  /**\n   * Convenience helper for the common connect/login/subscribe sequence.\n   */\n  async start(params: { resumeToken: string; roomIds: string[] }): Promise<void> {\n    this.connect()\n    await this.loginWithResume(params.resumeToken)\n    await this.subscribeRoomMessages(params.roomIds)\n  }\n}\n","import type { DdpTransport } from '../ddp/transport'\n\n/**\n * Close the transport immediately when we determine the account is unauthorized.\n *\n * v1 spec requirement:\n * - On any auth error/unauthorized response on the WebSocket (or any REST call),\n *   the plugin MUST immediately close the WebSocket and stop processing events\n *   for that account.\n */\nexport function revokeOnUnauthorized(params: { transport: DdpTransport; reason: string }): void {\n  params.transport.close({ reason: `unauthorized: ${params.reason}` })\n}\n","import { revokeOnUnauthorized } from './revocation'\nimport type { DdpTransport } from '../ddp/transport'\n\n/**\n * Options for periodic credential revalidation.\n */\nexport interface CredentialRevalidationOptions {\n  /** Revalidation cadence in milliseconds. */\n  intervalMs: number\n}\n\n/**\n * Start a periodic credential revalidation loop.\n *\n * This provides a bound on worst-case revocation latency for long-lived sessions.\n * v1 target: max 5 minutes.\n */\nexport function startCredentialRevalidation(params: {\n  transport: DdpTransport\n  validateOnce: () => Promise<{ ok: true } | { ok: false; reason: string }>\n  options: CredentialRevalidationOptions\n}): { stop: () => void } {\n  let stopped = false\n  const timer = setInterval(async () => {\n    if (stopped) return\n\n    try {\n      const result = await params.validateOnce()\n      if (!result.ok) {\n        revokeOnUnauthorized({ transport: params.transport, reason: result.reason })\n      }\n    } catch (err) {\n      const reason = err instanceof Error ? err.message : String(err)\n      revokeOnUnauthorized({ transport: params.transport, reason })\n    }\n  }, params.options.intervalMs)\n\n  return {\n    stop: () => {\n      stopped = true\n      clearInterval(timer)\n    },\n  }\n}\n","import { RocketChatClient } from '../../rocketchat/client'\nimport { startCredentialRevalidation } from '../../rocketchat/security/revalidate'\nimport type { DdpTransport } from '../../rocketchat/ddp/transport'\nimport type { ResolvedRocketChatAccount } from '../../rocketchat/config/resolve_account'\n\n/**\n * Factory for creating a transport per account.\n */\nexport type RocketChatTransportFactory = (params: {\n  account: ResolvedRocketChatAccount\n}) => DdpTransport\n\n/**\n * Start (connect/login/subscribe) a Rocket.Chat client for a single account.\n *\n * v1 security requirements:\n * - periodic credential revalidation to bound revocation latency\n */\nexport async function startRocketChatAccount(params: {\n  account: ResolvedRocketChatAccount\n  roomIds: string[]\n  createTransport: RocketChatTransportFactory\n\n  /** Override credential revalidation cadence. Default is 5 minutes. */\n  revalidateIntervalMs?: number\n\n  /** Optional fetch injection for credential validation. */\n  fetcher?: (input: string, init?: RequestInit) => Promise<Response>\n}): Promise<{\n  client: RocketChatClient\n  transport: DdpTransport\n  stop: () => void\n}> {\n  const transport = params.createTransport({ account: params.account })\n  const client = new RocketChatClient(transport)\n\n  await client.start({ resumeToken: params.account.pat, roomIds: params.roomIds })\n\n  const revalidate = startCredentialRevalidation({\n    transport,\n    options: { intervalMs: params.revalidateIntervalMs ?? 5 * 60 * 1000 },\n    validateOnce: async () => {\n      try {\n        // REST auth with PAT is not reliable across Rocket.Chat deployments.\n        // Revalidate by attempting a DDP resume login again. If it fails, assume\n        // the token was revoked and tear down the transport.\n        await client.loginWithResume(params.account.pat)\n        return { ok: true } as const\n      } catch (err) {\n        const reason = err instanceof Error ? err.message : String(err)\n        return { ok: false, reason } as const\n      }\n    },\n  })\n\n  return {\n    client,\n    transport,\n    stop: () => {\n      revalidate.stop()\n      transport.close({ reason: 'stopped' })\n    },\n  }\n}\n","import type { RocketChatChatType } from '../policy/chat_types'\n\n/**\n * Determine chat type for a room.\n *\n * v1: this is config-driven. The caller can supply an explicit room type map.\n */\nexport function classifyChatType(params: {\n  roomId: string\n  roomTypeById?: Record<string, RocketChatChatType>\n}): RocketChatChatType {\n  const hint = params.roomTypeById?.[params.roomId]\n  return hint ?? 'group'\n}\n","import type { InboundNormalizeResult } from './types'\nimport type { RocketChatSender } from '../policy/chat_types'\n\ntype RocketChatUser = {\n  _id?: string\n  username?: string\n}\n\ntype RocketChatMessage = {\n  _id?: string\n  rid?: string\n  msg?: string\n  u?: RocketChatUser\n  tmid?: string\n}\n\ntype DdpChangedFrame = {\n  msg?: string\n  collection?: string\n  fields?: {\n    eventName?: string\n    args?: unknown[]\n  }\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n  return Boolean(v) && typeof v === 'object'\n}\n\nfunction decodeSender(u: RocketChatUser | undefined): RocketChatSender {\n  return {\n    id: (u?._id ?? '').trim(),\n    username: u?.username?.trim() || undefined,\n  }\n}\n\nfunction decodeMessage(m: RocketChatMessage): {\n  roomId: string\n  messageId: string\n  text: string\n  sender: RocketChatSender\n  threadId?: string\n} | null {\n  const roomId = (m.rid ?? '').trim()\n  const messageId = (m._id ?? '').trim()\n  const text = (m.msg ?? '').toString()\n  const sender = decodeSender(m.u)\n  const threadId = m.tmid?.trim() || undefined\n\n  if (!roomId) return null\n  if (!messageId) return null\n  if (!sender.id) return null\n\n  return { roomId, messageId, text, sender, threadId }\n}\n\n/**\n * Decode a `stream-room-messages` event payload into a normalized message shape.\n *\n * We support a couple of common payload shapes:\n * - direct message object\n * - DDP changed envelope with fields.args[0] = message\n */\nexport function decodeRoomMessageEvent(raw: unknown): {\n  roomId: string\n  messageId: string\n  text: string\n  sender: RocketChatSender\n  threadId?: string\n} | null {\n  if (!isObject(raw)) return null\n\n  // Shape 1: raw is already a Rocket.Chat message document.\n  //\n  // Note: DDP envelopes also have a `msg` field (e.g. msg=\"changed\"), so we must not\n  // treat the presence of `msg` alone as evidence this is a message document.\n  if ('rid' in raw || ('_id' in raw && 'u' in raw)) {\n    return decodeMessage(raw as RocketChatMessage)\n  }\n\n  // Shape 2: DDP changed frame.\n  const changed = raw as DdpChangedFrame\n  if (changed.msg !== 'changed') return null\n\n  // Some Rocket.Chat deployments expose this subscription name under the same\n  // `stream-room-messages` key used for subscribe(). For safety in early v1,\n  // accept any changed frame that includes args[0] shaped like a message.\n  //\n  // We still prefer strict matching when collection is present and known.\n  if (\n    changed.collection &&\n    changed.collection !== 'stream-room-messages' &&\n    changed.collection !== 'stream-room-messages-realtime'\n  ) {\n    return null\n  }\n\n  const args = changed.fields?.args\n  const first = Array.isArray(args) ? args[0] : undefined\n  if (!isObject(first)) return null\n\n  return decodeMessage(first as RocketChatMessage)\n}\n\n/**\n * Normalize and validate an inbound message event.\n */\nexport function normalizeDecodedMessage(raw: unknown): InboundNormalizeResult {\n  const decoded = decodeRoomMessageEvent(raw)\n  if (!decoded) return { ok: false, reason: 'unrecognized-message-shape' }\n  if (!decoded.roomId) return { ok: false, reason: 'missing-room-id' }\n  if (!decoded.messageId) return { ok: false, reason: 'missing-message-id' }\n  if (!decoded.sender.id) return { ok: false, reason: 'missing-sender-id' }\n  return { ok: true, value: { ...decoded, chatType: 'group' } as any }\n}\n","import type { RocketChatSender } from './chat_types'\n\n/**\n * Allowlist match result.\n */\nexport interface AllowlistMatch {\n  /** True when the sender matches the allowlist. */\n  allowed: boolean\n\n  /** Optional reason for denial (for logging/debugging). */\n  reason?: string\n}\n\nfunction normalizeEntry(raw: string): string {\n  return raw.trim().toLowerCase()\n}\n\nfunction normalizeSender(sender: RocketChatSender): string[] {\n  const entries: string[] = []\n  if (sender.id) entries.push(normalizeEntry(sender.id))\n  if (sender.username) entries.push(normalizeEntry(sender.username))\n  return entries\n}\n\n/**\n * Match a sender against an allowlist.\n *\n * Supported allowlist formats (v1):\n * - `\"*\"` wildcard\n * - raw user id\n * - raw username (without @)\n */\nexport function matchAllowlist(params: {\n  allowFrom: string[]\n  sender: RocketChatSender\n}): AllowlistMatch {\n  const allowFrom = params.allowFrom.map(normalizeEntry)\n  if (allowFrom.includes('*')) {\n    return { allowed: true }\n  }\n\n  const senderKeys = normalizeSender(params.sender)\n  if (senderKeys.length === 0) {\n    return { allowed: false, reason: 'missing-sender-id' }\n  }\n\n  const allowed = senderKeys.some((k) => allowFrom.includes(k))\n  return allowed ? { allowed: true } : { allowed: false, reason: 'not-allowlisted' }\n}\n","/**\n * Check whether a message includes a mention of the bot.\n *\n * v1 decision: implement this using a simple substring check (no regex).\n */\nexport function messageMentionsBot(params: { text: string; botUsername: string }): boolean {\n  const text = params.text\n  const botUsername = params.botUsername.trim().replace(/^@/, '')\n  if (!botUsername) return false\n  return text.includes(`@${botUsername}`)\n}\n","import { matchAllowlist } from './allowlist'\nimport { messageMentionsBot } from './mentions'\nimport type { RocketChatChatType, RocketChatSender } from './chat_types'\nimport type { ResolvedRocketChatAccount } from '../config/resolve_account'\n\n/**\n * The result of applying policy checks to an inbound message.\n */\nexport interface PolicyDecision {\n  allowed: boolean\n  reason?: string\n}\n\n/**\n * Decide whether an inbound message is authorized to be processed.\n *\n * This is the single top-level policy function for v1.\n */\nexport function authorizeInboundMessage(params: {\n  account: ResolvedRocketChatAccount\n  chatType: RocketChatChatType\n  roomId: string\n  sender: RocketChatSender\n  text: string\n}): PolicyDecision {\n  if (!params.account.enabled) {\n    return { allowed: false, reason: 'account-disabled' }\n  }\n\n  const baseAllowMatch = matchAllowlist({\n    allowFrom: params.account.allowFrom,\n    sender: params.sender,\n  })\n\n  if (params.chatType === 'direct') {\n    if (params.account.dmPolicy === 'disabled') {\n      return { allowed: false, reason: 'dm-disabled' }\n    }\n\n    if (params.account.dmPolicy === 'open') {\n      // open still requires explicit wildcard or direct allowlist match.\n      return baseAllowMatch.allowed\n        ? { allowed: true }\n        : { allowed: false, reason: baseAllowMatch.reason }\n    }\n\n    if (params.account.dmPolicy === 'allowlist') {\n      return baseAllowMatch.allowed\n        ? { allowed: true }\n        : { allowed: false, reason: baseAllowMatch.reason }\n    }\n\n    // pairing (v1): treat as allowlist until pairing flow is implemented.\n    return baseAllowMatch.allowed\n      ? { allowed: true }\n      : { allowed: false, reason: baseAllowMatch.reason ?? 'pairing-required' }\n  }\n\n  // group\n  if (params.account.groupPolicy === 'disabled') {\n    return { allowed: false, reason: 'group-disabled' }\n  }\n\n  const roomCfg = params.account.rooms[params.roomId]\n  const requireMention = roomCfg?.requireMention ?? params.account.defaultRequireMention\n  if (requireMention) {\n    const mentioned = messageMentionsBot({\n      text: params.text,\n      botUsername: params.account.username,\n    })\n    if (!mentioned) {\n      return { allowed: false, reason: 'missing-mention' }\n    }\n  }\n\n  if (params.account.groupPolicy === 'allowlist') {\n    // Room must be allowlisted under account.rooms. Using the presence of a room key\n    // as the allowlist signal is consistent with Slack-style allowlist-by-id.\n    if (!roomCfg) {\n      return { allowed: false, reason: 'room-not-allowlisted' }\n    }\n  }\n\n  if (!baseAllowMatch.allowed) {\n    return { allowed: false, reason: baseAllowMatch.reason }\n  }\n\n  // Optional per-room user allowlist.\n  const roomUsers = roomCfg?.users ?? []\n  if (roomUsers.length > 0) {\n    const roomMatch = matchAllowlist({ allowFrom: roomUsers, sender: params.sender })\n    if (!roomMatch.allowed) {\n      return { allowed: false, reason: roomMatch.reason ?? 'room-user-not-allowlisted' }\n    }\n  }\n\n  return { allowed: true }\n}\n\n/**\n * Determine whether a prefix command is allowed.\n *\n * Risky commands (!model, !stop) require operator authorization.\n */\nexport function authorizeCommand(params: {\n  account: ResolvedRocketChatAccount\n  chatType: RocketChatChatType\n  sender: RocketChatSender\n  command: 'status' | 'model' | 'stop'\n}): PolicyDecision {\n  if (!params.account.enabled) {\n    return { allowed: false, reason: 'account-disabled' }\n  }\n\n  // Base sender must be allowlisted for all commands.\n  const baseAllowMatch = matchAllowlist({\n    allowFrom: params.account.allowFrom,\n    sender: params.sender,\n  })\n  if (!baseAllowMatch.allowed) {\n    return { allowed: false, reason: baseAllowMatch.reason }\n  }\n\n  if (params.command === 'status') {\n    return { allowed: true }\n  }\n\n  // Operator allowlist.\n  const operatorAllowFrom = params.account.commandAllowFrom\n\n  // Default deny for risky commands in group chats when unset/empty.\n  if (params.chatType === 'group' && operatorAllowFrom.length === 0) {\n    return { allowed: false, reason: 'operator-allowlist-empty' }\n  }\n\n  const operatorMatch = matchAllowlist({\n    allowFrom: operatorAllowFrom,\n    sender: params.sender,\n  })\n  return operatorMatch.allowed\n    ? { allowed: true }\n    : { allowed: false, reason: operatorMatch.reason ?? 'not-operator' }\n}\n","import { classifyChatType } from './classify_chat_type'\nimport { decodeRoomMessageEvent } from './decode_message'\nimport { authorizeInboundMessage } from '../policy/policy'\nimport type { ResolvedRocketChatAccount } from '../config/resolve_account'\nimport type { InboundNormalizeResult, RocketChatInboundMessage } from './types'\n\n/**\n * Normalize, classify, and authorize an inbound Rocket.Chat message event.\n */\nexport function processInboundRoomMessage(params: {\n  account: ResolvedRocketChatAccount\n  rawEvent: unknown\n  roomTypeById?: Record<string, 'direct' | 'group'>\n}): InboundNormalizeResult {\n  const decoded = decodeRoomMessageEvent(params.rawEvent)\n  if (!decoded) return { ok: false, reason: 'unrecognized-message-shape' }\n\n  // Loop prevention: ignore self-authored events.\n  if (decoded.sender.id === params.account.userId) {\n    return { ok: false, reason: 'self-message' }\n  }\n\n  const chatType = classifyChatType({\n    roomId: decoded.roomId,\n    roomTypeById: params.roomTypeById,\n  })\n\n  const decision = authorizeInboundMessage({\n    account: params.account,\n    chatType,\n    roomId: decoded.roomId,\n    sender: decoded.sender,\n    text: decoded.text,\n  })\n\n  if (!decision.allowed) {\n    return { ok: false, reason: decision.reason ?? 'blocked' }\n  }\n\n  const value: RocketChatInboundMessage = {\n    roomId: decoded.roomId,\n    messageId: decoded.messageId,\n    text: decoded.text,\n    sender: decoded.sender,\n    chatType,\n    threadId: decoded.threadId,\n  }\n\n  return { ok: true, value }\n}\n","import type { RocketChatChannelConfig, RocketChatDmPolicy, RocketChatGroupPolicy } from './types'\n\n/**\n * Default DM policy for Rocket.Chat.\n *\n * v1 default is `pairing` (Slack-aligned).\n */\nexport const DEFAULT_DM_POLICY: RocketChatDmPolicy = 'pairing'\n\n/**\n * Default group policy for Rocket.Chat.\n *\n * v1 default is `allowlist` (fail closed).\n */\nexport const DEFAULT_GROUP_POLICY: RocketChatGroupPolicy = 'allowlist'\n\n/**\n * Default setting for mention gating in group chats.\n */\nexport const DEFAULT_REQUIRE_MENTION = true\n\n/**\n * Compute the effective channel config.\n *\n * This applies defaults, but does not validate credentials.\n */\nexport function resolveChannelDefaults(\n  cfg: RocketChatChannelConfig | undefined\n): Required<Pick<RocketChatChannelConfig, 'dmPolicy' | 'groupPolicy'>> & RocketChatChannelConfig {\n  return {\n    ...cfg,\n    dmPolicy: cfg?.dmPolicy ?? DEFAULT_DM_POLICY,\n    groupPolicy: cfg?.groupPolicy ?? DEFAULT_GROUP_POLICY,\n  }\n}\n","import { DEFAULT_DM_POLICY, DEFAULT_GROUP_POLICY, DEFAULT_REQUIRE_MENTION } from './defaults'\nimport type {\n  RocketChatAccountConfig,\n  RocketChatChannelConfig,\n  RocketChatOpenClawConfig,\n  RocketChatRoomConfig,\n} from './types'\n\n/**\n * Resolved account config for a specific OpenClaw agent id.\n */\nexport interface ResolvedRocketChatAccount {\n  /** OpenClaw agent id / account id key. */\n  accountId: string\n\n  /** Whether this account is enabled after all merges. */\n  enabled: boolean\n\n  /** Rocket.Chat server base URL (https://...). */\n  serverUrl: string\n\n  /** Rocket.Chat bot user id. */\n  userId: string\n\n  /** Rocket.Chat bot username (for mention gating). */\n  username: string\n\n  /** Rocket.Chat personal access token. */\n  pat: string\n\n  /** Effective DM policy. */\n  dmPolicy: NonNullable<RocketChatChannelConfig['dmPolicy']>\n\n  /** Effective group policy. */\n  groupPolicy: NonNullable<RocketChatChannelConfig['groupPolicy']>\n\n  /** Effective sender allowlist. */\n  allowFrom: string[]\n\n  /** Effective operator allowlist for risky commands. */\n  commandAllowFrom: string[]\n\n  /** Effective per-room config. */\n  rooms: Record<string, RocketChatRoomConfig>\n\n  /** Default mention gating for group chats when room override is not present. */\n  defaultRequireMention: boolean\n}\n\nfunction normalizeAccountId(raw: string): string {\n  const trimmed = raw.trim()\n  if (!trimmed) {\n    throw new Error('rocketchat: accountId is required')\n  }\n  return trimmed\n}\n\nfunction normalizeHttpsUrl(raw: string, pathLabel: string): string {\n  const trimmed = raw.trim()\n  if (!trimmed) {\n    throw new Error(`rocketchat: ${pathLabel} is required`)\n  }\n  if (!trimmed.startsWith('https://')) {\n    throw new Error(`rocketchat: ${pathLabel} must start with https:// (got ${trimmed})`)\n  }\n  return trimmed.replace(/\\/$/, '')\n}\n\nfunction normalizeAllowlist(raw?: string[]): string[] {\n  return (raw ?? []).map((v) => v.trim()).filter((v) => v.length > 0)\n}\n\nfunction normalizeRoomConfig(raw: RocketChatRoomConfig | undefined): RocketChatRoomConfig {\n  if (!raw) return {}\n\n  // Accept legacy/typoed config key used in the wild.\n  const r = raw as RocketChatRoomConfig & { requiresMention?: boolean }\n  if (r.requireMention === undefined && r.requiresMention !== undefined) {\n    return { ...r, requireMention: r.requiresMention }\n  }\n\n  return raw\n}\n\nfunction mergeRooms(\n  base: Record<string, RocketChatRoomConfig> | undefined,\n  override: Record<string, RocketChatRoomConfig> | undefined\n): Record<string, RocketChatRoomConfig> {\n  const merged = {\n    ...(base ?? {}),\n    ...(override ?? {}),\n  }\n\n  for (const [roomId, cfg] of Object.entries(merged)) {\n    merged[roomId] = normalizeRoomConfig(cfg)\n  }\n\n  return merged\n}\n\n/**\n * Resolve the effective Rocket.Chat account configuration for a given agent id.\n *\n * Merging rules:\n * - Top-level `channels.rocketchat` is the base.\n * - `channels.rocketchat.accounts[accountId]` overrides base.\n *\n * Validation rules:\n * - If the merged account is enabled, it must have: serverUrl, userId, username, pat.\n */\nexport function resolveRocketChatAccount(params: {\n  cfg: RocketChatOpenClawConfig\n  accountId: string\n}): ResolvedRocketChatAccount {\n  const accountId = normalizeAccountId(params.accountId)\n  const channel: RocketChatChannelConfig | undefined = params.cfg.channels?.rocketchat\n\n  const baseEnabled = channel?.enabled !== false\n  const override: RocketChatAccountConfig | undefined = channel?.accounts?.[accountId]\n  const accountEnabled = override?.enabled !== false\n  const enabled = baseEnabled && accountEnabled\n\n  const serverUrlRaw = override?.serverUrl ?? channel?.serverUrl\n  const userIdRaw = override?.userId\n  const usernameRaw = override?.username\n  const patRaw = override?.pat\n\n  if (!enabled) {\n    // For disabled accounts we still return a fully-typed object, but with empty\n    // credential fields. Callers should branch on enabled.\n    return {\n      accountId,\n      enabled: false,\n      serverUrl: serverUrlRaw?.trim() || '',\n      userId: userIdRaw?.trim() || '',\n      username: usernameRaw?.trim() || '',\n      pat: patRaw?.trim() || '',\n      dmPolicy: override?.dmPolicy ?? channel?.dmPolicy ?? DEFAULT_DM_POLICY,\n      groupPolicy: override?.groupPolicy ?? channel?.groupPolicy ?? DEFAULT_GROUP_POLICY,\n      allowFrom: normalizeAllowlist(override?.allowFrom ?? channel?.allowFrom),\n      commandAllowFrom: normalizeAllowlist(override?.commandAllowFrom ?? channel?.commandAllowFrom),\n      rooms: mergeRooms(channel?.rooms, override?.rooms),\n      defaultRequireMention: DEFAULT_REQUIRE_MENTION,\n    }\n  }\n\n  const serverUrl = normalizeHttpsUrl(serverUrlRaw ?? '', 'serverUrl')\n  const userId = (userIdRaw ?? '').trim()\n  const username = (usernameRaw ?? '').trim()\n  const pat = (patRaw ?? '').trim()\n\n  if (!userId) throw new Error(`rocketchat: userId is required for ${accountId}`)\n  if (!username) throw new Error(`rocketchat: username is required for ${accountId}`)\n  if (!pat) throw new Error(`rocketchat: pat is required for ${accountId}`)\n\n  return {\n    accountId,\n    enabled: true,\n    serverUrl,\n    userId,\n    username,\n    pat,\n    dmPolicy: override?.dmPolicy ?? channel?.dmPolicy ?? DEFAULT_DM_POLICY,\n    groupPolicy: override?.groupPolicy ?? channel?.groupPolicy ?? DEFAULT_GROUP_POLICY,\n    allowFrom: normalizeAllowlist(override?.allowFrom ?? channel?.allowFrom),\n    commandAllowFrom: normalizeAllowlist(override?.commandAllowFrom ?? channel?.commandAllowFrom),\n    rooms: mergeRooms(channel?.rooms, override?.rooms),\n    defaultRequireMention: DEFAULT_REQUIRE_MENTION,\n  }\n}\n","import { WebSocketDdpTransport } from './rocketchat/ddp/ws_transport'\nimport { startRocketChatAccount } from './openclaw/runtime/account_runner'\nimport { processInboundRoomMessage } from './rocketchat/inbound/process_inbound'\nimport { createOptionalChannelSetupAdapter } from 'openclaw/plugin-sdk/channel-setup'\nimport { dispatchInboundMessageWithBufferedDispatcher } from 'openclaw/plugin-sdk/reply-runtime'\nimport {\n  createChannelPluginBase,\n  type ChannelPlugin,\n  type OpenClawConfig,\n} from 'openclaw/plugin-sdk/core'\nimport {\n  resolveRocketChatAccount,\n  type ResolvedRocketChatAccount,\n} from './rocketchat/config/resolve_account'\nimport type { MsgContext, ReplyPayload } from 'openclaw/plugin-sdk/reply-runtime'\n\nfunction getAccountsSection(cfg: OpenClawConfig): Record<string, unknown> {\n  const section = (cfg.channels as Record<string, any> | undefined)?.rocketchat\n  return (section?.accounts ?? {}) as Record<string, unknown>\n}\n\nfunction serverUrlToDdpWsUrl(serverUrl: string): string {\n  const base = serverUrl.replace(/\\/$/, '')\n  if (base.startsWith('https://')) return `wss://${base.slice('https://'.length)}/websocket`\n  if (base.startsWith('http://')) return `ws://${base.slice('http://'.length)}/websocket`\n  return `${base}/websocket`\n}\n\ntype RunningAccount = {\n  stop: () => void\n}\n\nconst runningByAccountId = new Map<string, RunningAccount>()\n\nasync function startAccount(ctx: any): Promise<void> {\n  const { accountId, account, abortSignal } = ctx as {\n    accountId: string\n    account: ResolvedRocketChatAccount\n    abortSignal?: AbortSignal\n    log?: { info: (m: string) => void; warn: (m: string) => void; error: (m: string) => void }\n  }\n\n  const log = ctx?.log\n\n  // Ensure we never leave a stale runner around.\n  const existing = runningByAccountId.get(accountId)\n  if (existing) {\n    existing.stop()\n    runningByAccountId.delete(accountId)\n  }\n\n  if (!account.enabled) {\n    log?.info?.(`rocketchat/${accountId}: account disabled; not starting`)\n    return\n  }\n\n  // Provide an explicit, actionable error instead of an unlogged crash.\n  if (typeof WebSocket === 'undefined') {\n    throw new Error('rocketchat: global WebSocket is not available in this runtime')\n  }\n\n  const roomIds = Object.keys(account.rooms ?? {})\n  log?.info?.(`rocketchat/${accountId}: starting DDP session (rooms=${roomIds.length})`)\n\n  // Deliverer used by OpenClaw's reply runtime.\n  // deliver is wired after login when we have a real REST auth token.\n  let deliver: ((payload: ReplyPayload) => Promise<void>) | null = null\n\n  try {\n    const transport = new WebSocketDdpTransport(serverUrlToDdpWsUrl(account.serverUrl))\n\n    const closePromise = new Promise<void>((resolve) => {\n      transport.onClose(() => resolve())\n    })\n\n    // Attach inbound processing before we start so we do not miss events.\n    transport.onMessage((rawEvent) => {\n      try {\n        const normalized = processInboundRoomMessage({\n          account,\n          rawEvent,\n        })\n\n        if (!normalized.ok) return\n\n        if (!ctx?.channelRuntime) {\n          log?.warn?.(`rocketchat/${accountId}: channelRuntime missing; dropping inbound message`)\n          return\n        }\n\n        const inbound = normalized.value\n\n        // Feed into OpenClaw reply runtime.\n        // NOTE: We keep ctx minimal and provider-native (room id as To).\n        const msgCtx: MsgContext = {\n          Body: inbound.text,\n          RawBody: inbound.text,\n          CommandBody: inbound.text,\n          BodyForCommands: inbound.text,\n          From: inbound.sender.id,\n          To: inbound.roomId,\n          ChatType: inbound.chatType,\n          SenderId: inbound.sender.id,\n          SenderUsername: inbound.sender.username,\n          SenderName: inbound.sender.username,\n          NativeChannelId: inbound.roomId,\n          OriginatingChannel: 'rocketchat',\n          OriginatingTo: inbound.roomId,\n          ExplicitDeliverRoute: true,\n          AccountId: accountId,\n          Surface: 'rocketchat',\n          Provider: 'rocketchat',\n        }\n\n        if (!deliver) {\n          log?.warn?.(`rocketchat/${accountId}: deliver not ready; dropping inbound message`)\n          return\n        }\n\n        void dispatchInboundMessageWithBufferedDispatcher({\n          ctx: msgCtx,\n          cfg: ctx.cfg,\n          dispatcherOptions: {\n            deliver: async (payload) => {\n              payload.channelData = {\n                ...(payload.channelData ?? {}),\n                rocketchatRoomId: inbound.roomId,\n              }\n              await deliver?.(payload)\n            },\n          },\n        })\n      } catch (err) {\n        log?.error?.(\n          `rocketchat/${accountId}: inbound handler error: ${err instanceof Error ? err.stack || err.message : String(err)}`\n        )\n      }\n    })\n\n    const started = await startRocketChatAccount({\n      account,\n      roomIds,\n      createTransport: () => transport,\n    })\n\n    // Now that the DDP session is live, enable outbound delivery.\n    deliver = async (payload: ReplyPayload): Promise<void> => {\n      const text = payload.text ?? ''\n      if (!text) return\n\n      const roomId = String((payload.channelData as any)?.rocketchatRoomId ?? '')\n      if (!roomId) return\n\n      await started.client.sendMessage({ roomId, text })\n    }\n\n    runningByAccountId.set(accountId, {\n      stop: started.stop,\n    })\n\n    log?.info?.(`rocketchat/${accountId}: started`)\n\n    const abortPromise =\n      abortSignal && !abortSignal.aborted\n        ? new Promise<void>((resolve) => {\n            abortSignal.addEventListener('abort', () => resolve(), { once: true })\n          })\n        : Promise.resolve()\n\n    await Promise.race([closePromise, abortPromise])\n\n    if (abortSignal?.aborted) {\n      started.stop()\n      await closePromise\n    }\n  } catch (err) {\n    const msg = err instanceof Error ? err.stack || err.message : String(err)\n    log?.error?.(`rocketchat/${accountId}: start failed: ${msg}`)\n    throw err\n  }\n}\n\nasync function stopAccount(ctx: any): Promise<void> {\n  const { accountId } = ctx as { accountId: string }\n  const existing = runningByAccountId.get(accountId)\n  if (!existing) return\n\n  existing.stop()\n  runningByAccountId.delete(accountId)\n}\n\nconst base = createChannelPluginBase<ResolvedRocketChatAccount>({\n  id: 'rocketchat',\n  setup: createOptionalChannelSetupAdapter({\n    channel: 'rocketchat',\n    label: 'Rocket.Chat',\n    npmSpec: '@nhtio/rocket-chat-openclaw-integration',\n  }),\n})\n\n// Channel plugin surface.\n//\n// v1 wiring:\n// - config adapter for health/status\n// - gateway adapter to start/stop the account runtime (DDP WebSocket login)\nexport const rocketChatPlugin: ChannelPlugin<ResolvedRocketChatAccount> = {\n  ...base,\n  capabilities: {\n    chatTypes: ['direct', 'group'],\n    reactions: true,\n    reply: true,\n  },\n\n  config: {\n    listAccountIds: (cfg) => {\n      const ids = Object.keys(getAccountsSection(cfg))\n      return ids.length > 0 ? ids : ['default']\n    },\n\n    resolveAccount: (cfg, accountId) =>\n      resolveRocketChatAccount({\n        cfg: cfg as any,\n        accountId: accountId ?? 'default',\n      }),\n\n    inspectAccount: (cfg, accountId) => {\n      try {\n        const account = resolveRocketChatAccount({\n          cfg: cfg as any,\n          accountId: accountId ?? 'default',\n        })\n\n        return {\n          enabled: account.enabled,\n          configured: Boolean(\n            account.serverUrl && account.userId && account.username && account.pat\n          ),\n        }\n      } catch (err) {\n        return {\n          enabled: false,\n          configured: false,\n          error: err instanceof Error ? err.message : String(err),\n        }\n      }\n    },\n\n    defaultAccountId: (cfg) => {\n      const ids = Object.keys(getAccountsSection(cfg))\n      return ids[0] ?? 'default'\n    },\n  },\n\n  gateway: {\n    startAccount,\n    stopAccount,\n  },\n}\n","import type { DdpTransport } from './transport'\n\n/**\n * A deterministic in-memory transport for unit testing DDP protocol logic.\n */\nexport class FakeDdpTransport implements DdpTransport {\n  private messageHandlers: Array<(msg: unknown) => void> = []\n  private closeHandlers: Array<(info?: { code?: number; reason?: string }) => void> = []\n\n  /** Outbound frames sent by the client. */\n  public readonly sent: unknown[] = []\n\n  /** Whether close() has been called. */\n  public closed = false\n\n  onMessage(cb: (msg: unknown) => void): void {\n    this.messageHandlers.push(cb)\n  }\n\n  onClose(cb: (info?: { code?: number; reason?: string }) => void): void {\n    this.closeHandlers.push(cb)\n  }\n\n  send(msg: unknown): void {\n    this.sent.push(msg)\n  }\n\n  close(info?: { code?: number; reason?: string }): void {\n    this.closed = true\n    for (const cb of this.closeHandlers) cb(info)\n  }\n\n  /**\n   * Inject an inbound message as if it arrived from Rocket.Chat.\n   */\n  inject(msg: unknown): void {\n    for (const cb of this.messageHandlers) cb(msg)\n  }\n}\n","/**\n * Rocket.Chat session key components.\n */\nexport interface RocketChatSessionConversation {\n  /** Base conversation id (room). */\n  baseConversationId: string\n\n  /** Optional thread id (when replying in a thread). */\n  threadId?: string\n}\n\n/**\n * Compute the canonical conversation ids for OpenClaw session grammar.\n */\nexport function computeSessionConversation(params: {\n  roomId: string\n  threadId?: string\n}): RocketChatSessionConversation {\n  const baseConversationId = params.roomId.trim()\n  const threadId = params.threadId?.trim() || undefined\n  return threadId ? { baseConversationId, threadId } : { baseConversationId }\n}\n","export interface RocketChatPostMessageParams {\n  /** Rocket.Chat base server URL, for example https://chat.example.com */\n  serverUrl: string\n\n  /** Rocket.Chat user id for auth. */\n  userId: string\n\n  /** Rocket.Chat personal access token for auth. */\n  authToken: string\n\n  /** Room id to post into. */\n  roomId: string\n\n  /** Message text. */\n  text: string\n\n  /** Optional thread message id (Rocket.Chat tmid). */\n  threadId?: string\n}\n\nexport interface RocketChatPostMessageRequest {\n  url: string\n  method: 'POST'\n  headers: Record<string, string>\n  body: string\n}\n\nfunction normalizeServerUrl(serverUrl: string): string {\n  const trimmed = serverUrl.trim().replace(/\\/$/, '')\n  if (!trimmed.startsWith('https://')) {\n    throw new Error(`rocketchat: serverUrl must start with https:// (got ${trimmed})`)\n  }\n  return trimmed\n}\n\n/**\n * Build a REST request for Rocket.Chat `chat.postMessage`.\n *\n * Reference: https://developer.rocket.chat/reference/api/rest-api/endpoints/messaging/chat-endpoints/postmessage\n */\nexport function buildPostMessageRequest(\n  params: RocketChatPostMessageParams\n): RocketChatPostMessageRequest {\n  const serverUrl = normalizeServerUrl(params.serverUrl)\n  const url = `${serverUrl}/api/v1/chat.postMessage`\n\n  const payload: Record<string, unknown> = {\n    rid: params.roomId,\n    msg: params.text,\n  }\n  if (params.threadId) {\n    payload.tmid = params.threadId\n  }\n\n  return {\n    url,\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-User-Id': params.userId,\n      'X-Auth-Token': params.authToken,\n    },\n    body: JSON.stringify(payload),\n  }\n}\n\n/**\n * Post a message using Rocket.Chat REST API.\n *\n * This function accepts an optional fetch implementation to keep unit tests deterministic.\n */\nexport async function postMessage(\n  params: RocketChatPostMessageParams & {\n    fetcher?: (input: string, init?: RequestInit) => Promise<Response>\n  }\n): Promise<unknown> {\n  const req = buildPostMessageRequest(params)\n  const fetcher = params.fetcher ?? fetch\n\n  const res = await fetcher(req.url, {\n    method: req.method,\n    headers: req.headers,\n    body: req.body,\n  })\n\n  const text = await res.text()\n  if (!res.ok) {\n    throw new Error(`rocketchat: chat.postMessage failed (${res.status}): ${text}`)\n  }\n\n  try {\n    return JSON.parse(text)\n  } catch {\n    return text\n  }\n}\n","export interface RocketChatReactParams {\n  /** Rocket.Chat base server URL, for example https://chat.example.com */\n  serverUrl: string\n\n  /** Rocket.Chat user id for auth. */\n  userId: string\n\n  /** Rocket.Chat personal access token for auth. */\n  authToken: string\n\n  /** Message id to react to. */\n  messageId: string\n\n  /** Emoji name (without surrounding colons), e.g. \"smile\". */\n  emoji: string\n\n  /** Whether to add or remove the reaction. */\n  shouldReact: boolean\n}\n\nexport interface RocketChatReactRequest {\n  url: string\n  method: 'POST'\n  headers: Record<string, string>\n  body: string\n}\n\nfunction normalizeServerUrl(serverUrl: string): string {\n  const trimmed = serverUrl.trim().replace(/\\/$/, '')\n  if (!trimmed.startsWith('https://')) {\n    throw new Error(`rocketchat: serverUrl must start with https:// (got ${trimmed})`)\n  }\n  return trimmed\n}\n\n/**\n * Build a REST request for Rocket.Chat `chat.react`.\n *\n * Reference: https://developer.rocket.chat/apidocs/react-to-message\n */\nexport function buildReactRequest(params: RocketChatReactParams): RocketChatReactRequest {\n  const serverUrl = normalizeServerUrl(params.serverUrl)\n  const url = `${serverUrl}/api/v1/chat.react`\n\n  return {\n    url,\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-User-Id': params.userId,\n      'X-Auth-Token': params.authToken,\n    },\n    body: JSON.stringify({\n      messageId: params.messageId,\n      emoji: params.emoji,\n      shouldReact: params.shouldReact,\n    }),\n  }\n}\n\n/**\n * Set or unset a reaction using Rocket.Chat REST API.\n */\nexport async function reactToMessage(\n  params: RocketChatReactParams & {\n    fetcher?: (input: string, init?: RequestInit) => Promise<Response>\n  }\n): Promise<unknown> {\n  const req = buildReactRequest(params)\n  const fetcher = params.fetcher ?? fetch\n\n  const res = await fetcher(req.url, {\n    method: req.method,\n    headers: req.headers,\n    body: req.body,\n  })\n\n  const text = await res.text()\n  if (!res.ok) {\n    throw new Error(`rocketchat: chat.react failed (${res.status}): ${text}`)\n  }\n\n  try {\n    return JSON.parse(text)\n  } catch {\n    return text\n  }\n}\n","export interface RocketChatMeParams {\n  serverUrl: string\n  userId: string\n  authToken: string\n}\n\nexport interface RocketChatMeRequest {\n  url: string\n  method: 'GET'\n  headers: Record<string, string>\n}\n\nfunction normalizeServerUrl(serverUrl: string): string {\n  const trimmed = serverUrl.trim().replace(/\\/$/, '')\n  if (!trimmed.startsWith('https://')) {\n    throw new Error(`rocketchat: serverUrl must start with https:// (got ${trimmed})`)\n  }\n  return trimmed\n}\n\n/**\n * Build a REST request for Rocket.Chat `GET /api/v1/me`.\n */\nexport function buildMeRequest(params: RocketChatMeParams): RocketChatMeRequest {\n  const serverUrl = normalizeServerUrl(params.serverUrl)\n  return {\n    url: `${serverUrl}/api/v1/me`,\n    method: 'GET',\n    headers: {\n      'X-User-Id': params.userId,\n      'X-Auth-Token': params.authToken,\n    },\n  }\n}\n\n/**\n * Validate current credentials by calling Rocket.Chat `GET /api/v1/me`.\n */\nexport async function getMe(\n  params: RocketChatMeParams & {\n    fetcher?: (input: string, init?: RequestInit) => Promise<Response>\n  }\n): Promise<unknown> {\n  const req = buildMeRequest(params)\n  const fetcher = params.fetcher ?? fetch\n\n  const res = await fetcher(req.url, {\n    method: req.method,\n    headers: req.headers,\n  })\n\n  const text = await res.text()\n  if (!res.ok) {\n    throw new Error(`rocketchat: /api/v1/me failed (${res.status}): ${text}`)\n  }\n\n  try {\n    return JSON.parse(text)\n  } catch {\n    return text\n  }\n}\n","export type RocketChatPrefixCommandName = 'status' | 'model' | 'stop'\n\nexport type RocketChatPrefixCommand =\n  | { kind: 'command'; name: 'status'; raw: string }\n  | { kind: 'command'; name: 'stop'; raw: string }\n  | { kind: 'command'; name: 'model'; raw: string; model: string }\n  | { kind: 'not-a-command' }\n\nfunction tokenize(text: string): string[] {\n  return text\n    .trim()\n    .split(/\\s+/)\n    .map((t) => t.trim())\n    .filter((t) => t.length > 0)\n}\n\n/**\n * Parse Rocket.Chat prefix commands.\n *\n * Rules:\n * - Only treat as command if the trimmed message begins with '!'.\n * - Commands supported: !status, !stop, !model {name}\n */\nexport function parsePrefixCommand(text: string): RocketChatPrefixCommand {\n  const trimmed = text.trim()\n  if (!trimmed.startsWith('!')) return { kind: 'not-a-command' }\n\n  const tokens = tokenize(trimmed.slice(1))\n  const name = (tokens[0] ?? '').toLowerCase()\n\n  if (name === 'status') {\n    return { kind: 'command', name: 'status', raw: trimmed }\n  }\n\n  if (name === 'stop') {\n    return { kind: 'command', name: 'stop', raw: trimmed }\n  }\n\n  if (name === 'model') {\n    const model = tokens.slice(1).join(' ').trim()\n    if (!model) return { kind: 'not-a-command' }\n    return { kind: 'command', name: 'model', raw: trimmed, model }\n  }\n\n  return { kind: 'not-a-command' }\n}\n","import { authorizeCommand } from '../policy/policy'\nimport type { RocketChatPrefixCommand } from './parse'\nimport type { ResolvedRocketChatAccount } from '../config/resolve_account'\nimport type { RocketChatChatType, RocketChatSender } from '../policy/chat_types'\n\n/**\n * Normalized command dispatch instruction.\n */\nexport type RocketChatCommandDispatch =\n  | { ok: true; kind: 'openclaw-command'; commandText: string }\n  | { ok: false; reason: string }\n\n/**\n * Map a parsed Rocket.Chat prefix command into an OpenClaw command message.\n */\nexport function dispatchPrefixCommand(params: {\n  account: ResolvedRocketChatAccount\n  chatType: RocketChatChatType\n  sender: RocketChatSender\n  parsed: RocketChatPrefixCommand\n}): RocketChatCommandDispatch {\n  if (params.parsed.kind !== 'command') {\n    return { ok: false, reason: 'not-a-command' }\n  }\n\n  const decision = authorizeCommand({\n    account: params.account,\n    chatType: params.chatType,\n    sender: params.sender,\n    command: params.parsed.name,\n  })\n\n  if (!decision.allowed) {\n    return { ok: false, reason: decision.reason ?? 'blocked' }\n  }\n\n  if (params.parsed.name === 'status') {\n    return { ok: true, kind: 'openclaw-command', commandText: '/status' }\n  }\n\n  if (params.parsed.name === 'stop') {\n    return { ok: true, kind: 'openclaw-command', commandText: '/stop' }\n  }\n\n  return {\n    ok: true,\n    kind: 'openclaw-command',\n    commandText: `/model ${params.parsed.model}`,\n  }\n}\n","/**\n * @module @nhtio/rocket-chat-openclaw-integration\n */\n\nimport { rocketChatPlugin } from './channel'\nimport { defineChannelPluginEntry } from 'openclaw/plugin-sdk/core'\n\nexport type {\n  RocketChatAccountConfig,\n  RocketChatChannelConfig,\n  RocketChatDmPolicy,\n  RocketChatGroupPolicy,\n  RocketChatOpenClawConfig,\n  RocketChatRoomConfig,\n} from './rocketchat/config/types'\n\nexport {\n  DEFAULT_DM_POLICY,\n  DEFAULT_GROUP_POLICY,\n  DEFAULT_REQUIRE_MENTION,\n  resolveChannelDefaults,\n} from './rocketchat/config/defaults'\n\nexport type { ResolvedRocketChatAccount } from './rocketchat/config/resolve_account'\nexport { resolveRocketChatAccount } from './rocketchat/config/resolve_account'\n\nexport type { RocketChatChatType, RocketChatSender } from './rocketchat/policy/chat_types'\nexport type { PolicyDecision } from './rocketchat/policy/policy'\nexport { authorizeCommand, authorizeInboundMessage } from './rocketchat/policy/policy'\n\nexport type { DdpTransport } from './rocketchat/ddp/transport'\nexport { FakeDdpTransport } from './rocketchat/ddp/fake_transport'\nexport type { DdpConnectFrame, DdpPingFrame, DdpPongFrame } from './rocketchat/ddp/protocol'\nexport { installDdpProtocol, makeConnectFrame } from './rocketchat/ddp/protocol'\nexport type { DdpMethodFrame, DdpResultFrame } from './rocketchat/ddp/methods'\nexport { DdpMethodClient } from './rocketchat/ddp/methods'\nexport { ddpLoginWithResume } from './rocketchat/ddp/auth'\nexport type { DdpSubFrame, DdpReadyFrame } from './rocketchat/ddp/subscriptions'\nexport { DdpSubscriptionClient } from './rocketchat/ddp/subscriptions'\n\nexport { RocketChatClient } from './rocketchat/client'\n\nexport type { InboundNormalizeResult, RocketChatInboundMessage } from './rocketchat/inbound/types'\nexport { decodeRoomMessageEvent } from './rocketchat/inbound/decode_message'\nexport { classifyChatType } from './rocketchat/inbound/classify_chat_type'\nexport type { RocketChatSessionConversation } from './rocketchat/inbound/session_keys'\nexport { computeSessionConversation } from './rocketchat/inbound/session_keys'\nexport { processInboundRoomMessage } from './rocketchat/inbound/process_inbound'\n\nexport type {\n  RocketChatPostMessageParams,\n  RocketChatPostMessageRequest,\n} from './rocketchat/rest/post_message'\nexport { buildPostMessageRequest, postMessage } from './rocketchat/rest/post_message'\n\nexport type { RocketChatReactParams, RocketChatReactRequest } from './rocketchat/rest/react'\nexport { buildReactRequest, reactToMessage } from './rocketchat/rest/react'\n\nexport type { RocketChatMeParams, RocketChatMeRequest } from './rocketchat/rest/me'\nexport { buildMeRequest, getMe } from './rocketchat/rest/me'\n\nexport type {\n  RocketChatPrefixCommand,\n  RocketChatPrefixCommandName,\n} from './rocketchat/commands/parse'\nexport { parsePrefixCommand } from './rocketchat/commands/parse'\n\nexport type { RocketChatCommandDispatch } from './rocketchat/commands/dispatch'\nexport { dispatchPrefixCommand } from './rocketchat/commands/dispatch'\n\nexport type { CredentialRevalidationOptions } from './rocketchat/security/revalidate'\nexport { startCredentialRevalidation } from './rocketchat/security/revalidate'\nexport { revokeOnUnauthorized } from './rocketchat/security/revocation'\n\nexport type { RocketChatTransportFactory } from './openclaw/runtime/account_runner'\nexport { startRocketChatAccount } from './openclaw/runtime/account_runner'\n\n/**\n * The current version of the package.\n *\n * @tip This constant is replaced during the build process with the actual version of the package.\n */\nexport const version: string = __VERSION__\n\n/**\n * OpenClaw Rocket.Chat channel plugin entrypoint.\n */\nexport default defineChannelPluginEntry({\n  id: 'rocketchat',\n  name: 'Rocket.Chat',\n  description: 'Rocket.Chat channel plugin',\n  plugin: rocketChatPlugin,\n})\n"],"mappings":"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"}