New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@cjhyy/code-shell-chat

Package Overview
Dependencies
Maintainers
1
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cjhyy/code-shell-chat

A standalone, transport-agnostic multi-channel chat gateway for Node.js and Bun.

latest
Source
npmnpm
Version
0.9.46
Version published
Weekly downloads
280
-86.08%
Maintainers
1
Weekly downloads
 
Created
Source

@cjhyy/code-shell-chat

可独立安装的 Node.js/Bun 多渠道 Chat Gateway。包本身不依赖 CodeShell runtime:第三方应用可以 直接组合 channel adapter、middleware、白名单和 webhook ingress,并使用自己的业务处理器。

独立使用

bun add @cjhyy/code-shell-chat
import { ChatGateway, createAllowlistMiddleware } from "@cjhyy/code-shell-chat";
import { TelegramAdapter } from "@cjhyy/code-shell-chat/telegram";

const telegram = new TelegramAdapter({ botToken: process.env.TELEGRAM_BOT_TOKEN! });
const chat = new ChatGateway({ adapters: [telegram] });

chat.use(
  createAllowlistMiddleware({
    telegram: { targetIds: [process.env.TELEGRAM_CHAT_ID!] },
  }),
);
chat.use(async ({ message, reply }) => {
  await reply({ text: `echo: ${message.text}` });
});

const shutdown = new AbortController();
process.once("SIGINT", () => shutdown.abort());
await chat.run(shutdown.signal);

第三方也可以实现 ChannelAdapter 接入新平台,或用 middleware 接自己的 Agent、Webhook、工单系统与自动化服务。

配置驱动的 host 应使用异步工厂;它只会动态加载实际选择的平台模块:

import { createChannelAdapterAsync } from "@cjhyy/code-shell-chat/factory";

const telegram = await createChannelAdapterAsync({
  channel: "telegram",
  botToken: process.env.TELEGRAM_BOT_TOKEN!,
  apiBaseUrl: "https://api.telegram.org",
  allowedTargetIds: [process.env.TELEGRAM_CHAT_ID!],
  allowedUserIds: [],
});

旧的同步 createChannelAdapter() 继续兼容,但返回的是首次 run、send 或 webhook 请求时 才加载具体平台的轻量代理。内置 CLI 和 Desktop 已使用异步入口,因此启动阶段会直接报告所选 adapter 的模块加载或构造错误。

当前各平台 SDK 仍是默认依赖:动态加载减少的是未启用平台的模块求值、副作用和启动开销, 不是安装体积。暂不改成 peer/optional dependency,是为了保证标准 bun add @cjhyy/code-shell-chat 和内置 CLI 安装后仍可直接启用任一支持渠道;后续若拆独立 adapter 包,需要同时提供安装检测和明确的按渠道安装指引。

CodeShell integration

包内置了可选的 @cjhyy/code-shell-chat/codeshell integration 和 code-shell-chat CLI。 在 CodeShell Desktop 中可直接进入「凭证 → Link → CodeShell Chat Gateway」查看全部支持渠道、 逐渠道连接状态和接入说明,一键打开可用平台的官方配置后台,启动/停止 gateway、编辑配置, 或扫码连接个人微信;卡片还会显示本次桌面运行中最近的收发消息。无需再单独打开终端运行 CLI。 钉钉渠道提供独立的结构化向导:填写应用凭据后可建立临时 Stream 连接,通过向机器人发送 测试消息自动发现 conversationId 和 senderStaffId,勾选后保存白名单并启动。Desktop 会把 Client Secret 放入 CodeShell 安全凭据库(系统支持时使用系统加密),config.json 只保留 Client ID 和白名单;旧版配置中的 明文 Secret 会在首次使用新向导保存时迁移。独立 CLI 继续支持配置文件或 CODE_SHELL_DINGTALK_CLIENT_SECRET 环境变量。 CLI 会把普通文字和附件转到桌面端的 Mimi Pet 长期会话。Mimi 先通过只读 Gateway 工具发现 本进程已启用的渠道及某一渠道的精确能力,需要回复时再用 GatewayReply 把结果送回原 IM 会话。 不同渠道继续共享 Mimi 的长期上下文,但每条入站消息会标明来源渠道,当前消息来源也会进入 Mimi 的运行时上下文,避免多渠道同时使用时无法分辨入口。 /open、/close、/status 仍作为确定性快捷入口;“帮我打开手机遥控”“关闭公网入口” 和“看看手机遥控状态”等明确自然语言也会触发相同操作。mobile remote 继续复用 tunnel + pairing + passcode,并把 10 分钟一次性配对入口作为按钮回到原会话。

