🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

多模型茶馆 plutoCC/chat-teahouse

DeepSeek Harness
plutoCC15 天前有提交已提供中文文档扫描:含敏感能力

让多个大模型在同一群聊里自主发言讨论

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/20 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add plutoCC/chat-teahouse
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · tool
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 15 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 92 处,展示前 83 条)
  • 读取本地文件
    server/app.ts:45
    const freshWorkspace=!existsSync(resolve(options.dataDir,'teahouse.sqlite'));
  • 读取本地文件
    server/app.ts:304
    const root=resolve('dist');if(!existsSync(resolve(root,'index.html')))throw new Error('请先运行 npm run build。');
  • 读取本地文件
    server/browser-connector.ts:65
    if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new BrowserConnectorError('浏览器配置目录不能是符号链接。',400);
  • 读取本地文件
    server/browser-connector.ts:65
    if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new BrowserConnectorError('浏览器配置目录不能是符号链接。',400);
  • 写入本地文件
    server/browser-connector.ts:66
    mkdirSync(path,{recursive:true,mode:0o700});
  • 发起网络请求
    server/browser-connector.ts:91
    await context.route('**/*',route=>safeBrowserRequest(route.request().url())?route.continue():route.abort('blockedbyclient'));
  • 读取本地文件
    server/hosting/accounts.ts:25
    for(const path of [directory,file])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new HostingAccountError('分享账号路径不能使用符号链接。');
  • 读取本地文件
    server/hosting/accounts.ts:25
    for(const path of [directory,file])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new HostingAccountError('分享账号路径不能使用符号链接。');
  • 写入本地文件
    server/hosting/accounts.ts:26
    mkdirSync(directory,{recursive:true,mode:0o700});
  • 写入本地文件
    server/hosting/accounts.ts:27
    if(process.platform!=='win32')chmodSync(directory,0o700);
  • 写入本地文件
    server/hosting/accounts.ts:34
    if(process.platform!=='win32')chmodSync(file,0o600);
  • 读取本地文件
    server/hosting/accounts.ts:68
    if(existsSync(root)&&lstatSync(root).isSymbolicLink())throw new HostingAccountError('邀请码路径不能使用符号链接。');
  • 读取本地文件
    server/hosting/accounts.ts:68
    if(existsSync(root)&&lstatSync(root).isSymbolicLink())throw new HostingAccountError('邀请码路径不能使用符号链接。');
  • 读取本地文件
    server/hosting/accounts.ts:75
    for(const path of [root,directory,file])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new HostingAccountError('邀请码路径不能使用符号链接。');
  • 读取本地文件
    server/hosting/accounts.ts:75
    for(const path of [root,directory,file])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new HostingAccountError('邀请码路径不能使用符号链接。');
  • 写入本地文件
    server/hosting/budget.ts:31
    mkdirSync(dataDir,{recursive:true,mode:0o700});this.db=new DatabaseSync(join(dataDir,'hosting-budget.sqlite'));
  • 读取本地文件
    server/hosting/config.ts:10
    for(let cursor=dir;cursor!==dirname(cursor);cursor=dirname(cursor))if(existsSync(cursor)&&lstatSync(cursor).isSymbolicLink())throw new Error('网站数据目录及父
  • 读取本地文件
    server/hosting/config.ts:10
    for(let cursor=dir;cursor!==dirname(cursor);cursor=dirname(cursor))if(existsSync(cursor)&&lstatSync(cursor).isSymbolicLink())throw new Error('网站数据目录及父
  • 读取本地文件
    server/hosting/config.ts:11
    if(existsSync(resolve(dir,'teahouse.sqlite')))throw new Error('不能把已有个人工作区用作网站数据根目录。');
  • 写入本地文件
    server/hosting/config.ts:12
    mkdirSync(dir,{recursive:true,mode:0o700});
  • 读取本地文件
    server/hosting/config.ts:14
    if(existsSync(marker)){if(readFileSync(marker,'utf8')!=='{"format":"chat-teahouse-hosting-v1"}\n')throw new Error('网站数据目录标识无效。');}
  • 读取本地文件
    server/hosting/config.ts:14
    if(existsSync(marker)){if(readFileSync(marker,'utf8')!=='{"format":"chat-teahouse-hosting-v1"}\n')throw new Error('网站数据目录标识无效。');}
  • 写入本地文件
    server/hosting/config.ts:15
    else writeFileSync(marker,'{"format":"chat-teahouse-hosting-v1"}\n',{flag:'wx',mode:0o600});
  • 写入本地文件
    server/hosting/index.ts:22
    try{const fd=openSync(path,'wx',0o600);writeFileSync(fd,String(process.pid));closeSync(fd);}
  • 读取本地文件
    server/hosting/index.ts:25
    const pid=Number(readFileSync(path,'utf8'));if(!Number.isSafeInteger(pid)||pid<=0)throw new Error('网站进程锁无效,请先确认旧服务已停止。');
  • 删除本地文件
    server/hosting/index.ts:28
    unlinkSync(path);return processLock(dir);
  • 读取本地文件
    server/hosting/index.ts:30
    return ()=>{if(readFileSync(path,'utf8')===String(process.pid))unlinkSync(path);};
  • 删除本地文件
    server/hosting/index.ts:30
    return ()=>{if(readFileSync(path,'utf8')===String(process.pid))unlinkSync(path);};
  • 读取本地文件
    server/hosting/settings.ts:7
    function rejectLinks(path:string){for(let cursor=path;;cursor=dirname(cursor)){if(existsSync(cursor)&&lstatSync(cursor).isSymbolicLink())throw new Hos
  • 读取本地文件
    server/hosting/settings.ts:7
    function rejectLinks(path:string){for(let cursor=path;;cursor=dirname(cursor)){if(existsSync(cursor)&&lstatSync(cursor).isSymbolicLink())throw new Hos
  • 写入本地文件
    server/hosting/settings.ts:14
    this.initial=normalizeHostingSettings(initial);mkdirSync(dirname(this.path),{recursive:true,mode:0o700});
  • 读取本地文件
    server/hosting/settings.ts:18
    if(!existsSync(this.path))return {version:0,values:{...this.initial}};
  • 读取本地文件
    server/hosting/settings.ts:20
    if(!lstatSync(this.path).isFile()||lstatSync(this.path).size>32768)throw new Error();
  • 读取本地文件
    server/hosting/settings.ts:21
    const value=JSON.parse(readFileSync(this.path,'utf8'));
  • 写入本地文件
    server/hosting/settings.ts:37
    writeFileSync(temporary,JSON.stringify({format:'chat-teahouse-settings-v1',...next},null,2)+'\n',{mode:0o600,flag:'wx'});
  • 写入本地文件
    server/hosting/settings.ts:38
    renameSync(temporary,this.path);return next;
  • 读取本地文件
    server/hosting/settings.ts:39
    }finally{if(existsSync(temporary))unlinkSync(temporary);closeSync(descriptor);unlinkSync(lock);}
  • 删除本地文件
    server/hosting/settings.ts:39
    }finally{if(existsSync(temporary))unlinkSync(temporary);closeSync(descriptor);unlinkSync(lock);}
  • 写入本地文件
    server/hosting/shared-members.ts:16
    mkdirSync(directory,{recursive:true,mode:0o700});this.db=new DatabaseSync(join(directory,'shared-members.sqlite'));
  • 读取本地文件
    server/hosting/shared-members.ts:68
    const path=join(directory,'teahouse.sqlite');if(!existsSync(path))return;
  • 读取本地文件
    server/hosting/shared-members.ts:89
    const configPath=join(projectRoot,'data-hosted','hosting-launch.json');if(resolve(dataDir)!==resolve(projectRoot,'data')||!existsSync(configPath))retu
  • 读取本地文件
    server/hosting/shared-members.ts:90
    const config=JSON.parse(readFileSync(configPath,'utf8')) as {DataDir?:string;dataDir?:string};
  • 读取本地文件
    server/hosting/shared-members.ts:100
    const users=join(dataDir,'users');if(existsSync(users))for(const entry of readdirSync(users,{withFileTypes:true})){
  • 读取本地文件
    server/hosting/shared-members.ts:100
    const users=join(dataDir,'users');if(existsSync(users))for(const entry of readdirSync(users,{withFileTypes:true})){
  • 发起网络请求
    server/model-catalog.ts:33
    const response = await fetch(url, { headers, redirect: 'error', signal });
  • 发起网络请求
    server/providers.ts:175
    const response = await fetch(endpoint, { method: 'POST', headers: headers(request.provider, request.apiKey), body: JSON.stringify(body), signal, redir
  • 发起网络请求
    server/providers.ts:262
    const response = await fetch(`${base(provider)}/${isArk ? 'tokenization' : provider.id === 'ollama' ? 'tags' : 'models'}`, { method: isArk ? 'POST' :
  • 读取本地文件
    server/runtime-adapters/context-memory.ts:19
    if(existsSync(this.path)){
  • 读取本地文件
    server/runtime-adapters/context-memory.ts:20
    const stat=lstatSync(this.path);if(stat.isSymbolicLink()||!stat.isFile()||stat.size>2_000_000)return;
  • 读取本地文件
    server/runtime-adapters/context-memory.ts:21
    try{const parsed=z.object({version:z.literal(1),entries:z.array(entry).max(128)}).strict().parse(JSON.parse(readFileSync(this.path,'utf8')));this.entr
  • 读取本地文件
    server/runtime-adapters/context-memory.ts:31
    for(const path of [this.path,temp])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new Error('Unsafe context memory path');
  • 读取本地文件
    server/runtime-adapters/context-memory.ts:31
    for(const path of [this.path,temp])if(existsSync(path)&&lstatSync(path).isSymbolicLink())throw new Error('Unsafe context memory path');
  • 写入本地文件
    server/runtime-adapters/context-memory.ts:32
    writeFileSync(temp,JSON.stringify({version:1,entries:[...this.entries.values()]}),{mode:0o600});renameSync(temp,this.path);
  • 写入本地文件
    server/runtime-adapters/context-memory.ts:32
    writeFileSync(temp,JSON.stringify({version:1,entries:[...this.entries.values()]}),{mode:0o600});renameSync(temp,this.path);
  • 写入本地文件
    server/runtime-adapters/harness.ts:179
    const directory=resolve(this.dataDir,'agent-workspaces',id);mkdirSync(directory,{recursive:true,mode:0o700});
  • 发起网络请求
    server/runtime-adapters/native-model.ts:153
    const response=await fetch(request.url,{method:'POST',headers:request.headers,body:JSON.stringify(request.body),signal,redirect:'error'});
  • 写入本地文件
    server/store.ts:17
    mkdirSync(dataDir, { recursive: true, mode: 0o700 });
  • 读取本地文件
    server/subscription/codex-rpc.ts:8
    const configured=process.env.CHAT_TEA_CODEX_PATH;if(configured){if(!isAbsolute(configured)||!existsSync(configured))throw new ModelCallError('CHAT_TEA
  • 读取本地文件
    server/subscription/codex-rpc.ts:9
    if(process.platform==='win32'&&process.env.LOCALAPPDATA){const root=join(process.env.LOCALAPPDATA,'OpenAI','Codex','bin');if(existsSync(root)){const c
  • 读取本地文件
    server/subscription/codex-rpc.ts:9
    if(process.platform==='win32'&&process.env.LOCALAPPDATA){const root=join(process.env.LOCALAPPDATA,'OpenAI','Codex','bin');if(existsSync(root)){const c
  • 写入本地文件
    server/subscription/codex-rpc.ts:21
    mkdirSync(this.home,{recursive:true,mode:0o700});mkdirSync(this.workspace,{recursive:true,mode:0o700});
  • 执行外部命令来自配置/变量
    server/subscription/codex-rpc.ts:25
    const child=this.child=spawn(codexExecutable(),['app-server','--listen','stdio://',...config.flatMap(c=>['-c',c])],{cwd:this.workspace,env,windowsHide
  • 发起网络请求
    server/subscription/codex.ts:40
    const rpc=this.rpc(id),result=await rpc.request('account/read',{refreshToken:false});const account=result?.account;
  • 发起网络请求
    server/subscription/codex.ts:43
    try{next.quotas=quotasOf(await rpc.request('account/rateLimits/read'));}catch{next.error='当前无法读取官方剩余额度;未知不代表额度充足。';}
  • 发起网络请求
    server/subscription/codex.ts:47
    async login(id:string){await this.cancel(id);const result=await this.rpc(id).request('account/login/start',{type:'chatgpt'});let url:URL;try{url=new U
  • 发起网络请求
    server/subscription/codex.ts:51
    async cancel(id:string){const loginId=this.logins.get(id);if(loginId){await this.rpc(id).request('account/login/cancel',{loginId});this.logins.delete(
  • 发起网络请求
    server/subscription/codex.ts:52
    async logout(id:string){await this.cancel(id);await this.rpc(id).request('account/logout');this.cache.delete(id);this.notify();}
  • 发起网络请求
    server/subscription/codex.ts:56
    for(let i=0;i<5;i++){const data=await this.rpc(id).request('model/list',{limit:100,includeHidden:false,...(cursor?{cursor}:{})});for(const m of data?.
  • 执行外部命令来自配置/变量
    server/vault.ts:46
    const result = spawnSync(powershell, ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', DPAPI_SCRIPT], {
  • 读取本地文件
    server/vault.ts:56
    if (existsSync(path) && lstatSync(path).isSymbolicLink()) throw new Error('凭据存储不能使用符号链接。');
  • 读取本地文件
    server/vault.ts:56
    if (existsSync(path) && lstatSync(path).isSymbolicLink()) throw new Error('凭据存储不能使用符号链接。');
  • 写入本地文件
    server/vault.ts:62
    writeFileSync(temporary, contents, { mode: 0o600, flag: 'wx' });
  • 写入本地文件
    server/vault.ts:63
    renameSync(temporary, path);
  • 写入本地文件
    server/vault.ts:64
    if (process.platform !== 'win32') chmodSync(path, 0o600);
  • 读取本地文件
    server/vault.ts:66
    if (existsSync(temporary)) unlinkSync(temporary);
  • 删除本地文件
    server/vault.ts:66
    if (existsSync(temporary)) unlinkSync(temporary);
  • 写入本地文件
    server/vault.ts:81
    mkdirSync(directory, { recursive: true, mode: 0o700 });
  • 读取本地文件
    server/vault.ts:82
    if (!lstatSync(directory).isDirectory()) throw new Error('凭据数据路径必须是目录。');
  • 写入本地文件
    server/vault.ts:87
    chmodSync(directory, 0o700);
  • 读取本地文件
    server/vault.ts:93
    if (!existsSync(keyPath)) {
  • 读取本地文件
    server/vault.ts:94
    if (existsSync(this.path)) throw new Error('加密凭据存在,但主密钥缺失。请恢复原始主密钥,避免丢失凭据。');
  • 写入本地文件
    server/vault.ts:103
    if (process.platform !== 'win32') chmodSync(keyPath, 0o600);
  • 发起网络请求
    src/draft-presence.ts:18
    const leave=()=>{for(const [groupId,hasDraft] of known.current)if(hasDraft){const body:DraftPresenceUpdate={clientId:client.current,sequence:++sequenc
扫描于 2026/11/3(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-loop@deepseek-ai/dsh-llm@deepseek-ai/dsh-session@deepseek-ai/dsh-session-persistence@deepseek-ai/dsh-session-persistence-jsonl@deepseek-ai/dsh-session-projection@deepseek-ai/dsh-system-prompt@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

chat 茶馆:让多个大语言模型在同一群聊中独立思考、自由讨论。本机运行,支持多模型、多成员、阅读节奏与用量管理。

README

由 DeepSeek 最新模型翻译生成
chat 茶馆 · Chat Teahouse 让不同大语言模型加入同一个群聊,读取彼此公开的观点,自行决定发言、追问或等待。你也可以随时加入讨论。 本地优先的多模型 AI 群聊。 参与者各自独立,公开讨论共享,节奏可配置,群聊长期保留。基于 React、Fastify、SQLite 和 DeepSeek Harness 构建。 这是运行在自己电脑上的网页应用,界面采用常见聊天软件的三栏布局。当前是预稳定的 0.2.0 源码版本,没有桌面安装包,也不是可直接部署到公网的多人 SaaS。 界面预览 主界面:群聊列表、独立成员的观点和随时参与讨论的输入区。 茶馆主界面:群聊列表和多人讨论 输入 @ 选择要点名的成员,随时加入讨论。 茶馆主界面:输入 @ 后选择群聊成员 以上截图使用独立的虚构演示数据,消息为预设示例,不是真实模型回答;不包含个人聊天、账号、密钥或本机历史记录。 功能 - 自主群聊:每位成员拥有独立 Agent 会话,自行选择发言或等待,不要求按固定轮次回答用户;另提供按轮圆桌模式。 - 多模型、多连接:OpenAI、Claude、DeepSeek、Qwen、豆包 / 火山方舟、本机 Ollama;同一供应商可保存多个 API 连接。 - 长期成员库:名字、图片头像、身份、模型与技能长期保存,可邀请到不同群聊。群内会话和上下文分别隔离。
展开完整文档(README 剩余 1 节)
chat 茶馆 · Chat Teahouse

让不同大语言模型加入同一个群聊,读取彼此公开的观点,自行决定发言、追问或等待。你也可以随时加入讨论。

本地优先的多模型 AI 群聊。 参与者各自独立,公开讨论共享,节奏可配置,群聊长期保留。基于 React、Fastify、SQLite 和 DeepSeek Harness 构建。

这是运行在自己电脑上的网页应用,界面采用常见聊天软件的三栏布局。当前是预稳定的 0.2.0 源码版本,没有桌面安装包,也不是可直接部署到公网的多人 SaaS。

界面预览

主界面:群聊列表、独立成员的观点和随时参与讨论的输入区。

茶馆主界面:群聊列表和多人讨论

输入 @ 选择要点名的成员,随时加入讨论。

茶馆主界面:输入 @ 后选择群聊成员

以上截图使用独立的虚构演示数据,消息为预设示例,不是真实模型回答;不包含个人聊天、账号、密钥或本机历史记录。

功能

- 自主群聊:每位成员拥有独立 Agent 会话,自行选择发言或等待,不要求按固定轮次回答用户;另提供按轮圆桌模式。
- 多模型、多连接:OpenAI、Claude、DeepSeek、Qwen、豆包 / 火山方舟、本机 Ollama;同一供应商可保存多个 API 连接。
- 长期成员库:名字、图片头像、身份、模型与技能长期保存,可邀请到不同群聊。群内会话和上下文分别隔离。
- 自然的聊天节奏:分段消息、阅读速度、随机发送间隔、输入保护、冷却、唤醒概率和并发可配置;新群可沿用默认模板。
- 人类随时参与:输入 @ 选择成员;点名会唤醒对应成员,已在思考的成员不会重复启动。
- 可见活动与预算:每群日志、接口实际返回的推理内容或摘要、模型说明的等待原因、逐成员/模型 Token 和人民币费用估算。
- 本地资料:聊天与成员保存在本机,支持 Markdown / JSON 导出、本地表情库、文字技能与暂停恢复。
- 私人笔记本:选中发言文字后加粗、变色、下划线、高亮、收藏和评论;按群聊与自定义分类整理灵感,不改变原消息或 AI 上下文。
- 等你输入完成:可选开启草稿等待;有文字就暂缓 AI 发言,删光后等待 3 秒恢复,支持分段消息和多窗口。
- 实验渠道:通过本机官方 Codex app-server 使用订阅授权;通过独立浏览器配置操作支持的聊天官网。兼容性和范围见下表。

原生身份模式不附加人设或预设立场,但仍保留群聊身份和共同讨论规则。它不代表模型具有自由意志,也不能消除供应商自己的行为规则。程序只展示接口实际提供的推理字段,不会生成或获取未开放的内部思考。

快速开始

需要 Node.js 24(建议使用该大版本的最新维护版)、npm,以及一个现代浏览器。通过 Git 获取源码,或者从 Releases 下载源码压缩包。

git clone https://github.com/plutoCC/chat-teahouse.git
cd chat-teahouse
npm ci
npm run build
npm start

打开 http://127.0.0.1:4317/。使用上述命令启动时请保持终端运行,按 Ctrl+C 停止。

后端直接使用 tsx 启动,因此安装时必须保留开发依赖,不要使用 npm ci --omit=dev。云 API 的密钥在应用的「模型连接」页面配置,不写进源码。

Windows 一键启动: 安装好 Node.js 24 并解压源码后,双击根目录的 Launch-Teahouse.cmd。它会自动安装缺少的依赖、构建、启动隐藏后台,并在服务就绪后用默认浏览器打开网站。重复双击会打开已运行的网站;Start-Teahouse.cmd 也提供相同体验。首次安装需要网络,准备期间请等待启动窗口完成。双击 Stop-Teahouse.cmd 停止后台,关闭网页不会停止讨论。

此方式不依赖 Codex 桌面应用,也不需要单独打开 DeepSeek Harness 客户端。可为 Launch-Teahouse.cmd 创建桌面快捷方式;不要把脚本单独移出项目目录。

详细的系统准备、后台运行、环境变量、升级与备份步骤见 部署指南。

第一次使用

1. 使用初始的本地演示成员熟悉界面;演示是预设文本,不调用云端模型。
2. 在「模型连接」中新增或编辑连接,保存密钥、选择模型并启用。模型列表中的参考选项不代表账号已经获得权限。
3. 按当前供应商价格为每个模型分别填写输入和输出单价,用于费用估算和金额保护。
4. 在「AI 成员库」创建成员,选择具体连接、模型和身份模式。
5. 新建群聊、邀请成员并填写议题,调整预算和节奏后点击「开始讨论」。

自主群聊需要模型支持工具调用。读消息、决定等待和纠正工具调用都可能产生用量;未公开发言不等于没有费用。详细说明见 使用指南。

模型与渠道

| 渠道 | 自主群聊 | 按轮圆桌 | 说明 |
| OpenAI / Claude / DeepSeek / Qwen / 豆包 API | 支持 | 支持 | 需相应账号、权限与 API 额度;自主模式需工具调用 |
| 本机 Ollama | 支持 | 支持 | 需先下载并启动适用模型,固定连接本机端口 |
| 本地演示 | 支持 | 支持 | 预设响应,用于体验与测试 |
| ChatGPT 订阅经 Codex app-server | 实验支持 | 不支持 | 需本机官方 Codex、可用订阅及官方登录,显示账号额度 |
| 官网浏览器连接 | 不支持 | 实验支持 | 支持的站点预设可能随官网更新失效,需要用户自行登录 |
适配器与模拟测试不等于所有账号或所有模型都已经实测可用。浏览器渠道不会把网页免费额度变成 API 额度,也不绕过网站验证与限制。订阅与 API 计费彼此独立;订阅连接失败不会自动改用付费 API。

账户查询面板支持 DeepSeek 余额、火山账户余额与可识别的方舟月度费用、阿里云余额、OpenAI / Claude 组织费用,以及 Codex 账号额度。部分查询需要额外的管理或财务凭据;没有配置时不执行查询。账户数据可能包含其他应用的用量,不能当作本群账单。

数据与安全

服务固定监听 127.0.0.1,面向单机个人使用。不要直接映射到公网;当前没有多用户认证、租户隔离或远程托管方案。

- data/ 保存数据库、加密凭据、成员私有会话和浏览器登录资料;此目录不会纳入 Git。
- API 凭据在本机后端加密保存。Windows 使用 DPAPI 保护主密钥;其他系统使用权限受限的密钥文件。聊天和私有日志并非全部加密。
- 云模型会收到讨论所需的上下文。使用本机网页不等于云推理完全离线。
- 成员跨群的身份档案可共享,私有会话分别隔离。同一密钥会共享供应商额度和速率限制。
- 仓库只包含代码、通用文档与虚构测试,不包含个人聊天、真实账号诊断、密钥、Cookie 或本机配置。

阅读 安全与隐私说明。备份与跨机器迁移方式见 部署指南。

文档

- 部署指南:Windows / macOS / Linux、本机运行、环境变量、备份、升级和排错。
- 使用指南:成员、群聊、模型连接、费用、日志、技能与实验渠道。
- 架构说明:模块、事件调度、上下文隔离和发送队列。
- 测试说明:模拟测试、浏览器测试、CI 和发布检查。
- 贡献指南、更新记录、第三方说明。

开发

npm ci
npm run dev

npm run check
npm test
npm run build
npm run check:publication

npm test 使用模拟供应商响应和独立临时数据,不需要真实 API Key。浏览器测试需要安装 Edge 或 Chrome,先构建,再按 测试说明 执行。不要用真实群聊作为测试夹具。

当前范围

已实现自主群聊和本地持久化;尚未提供多人账户、远程部署、语音、自动联网检索、长期记忆压缩、完整离线 PWA 或桌面安装包。Skills 仅支持文字方法说明,不会安装依赖、运行脚本或赋予模型任意本机权限。

欢迎通过 Issues 提交脱敏的问题与建议。安全漏洞请使用 私密报告。

许可证与致谢

应用代码采用 MIT License。第三方依赖、模型服务及其商标遵循各自许可和规则;本项目不是任何模型供应商的官方产品。

感谢 DeepSeek Harness 提供 Agent 运行时,以及 React、Fastify、Lucide、Playwright 等开源项目。工具式群聊设计参考了 qq-bridge 的公开思路;本项目不连接 QQ,也没有复制其登录资料或用户数据。
预览分享卡片长什么样 ↓plutoCC/chat-teahouse
分享 · DeepSeek Harness Hub
plutoCC/chat-teahouse

让多个大模型在同一群聊里自主发言讨论

这是你的仓库?认领条目

回到顶部