@@ -24,15 +24,15 @@ import { spawn } from 'node:child_process'; | ||
| if (options?.chatId) { | ||
| env.CC_BOT_CHAT_ID = options.chatId; | ||
| env.CC_IM_CHAT_ID = options.chatId; | ||
| } | ||
| if (options?.hookPort) { | ||
| env.CC_BOT_HOOK_PORT = String(options.hookPort); | ||
| env.CC_IM_HOOK_PORT = String(options.hookPort); | ||
| } | ||
| if (options?.threadRootMsgId) { | ||
| env.CC_BOT_THREAD_ROOT_MSG_ID = options.threadRootMsgId; | ||
| env.CC_IM_THREAD_ROOT_MSG_ID = options.threadRootMsgId; | ||
| } | ||
| if (options?.threadId) { | ||
| env.CC_BOT_THREAD_ID = options.threadId; | ||
| env.CC_IM_THREAD_ID = options.threadId; | ||
| } | ||
| if (options?.platform) { | ||
| env.CC_BOT_PLATFORM = options.platform; | ||
| env.CC_IM_PLATFORM = options.platform; | ||
| } | ||
@@ -39,0 +39,0 @@ const child = spawn(cliPath, args, { |
@@ -157,2 +157,12 @@ import { join } from 'node:path'; | ||
| dispatcher.register({ | ||
| 'im.message.recalled_v1': async (data) => { | ||
| const messageId = data?.message_id; | ||
| if (!messageId) | ||
| return; | ||
| if (sessionManager.removeThreadByRootMessageId(messageId)) { | ||
| log.info(`Thread session removed for recalled message: ${messageId}`); | ||
| } | ||
| }, | ||
| }); | ||
| dispatcher.register({ | ||
| 'im.message.receive_v1': async (data) => { | ||
@@ -159,0 +169,0 @@ const message = data.message; |
@@ -10,4 +10,4 @@ #!/usr/bin/env node | ||
| * Environment variables: | ||
| * CC_BOT_CHAT_ID - Chat ID to send the permission card to | ||
| * CC_BOT_HOOK_PORT - Port of the local permission server (default: 18900) | ||
| * CC_IM_CHAT_ID - Chat ID to send the permission card to | ||
| * CC_IM_HOOK_PORT - Port of the local permission server (default: 18900) | ||
| * | ||
@@ -69,4 +69,4 @@ * stdin: JSON { session_id, tool_name, tool_input } | ||
| async function main() { | ||
| const chatId = process.env.CC_BOT_CHAT_ID; | ||
| const port = parseInt(process.env.CC_BOT_HOOK_PORT ?? '18900', 10); | ||
| const chatId = process.env.CC_IM_CHAT_ID; | ||
| const port = parseInt(process.env.CC_IM_HOOK_PORT ?? '18900', 10); | ||
| // No chat ID configured - allow by default and exit | ||
@@ -96,5 +96,5 @@ if (!chatId) { | ||
| } | ||
| const threadRootMsgId = process.env.CC_BOT_THREAD_ROOT_MSG_ID; | ||
| const threadId = process.env.CC_BOT_THREAD_ID; | ||
| const platform = process.env.CC_BOT_PLATFORM; | ||
| const threadRootMsgId = process.env.CC_IM_THREAD_ROOT_MSG_ID; | ||
| const threadId = process.env.CC_IM_THREAD_ID; | ||
| const platform = process.env.CC_IM_PLATFORM; | ||
| try { | ||
@@ -101,0 +101,0 @@ const result = await httpPost(port, '/permission-request', { |
@@ -206,2 +206,16 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'; | ||
| } | ||
| removeThreadByRootMessageId(rootMessageId) { | ||
| for (const [, session] of this.sessions) { | ||
| if (!session.threads) | ||
| continue; | ||
| for (const [threadId, thread] of Object.entries(session.threads)) { | ||
| if (thread.rootMessageId === rootMessageId) { | ||
| delete session.threads[threadId]; | ||
| this.save(); | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| listThreads(userId) { | ||
@@ -208,0 +222,0 @@ const threads = this.sessions.get(userId)?.threads; |
+6
-1
| { | ||
| "name": "cc-im", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "description": "Multi-platform bot bridge (Feishu & Telegram) for Claude Code CLI", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/congqiu/cc-im.git" | ||
| }, | ||
| "homepage": "https://github.com/congqiu/cc-im", | ||
| "type": "module", | ||
@@ -6,0 +11,0 @@ "main": "dist/index.js", |
+21
-7
@@ -11,2 +11,6 @@ # cc-im | ||
| - **流式输出**:飞书端使用 CardKit 打字机效果,Telegram 端通过 editMessage 实时更新 | ||
| - **思考过程展示**:实时显示 Claude 的思考过程(折叠面板) | ||
| - **工具调用通知**:流式显示当前正在使用的工具及参数摘要 | ||
| - **图片消息支持**:支持发送图片给 Claude 进行分析 | ||
| - **话题会话**:飞书群聊话题(thread)独立会话 | ||
| - **会话管理**:每用户独立 session,支持 `/new` 重置 | ||
@@ -18,5 +22,9 @@ - **并发控制**:同会话串行执行,不同会话可并发,最多排队 3 条消息 | ||
| - **停止按钮**:执行过程中可随时停止 | ||
| - **工具使用统计**:完成时显示工具调用次数和类型 | ||
| - **生命周期通知**:服务启动/关闭时通知活跃用户 | ||
| ## 快速开始 | ||
| > 要求:Node.js >= 20,需要预先安装 [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) | ||
| ### 同时运行多个平台 | ||
@@ -58,8 +66,10 @@ | ||
| 2. 开启机器人能力 | ||
| 3. 添加权限:`im:message`、`im:message:send_as_bot`、`im:message:patch_as_bot`、`cardkit:card` | ||
| 3. 添加权限:`im:message`、`im:message:send_as_bot`、`im:message.group_msg`、`im:message.p2p_msg:readonly`、`cardkit:card:write` | ||
| 4. 事件订阅中启用 **长连接模式**,订阅以下事件: | ||
| - `im.message.receive_v1` — 接收消息 | ||
| - `im.message.recalled_v1` — 消息撤回(自动清理话题会话) | ||
| 5. 回调订阅 | ||
| - `card.action.trigger` — 卡片交互(停止按钮) | ||
| 5. 发布应用 | ||
| 6. 配置并启动: | ||
| 6. 发布应用 | ||
| 7. 配置并启动: | ||
@@ -75,3 +85,3 @@ ```bash | ||
| ```bash | ||
| git clone <repo-url> | ||
| git clone https://github.com/congqiu/cc-im.git | ||
| cd cc-im | ||
@@ -103,2 +113,3 @@ pnpm install | ||
| | `/todos` | 查看待办事项 | | ||
| | `/threads` | 列出所有话题会话(飞书) | | ||
@@ -120,3 +131,2 @@ ### 权限相关命令 | ||
| |------|------|--------| | ||
| | `PLATFORM` | 平台选择(`telegram` 或 `feishu`),留空自动检测 | 自动检测 | | ||
| | `FEISHU_APP_ID` | 飞书应用 App ID | 飞书平台必填 | | ||
@@ -145,3 +155,2 @@ | `FEISHU_APP_SECRET` | 飞书应用 App Secret | 飞书平台必填 | | ||
| { | ||
| "platform": "telegram", | ||
| "telegramBotToken": "your_bot_token", | ||
@@ -168,3 +177,4 @@ "allowedUserIds": ["123456789"], | ||
| ├── data/ | ||
| │ └── sessions.json # 会话持久化数据 | ||
| │ ├── sessions.json # 会话持久化数据 | ||
| │ └── active-chats.json # 活跃聊天记录(生命周期通知) | ||
| └── logs/ # 日志文件(可通过 LOG_DIR 自定义) | ||
@@ -219,2 +229,6 @@ ├── 2026-02-14.log | ||
| │ └── hook-script.ts # Claude Code PreToolUse Hook | ||
| ├── shared/ | ||
| │ ├── active-chats.ts # 活跃聊天记录(生命周期通知) | ||
| │ ├── types.ts # 共享类型定义 | ||
| │ └── utils.ts # 共享工具函数 | ||
| ├── session/ | ||
@@ -221,0 +235,0 @@ │ └── session-manager.ts # 会话管理(持久化到 data/sessions.json) |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
No website
QualityPackage does not have a website.
170332
1.15%4009
0.6%0
-100%234
6.36%27
-3.57%