富媒体纵向链路现在覆盖绝大多数内置渠道。附件在 sender/target 白名单通过后才按需下载, 受数量、单文件、总大小和超时限制;随后由 Electron main 校验并暂存到 Pet 会话的 .code-shell/attachments 目录。Mimi 仅在对应 IM 回合显式调用 GatewayReply 后,由宿主 校验并发送项目、no-repo 或用户 Downloads 目录中的本地图片、文件、音频或视频;任务 产物记录本身不会触发回传。

CLI 不直接拥有 cloudflared 或 mobile WebSocket host。Electron main 仍是生命周期唯一 owner,并通过 ~/.code-shell/im-gateway/desktop-control.json(0600)提供随机 bearer token 保护的 loopback control API。CLI 与 Desktop 使用同一个原子进程锁,不能同时消费平台更新。

支持的渠道

所有渠道可以在同一 gateway 进程中同时启用,每个渠道有独立的会话和用户白名单。

渠道官方接入方式公网 webhook
TelegramBot API getUpdates 长轮询不需要
DiscordGateway + REST不需要
SlackSocket Mode + Web API不需要
飞书 / Lark官方 SDK WebSocket 长连接不需要
钉钉Stream Mode不需要
个人微信腾讯 ClawBot iLink + 扫码登录不需要
企业微信智能机器人 WebSocket 长连接不需要
MatrixClient-Server API /sync不需要
MattermostWebSocket events + REST不需要
LINEMessaging API webhook需要,/webhooks/line
WhatsApp BusinessMeta Cloud API webhook需要,/webhooks/whatsapp
Microsoft TeamsBot Framework messaging endpoint需要,/webhooks/teams

Gateway 能力矩阵

下表描述的是 CodeShell 当前适配器已经实现并经过测试的能力,不代表上游平台 API 的全部潜在 功能。Gateway 通过 ChannelAdapter.capabilities 把同一份声明用于 QR、附件过滤、主动通知和 Pet 的两层工具;Pet 不再按渠道名猜测能力。

渠道入站附件出站文字/链接出站附件路由/实现限制
Telegram图片、文件、音频、视频文字、原生按钮图片、文件、音频、视频已知 chat 可回复或主动推送
Discord图片、文件、音频、视频文字、原生按钮图片、文件、音频、视频需 Attach Files;已知 channel
Slack图片、文件、音频、视频文字、原生按钮图片、文件、音频、视频需 files:read/files:write;已知 channel
飞书 / Lark图片、文件、音频、视频文字、原生按钮图片、文件、音频、视频需消息资源读取和图片/文件上传权限
钉钉—文字、ActionCard—Stream 临时 session webhook 不承载本地媒体
企业微信图片、文件、视频文字、Markdown 链接图片、文件、音频、视频入站语音当前协议只提供转写;出站音频按文件发送
个人微信图片、文件、音频、视频文字、普通链接图片、文件、音频、视频音频按可播放文件发送;依赖 context_token
Matrix图片、文件、音频、视频文字、HTML 链接图片、文件、音频、视频当前媒体管线不解密 E2EE 附件
Mattermost图片、文件、音频、视频文字、Markdown 链接图片、文件、音频、视频需 bot 文件上传权限
LINE图片、文件、音频、视频文字、按钮模板—出站媒体需另配公网 HTTPS 媒体存储,未虚报为已支持
WhatsApp Business图片、文件、音频、视频文字、CTA 按钮图片、文件、音频、视频受 customer-care window 约束
Microsoft Teams图片、文件、音频、视频文字、Markdown 链接图片Bot Framework 内联图片;其他文件需另接 Graph/SharePoint 流程

出站附件统一限制为每次最多 4 个、通常单个最多 10 MiB;Teams 内联图片遵循平台的 1 MiB 上限。适配器遇到未声明的附件类型会明确报错,Gateway 在调用适配器前会按能力过滤 Desktop/Pet 产物。

Pet 使用分层、原子化工具:

  • 只读 Gateway:search 只在本回合实际授予的渠道中查询,不带 query 时返回全部渠道名, 也可用 outbound:image、inbound:file、button:native 等条件过滤;describe 再返回一个 匹配渠道的精确入站/出站契约。能力目录每回合从当前适配器集合重建,只含渠道名和能力, 不传递 token、白名单或会话 target。
  • 有副作用的 GatewayReply:只绑定当前入站消息的原会话,一次声明完整文字、可选 URL 按钮 和可选本地附件。它不能借第一层目录向其他渠道或其他 target 任意发送。
  • 有副作用的 SendMessage:用于桌面 Mimi 主动发往宿主授权的 owner 目标。模型只看到宿主生成 的不透明 target_id,看不到原始平台用户 ID;宿主在执行前重新读取白名单并校验文字、附件与 渠道能力。工具调用只登记请求,Mimi 的原始回复会被宿主的实际“平台已接受”或失败回执替换; 平台接受仍不等于收件人设备已经展示消息。

普通纯文字回复可以直接进入第二层;富媒体能力不确定、或用户询问另一个已启用渠道时,Mimi 先查询第一层。Gateway 按当前路由动态移除 GatewayReply 不支持的附件参数,并在真正调用 适配器前再次校验。 工具文字最多 8000 字,Gateway 使用 1800 字的跨渠道安全分片发送,按钮和附件只跟随最后一片; 中途失败时从未发送的分片续传,不会重新运行 Pet 或重复已经送达的前半段。需要多次 平台 API 调用的文字+媒体组合也会记录已完成子步骤,同一进程内重试时从失败子项继续。

个人微信使用腾讯 ClawBot 公开的 iLink Bot 协议,不接逆向 Web 协议。首次使用先执行:

code-shell-chat wechat login

终端会显示二维码,用手机微信扫码并确认。token 保存在 owner-only 凭据文件中,不写入 gateway 配置。登录命令会自动增加 wechat.accountId,扫码者默认成为唯一允许的用户。 @cjhyy/code-shell-chat/wechat 同时导出 loginWechatWithQr、WechatAdapter、 sendWechatDirect 和 FileWechatCredentialStore,第三方应用可以完全不使用 CodeShell CLI。 sendWechatDirect 是供定时任务和通知 worker 使用的独立入口:同一进程里若已有相同账号与 凭据的长轮询 adapter,它会排队复用该实例;否则才创建一次性 adapter。同一账号只允许 一个 live adapter;所有 adapter 实例的发送、限流冷却和生命周期请求共用账号级串行边界。 运行中若更换 token,必须先重启 Gateway,不会用新旧凭据打开竞争会话。CodeShell Desktop 的 扫码流程也把游标和上下文视为凭据会话状态:token 或服务地址变化时清空旧状态;凭据未变时保留 有效状态,而一次用户主动发起且平台确认成功的登录(含“已绑定”确认)可以修复损坏或不安全的 状态文件。普通启动仍然失败关闭。 Desktop 宿主包装还会在整个直发期间持有与 CLI 共用的 Gateway 单实例锁;若锁已由 CLI 持有,通知会 留给 CLI 的事件 watcher,不会再开一条短连接。这样可避免并行连接得到 HTTP 成功却没有在微信 客户端显示。返回值区分 status: "accepted"、terminalDeliveryConfirmed: false 和 viaLiveAdapter,调用方不得把它 渲染成“已送达”。

import { FileWechatCredentialStore, sendWechatDirect } from "@cjhyy/code-shell-chat/wechat";

const store = new FileWechatCredentialStore();
const credentials = store.load("my-account");
if (!credentials?.userId) throw new Error("请先完成微信扫码登录");

const receipt = await sendWechatDirect(
  credentials,
  credentials.userId,
  { text: "这是一条定时提醒" },
  { stateStore: store.stateStore(credentials.accountId) },
);
// receipt.status === "accepted" 只表示平台接口接受,不代表收件设备已展示。

这层路由直接借鉴 Hermes 的 send_weixin_direct: 先按凭据查找同事件循环里的 live adapter,找不到时才创建短生命周期 adapter,并让两条路径复用 同一套上下文、媒体上传和响应校验。CodeShell 把这个做法收敛为渠道 adapter 的 send() 能力和 宿主单实例锁,而不是让定时任务直接依赖微信模块。

个人微信的公开协议契约仍以 context_token 为主。adapter 会持久保存每个 owner 最近一次入站 消息带来的 token;遇到明确的过期响应时清除它,并用同一个 client_id 做一次无 token 的兼容 尝试,但该降级并非所有账号都支持。CodeShell Desktop 因此只在 owner 先给 Mimi 发过一条消息、 且状态中存在可用 token 时向 SendMessage 暴露微信目标;若返回 prepare failed,界面会要求 先从微信发一条新消息刷新上下文,而不会继续宣称推送成功。 入站长轮询只在上层持久接收消息后才提交 get_updates_buf,临时处理失败会保留旧游标重投。 媒体下载只允许已知微信 CDN 的 HTTPS 主机,并逐跳重新校验重定向。 桌面任务和定时通知只发布渠道无关的事件;Gateway 停止时,宿主会用同一套独立直发能力处理 支持 direct 的目标,Gateway 在线时则由事件 watcher 独占发送。两者共享事件进度,启动交接不会 重复发送;独立直发完整成功时可直接确认当前队首,不必等 watcher 以后上线才释放容量。进度文件在事件游标旁保留最近 200 个事件、每个目标的文本分段和附件子步骤;文件使用 owner-only 权限和原子替换, 符号链接、过大或损坏状态会失败关闭,不能伪造“已投递”进度。单个 渠道失败或 Gateway 正常重启时只续传未完成部分。传输已受理但未来得及落盘就崩溃的窗口仍是 「至少一次」,不会伪称精确一次。 控制事件本身也会在对 watcher 可见前,先原子写入 owner-only outbox(最多 200 条未确认事件)。 Desktop 重启后恢复同一 streamId 和递增事件 ID,所以 Gateway 会从已确认游标继续,而不是 把重启前尚未接收的通知遗留在内存里。落盘失败时发布直接失败,不会暴露半成功事件。 watcher 在下一次请求带回持久游标后,Desktop 才原子删除已确认前缀;未确认队列满时施加背压并拒绝 新发布,不会静默挤掉旧通知。如果恢复后发现本地游标高于 outbox,协议会显式要求 watcher 重置, 且 checkpoint 必须先写盘成功,内存游标才能前进。 每个 live watcher 事件在真正调用 adapter 前还会重读当前配置验证 target 白名单;用户已撤销的目标会被安全跳过, 不会因为 Gateway 启动时的旧快照继续发送。 长任务 closure、Work Session 主动上报和定时任务终态都会携带匿名稳定 delivery key;Desktop 控制事件若在上游恢复时被重新发布,仍会接续原目标/分段/附件进度,而不是把它当成全新通知。 定时任务的完成、失败、永久停用和用户取消分别发布 automation.completed、 automation.failed、automation.stopped 和 automation.cancelled;开始事件不外发,避免每次 执行产生两条通知。

协议实现对齐 Tencent/openclaw-weixin 2.4.x 的公开文档。

CodeShell CLI 配置

创建 ~/.code-shell/im-gateway/config.json 并设置为 0600。只需保留实际启用的平台: Desktop 的「编辑配置」会生成包含所有渠道的模板;在模板里把要使用的渠道设为 "enabled": true 并填写凭据和白名单即可,未启用的占位配置不会加载。

{
  "telegram": {
    "botToken": "123456:replace-me",
    "allowedChatIds": ["123456789"],
    "allowedUserIds": ["123456789"]
  },
  "discord": {
    "botToken": "replace-me",
    "allowedChannelIds": ["channel-id"],
    "allowedUserIds": ["user-id"]
  },
  "slack": {
    "botToken": "xoxb-replace-me",
    "appToken": "xapp-replace-me",
    "allowedChannelIds": ["C0123456789"],
    "allowedUserIds": ["U0123456789"]
  },
  "lark": {
    "appId": "cli_replace_me",
    "appSecret": "replace-me",
    "allowedChatIds": ["oc_chat_id"],
    "allowedUserIds": ["ou_open_id"]
  },
  "dingtalk": {
    "clientId": "replace-me",
    "clientSecret": "replace-me",
    "allowedConversationIds": ["cid_replace_me"],
    "allowedUserIds": ["staff-id"]
  },
  "wecom": {
    "botId": "replace-me",
    "secret": "replace-me",
    "allowedChatIds": ["chat-id-or-user-id"],
    "allowedUserIds": ["user-id"]
  },
  "wechat": {
    "accountId": "generated-by-wechat-login"
  },
  "matrix": {
    "homeserverUrl": "https://matrix.example.com",
    "accessToken": "replace-me",
    "botUserId": "@codeshell:example.com",
    "allowedRoomIds": ["!room:example.com"],
    "allowedUserIds": ["@owner:example.com"]
  },
  "mattermost": {
    "serverUrl": "https://mattermost.example.com",
    "botToken": "replace-me",
    "botUserId": "bot-user-id",
    "allowedChannelIds": ["channel-id"],
    "allowedUserIds": ["owner-user-id"]
  },
  "line": {
    "channelSecret": "replace-me",
    "channelAccessToken": "replace-me",
    "allowedTargetIds": ["group-room-or-user-id"],
    "allowedUserIds": ["owner-user-id"]
  },
  "whatsapp": {
    "accessToken": "replace-me",
    "appSecret": "replace-me",
    "verifyToken": "choose-a-random-value",
    "phoneNumberId": "replace-me",
    "apiVersion": "v25.0",
    "allowedPhoneNumbers": ["8613800000000"]
  },
  "teams": {
    "appId": "replace-me",
    "appPassword": "replace-me",
    "appType": "MultiTenant",
    "allowedConversationIds": ["conversation-id"],
    "allowedUserIds": ["aad-or-channel-user-id"]
  },
  "webhook": {
    "host": "127.0.0.1",
    "port": 8787,
    "maxBodyBytes": 1048576
  },
  "runtime": {
    "maxPending": 1000,
    "maxConcurrent": 4,
    "maxPerTarget": 1,
    "maxMessagesPerUserPerMinute": 20
  },
  "notifications": {
    "enabled": true,
    "targets": {
      "telegram": ["123456789"]
    }
  }
}

每个已启用渠道都必须配置 target 白名单;allowedUserIds 可选,但群聊强烈建议配置。 未知会话和用户会被静默忽略。配置文件包含 bot secret、允许远程拉起桌面端的命令,非 Windows 系统只接受 0600 权限。

可序列化的文字入站会先原子写入 ~/.code-shell/im-gateway/inbox.json,webhook 平台随后才会 收到确认;同一账号的相同平台 message ID 会持久去重。处理采用有界队列、全局并发和 per-target 串行,暂时失败按指数退避重试。附件字节先写入 inbox 旁的 spool 再确认;spool 本地写入失败时 拒绝确认,交由平台重投。超出 spool 上限或下载失败的媒体保留在内存队列,仍受平台重投策略约束。 notifications.targets 最多 32 个,只能引用已启用渠道且必须同时位于该渠道 target 白名单。Desktop 主动 通知游标会按事件流实例原子写入 ~/.code-shell/im-gateway/desktop-events.json(可用 runtime.eventCursorPath 覆盖);Gateway 和 Desktop 正常重启后都会续传。只有 Desktop 初始化了全新的 事件 outbox 时,watcher 才会识别新 streamId 并安全重置游标。

所有字段都有 CODE_SHELL_<CHANNEL>_* 环境变量对应项;数组使用逗号分隔。例如:

export CODE_SHELL_DISCORD_BOT_TOKEN='replace-me'
export CODE_SHELL_DISCORD_ALLOWED_CHANNEL_IDS='channel-1,channel-2'
export CODE_SHELL_DISCORD_ALLOWED_USER_IDS='owner-user-id'

也可用 CODE_SHELL_IM_GATEWAY_CONFIG 指定配置路径。完整环境变量名以仓库中的 packages/chat/src/config.ts 为准。

Webhook 渠道

LINE、WhatsApp、Teams 要求一个固定公网 HTTPS origin。gateway 默认仅监听 127.0.0.1:8787,应由同机的反向代理或固定隧道暴露上表中的 path。手机遥控使用的 Cloudflare quick tunnel 指向 mobile host、URL 也会变化,不能当作这些平台的 webhook origin。

  • LINE POST 请求校验 x-line-signature;
  • WhatsApp 支持 GET challenge,并用 app secret 校验 x-hub-signature-256;
  • Teams 使用官方 Bot Framework SDK 校验 bearer JWT 和 channel identity;
  • request body 默认最多 1 MiB,且 webhook server 有请求超时。

本地探针为 GET /healthz 与 GET /readyz;探针不会由生成的公网 ingress 暴露。生成固定 HTTPS 反向代理配置:

code-shell-chat ingress print --host chat.example.com --format caddy
# 或 --format nginx;upstream 只允许 loopback host:port

平台侧最低要求

  • Discord 需启用 Message Content intent;gateway 启动时会补注册 /open、/close、/status application commands;
  • Slack app 需启用 Socket Mode、订阅 message events、注册三个同名 slash commands,并给 bot chat:write;
  • 飞书需开启机器人能力、订阅 im.message.receive_v1,选择长连接接收事件;
  • 钉钉应用机器人选择 Stream 模式;
  • 企业微信使用智能机器人长连接的 bot ID + secret;
  • 个人微信需先运行 code-shell-chat wechat login,支持二维码确认和手机数字验证;
  • Telegram 不得同时配置 webhook,因为 webhook 与 getUpdates 互斥;
  • WhatsApp 自由格式回复受 Cloud API 的 customer-care window 约束。

运行

桌面端需要先设置 mobile remote 访问口令。macOS 默认用 open -a code-shell 拉起离线桌面端; 其他安装方式可覆盖 desktop.command 和 desktop.args。

bun run --filter '@cjhyy/code-shell-chat' build
node packages/chat/dist/cli.js

也可以直接开发运行:

bun run --filter '@cjhyy/code-shell-chat' dev

安装为当前用户的常驻服务(macOS launchd、Linux systemd user、Windows Task Scheduler):

code-shell-chat service install
code-shell-chat service status
code-shell-chat service uninstall

服务定义不写入 secret,只引用 owner-only 配置;运行时单实例锁会阻止 Desktop 与 daemon 双开。

真实平台发布前运行凭据 canary。命令会生成随机 nonce;在每个启用 channel 的白名单会话中发送 提示的 /canary <nonce>,只有真实入站、验签/鉴权、allowlist 和真实回发全部成功才会通过:

code-shell-chat canary --timeout-ms 600000

Keywords

chat

FAQs

Package last updated on 22 Oct 2026

Related posts