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

云湖客户端 FLY1919/yunhu-vue

Cordis
FLY191927 天前有提交已提供中文文档扫描:含敏感能力

云湖第三方客户端 · Vue3 + cordis 内核 + Koishi 式控制台 + Satori…

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

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

自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add FLY1919/yunhu-vue
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包yunhu-vue(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/22 06:43:14

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 35 处,展示前 33 条)
  • 发起网络请求
    satori-ws.ts:85
    const req = https.request({
  • 发起网络请求
    satori.ts:62
    const req = https.request({
  • 发起网络请求
    server.ts:79
    const r = https.request({
  • 发起网络请求
    server.ts:131
    const r = https.request({
  • 读取本地文件
    server.ts:153
    const text = existsSync(CONFIG_FILE) ? await readFile(CONFIG_FILE, 'utf8') : DEFAULT_CONFIG
  • 读取本地文件
    server.ts:153
    const text = existsSync(CONFIG_FILE) ? await readFile(CONFIG_FILE, 'utf8') : DEFAULT_CONFIG
  • 写入本地文件
    server.ts:154
    if (!existsSync(CONFIG_FILE)) await writeFile(CONFIG_FILE, DEFAULT_CONFIG, 'utf8')
  • 读取本地文件
    server.ts:154
    if (!existsSync(CONFIG_FILE)) await writeFile(CONFIG_FILE, DEFAULT_CONFIG, 'utf8')
  • 写入本地文件
    server.ts:163
    await writeFile(CONFIG_FILE, body, 'utf8')
  • 读取本地文件
    server.ts:177
    if (!file.startsWith(ROOT) || !existsSync(file)) file = join(ROOT, 'index.html')
  • 读取本地文件
    server.ts:179
    const data = await readFile(file)
  • 读取本地文件
    server.ts:239
    if (!existsSync(keyFile) || !existsSync(certFile)) {
  • 写入本地文件
    server.ts:240
    mkdirSync(CERT_DIR, { recursive: true })
  • 执行外部命令固定命令
    server.ts:241
    execFileSync('openssl', [
  • 发起网络请求
    src/plugins/api.ts:83
    const res = await fetch(base + path, init)
  • 请求外部主机api.qiniu.com
    src/plugins/api.ts:822
    const q: any = await fetch(`https://api.qiniu.com/v4/query?ak=${ak}&bucket=${bucket}`).then(r => r.json())
  • 发起网络请求
    src/plugins/api.ts:822
    const q: any = await fetch(`https://api.qiniu.com/v4/query?ak=${ak}&bucket=${bucket}`).then(r => r.json())
  • 发起网络请求
    src/plugins/api.ts:847
    const res = await fetch(`/up/${host}/`, { method: 'POST', body: fd })
  • 发起网络请求
    src/plugins/config.ts:57
    const res = await fetch('/config', { cache: 'no-store' })
  • 发起网络请求
    src/plugins/config.ts:89
    const res = await fetch('/config', {
  • 发起网络请求
    src/plugins/group-admin.ts:246
    const r = await fetch('/mount-json?' + q.toString(), { headers: { token } }).then((x) => x.json())
  • 发起网络请求
    src/plugins/http.ts:78
    const res = await fetch(finalUrl, {
  • 发起网络请求
    src/plugins/http.ts:118
    const resp = await CapacitorHttp.request({
  • 发起网络请求
    src/plugins/market.ts:76
    const res = await fetch(p.url)
  • 写入剪贴板
    src/plugins/message-actions.ts:106
    await navigator.clipboard.writeText(text)
  • 发起网络请求
    webdav.ts:60
    const req = https.request({
  • 发起网络请求
    webdav.ts:162
    const res = await fetch(`https://${host}/`, { method: 'POST', body: fd })
  • 发起网络请求
    webdav.ts:174
    https.get(url, { headers: { 'User-Agent': 'yunhu-webdav/1.0' } }, (r) => {
  • 读取本地文件
    webdav.ts:229
    if (existsSync(PWD_FILE)) pwdCache = JSON.parse(readFileSync(PWD_FILE, 'utf8') || '{}')
  • 读取本地文件
    webdav.ts:229
    if (existsSync(PWD_FILE)) pwdCache = JSON.parse(readFileSync(PWD_FILE, 'utf8') || '{}')
  • 写入本地文件
    webdav.ts:234
    try { writeFileSync(PWD_FILE, JSON.stringify(pwdCache, null, 2)) } catch { }
  • 发起网络请求
    webdav.ts:277
    const r2 = https.request({
  • 发起网络请求
    webdav.ts:357
    const r2 = https.request({
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
涉及外部主机:api.qiniu.com

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • Vue3界面
  • cordis内核
  • Satori消息层

README

由 DeepSeek 最新模型翻译生成
云湖第三方客户端 · Vue3 + Cordis 内核 + Koishi 式控制台 + Satori 消息层 仅供 学习交流。接口来自社区公开整理: yh-Tpdev/yhchatAPI(在线 <https://yh-api.yyyyt.top>)与 yhwiki。只使用 你自己的账号 token。 全量 TypeScript,tsc --noEmit 0 error。 📘 想写插件?看 docs/plugin-development.md —— 从最小插件到「撤回消息」完整案例。 一、三层结构 ┌───────────────────────────────────────────────┐
展开完整文档(README 剩余 1 节)
云湖第三方客户端 · Vue3 + Cordis 内核 + Koishi 式控制台 + Satori 消息层

仅供 学习交流。接口来自社区公开整理: yh-Tpdev/yhchatAPI(在线 <https://yh-api.yyyyt.top>)与 yhwiki。只使用 你自己的账号 token。

全量 TypeScript,tsc --noEmit 0 error。

📘 想写插件?看 docs/plugin-development.md —— 从最小插件到「撤回消息」完整案例。

一、三层结构

┌───────────────────────────────────────────────┐
│ 控制台 (Koishi 风格) │ │ rail 导航由「插件注册的页面」动态生成 │ │ ctx.page / ctx.slot / ctx.action / ctx.menu │ ├───────────────────────────────────────────────┤ │ Satori 消息层 │ │ Element[] 元素树 · Session · Bot │ │ 适配器负责 云湖 protobuf ⇄ 元素树 │ ├───────────────────────────────────────────────┤ │ Cordis 内核 │ │ Context / plugin / Service / inject / effect │ └───────────────────────────────────────────────┘

1. 内核(src/core/context.ts)

| 机制 | 说明 |
| Context | ctx.extend() 派生;ctx.<service> 直接访问;ctx.root |
| Context.current | 当前正在 apply 的上下文(服务方法里把副作用挂到调用方) |
| Service | 服务基类,构造完成后由框架调 start() 注册 |
| inject | 服务 falsy → 不加载;值变化 → 回滚重载;服务消失 → 回滚挂起,恢复 → 自动重载 |
| ctx.inject(deps, cb) | 语法糖(带 using 的子插件) |
| ctx.effect(fn) | 副作用,随插件卸载自动清理 |
| 稳定服务代理 | ctx.<service> 返回按名字缓存的转发代理:插件重载换实例后,组件缓存的引用依然有效;代理取值会读 app.revision,保证 computed 正确建立依赖 |

2. Satori 消息层(src/satori/)

import { h } from '../satori'

h.text('你好') · h.image(url) · h.at(id, name) · h.a(href, [h.text('链接')])
h.markdown('# 标题') · h.html('x') · h.sticker(url, itemId, packId)
h.file(url, name, size) · h.audio(url, time) · h.video(url) · h.a2ui(json) · h.br()

parse(云湖content, contentType) -> Element[] // 适配器入
render(Element[]) -> { contentType, content } // 适配器出
toText(Element[]) / toHtml(Element[])

ctx.on('message-created', (session) => {
session.channel // { id, type: 1用户 2群 3机器人, name } session.user // { id, name, avatar } session.elements // Element[] session.content // 纯文本
session.send([h.text('收到'), h.image(url)])
})

ctx.bots[0].sendMessage({ id: '418769995', type: 2 }, [h.markdown('hi')])

事件:message-created / message-updated / message-recalled。

3. 控制台(src/console/)

ctx.inject(['console'], (ctx) => {
ctx.console.addListener('chat/summary', () => ({ ... })) // 后端接口
ctx.console.menu('logger', [{ id: 'logger.clear', label: '清空' }])
ctx.console.action('logger.clear', { action: () => logger.clear() })
ctx.console.addEntry((ctx) => { // 客户端入口
ctx.page({ name: '日志', path: '/logs', icon: 'terminal', order: 800, component: LogsPage })
ctx.slot({ type: 'status-left', component: Widget, order: 10 })
})
})

左侧导航完全由已注册页面生成,插件装卸即时增减。页面组件用 <k-layout> / <k-card> / <k-slot> 组装。

二、配置文件(对标 koishi.yml)

yunhu.config.yml(服务端 /config 读写,控制台「配置」页可视化编辑)

app:
title: 云湖 · 第三方客户端 theme: dark pollMs: 4000 plugins: chat: pollMs: 4000 maxMessages: 200 ws: heartbeat: 25000 quickreply: enabled: true
items: [收到 ✅, 稍等一下, 好的]

- 启动时先加载配置,再把 {...app, ...plugins.<key>} 作为 config 传给插件
- 「保存」写回服务器文件;「应用并重载插件」按新配置重挂载并自动恢复登录态
- 实测:改 plugins.quickreply.items → 应用 → 聊天页按钮条立刻变化

三、插件清单

| 插件 | 提供服务 | 控制台页面 | 依赖 |
| config | config | 配置 | — |
| logger | logger | 日志(含菜单动作) | — |
| ui | ui | — | — |
| theme | theme | 主题(配色/壁纸/圆角) | ui |
| console | console | 插件 | — |
| api | api | — | — |
| auth | auth | 账号 | api |
| adapter | bots satori | 事件 | api, auth |
| chat | chat | 聊天 + 状态栏插槽 | api, auth |
| ws | ws | — | api, auth |
| message-actions | messageActions | 消息操作(撤回/多选/转发/引用/全选) | chat, ui |
| group-admin | group | 成员列表 / 踢出 / 禁言 / 看板 / 记录搜索 | api, chat, ui |
| quickreply | quickreply | 快捷回复 | ui, chat |

四、功能

登录(邮箱/Token)· 会话(未读/@/搜索)· 消息收发 · 消息操作(撤回/多选/全选/转发/引用 + 右键菜单) · 群管理(成员/踢出/禁言/看板/记录搜索) · A2UI v0.9 · 视频/语音 · WebSocket 实时 · 资源代理(补 Referer: http://myapp.jwznb.com,解锁图片/表情/音频/视频/文件/头像)· 富消息渲染(文本/图片/Markdown/HTML/文件/表单/文章/表情/语音/通话/A2UI,统一走元素树)· 浅色深色 · 移动端抽屉

五、目录

yunhu-vue/
├── server.ts # 托管 + /api + /res + /config(Node 24 直接跑 TS) ├── docs/plugin-development.md # ★ 插件开发指南(以撤回为完整案例) ├── yunhu.config.yml ├── vite.config.ts ├── tsconfig.json ├── src/ │ ├── core/context.ts # ★ Cordis 内核 │ ├── core/{pb,res,render,util}.ts │ ├── satori/{element,session,index}.ts # ★ Satori 消息层 │ ├── console/ # ★ 控制台(service.ts / Console.vue / components / pages) │ ├── plugins/ # 各插件(ts) │ ├── components/ # 聊天 UI(vue,全部 lang="ts") │ ├── app.ts main.ts env.d.ts ├── test-kernel.mjs test.mjs test-ws.mjs

六、运行

npm install
npm run dev # 开发
npm run build && npm run serve # 生产:HOST=:: PORT=8902 node server.ts
npm run typecheck # vue-tsc
npm run test:kernel # 内核语义测试(无需账号)
YH_EMAIL=… YH_PASS=… npm run test:api && npm run test:ws

调试:控制台里 __yunhu 就是根 Context,可查 __yunhu.app.list() / __yunhu.console.state.pages。

七、加一个带页面 + 消费消息的插件

// src/plugins/hello.ts
import { h } from '../satori'
import HelloPage from '../console/pages/HelloPage.vue'

export const helloPlugin = {
name: 'hello',
inject: { required: ['ui'], optional: ['console'] },
provide: ['hello'],
apply(ctx, config) {
ctx.on('message-created', (s) => {
if (s.content.includes('你好')) s.send([h.text('你好呀 '), h.at(s.user.id, s.user.name)])
})
ctx.set('hello', { hi: () => ctx.ui.toast('hi') })
ctx.inject(['console'], (ctx) => {
ctx.console.addEntry((c) => {
c.page({ name: '你好', path: '/hello', icon: 'bolt', order: 200, component: HelloPage })
})
})
},
}
// src/app.ts 的 registry 里加一行,并在 yunhu.config.yml 的 plugins 下写配置

访问地址

服务端默认绑双栈 HOST=::(IPv6 + IPv4 都能连),端口 8902:

IPv6: http://[2409:8a4c:9e33:4570::9]:8902
IPv4: http://192.168.1.100:8902

只绑 0.0.0.0 的话 IPv6 连不上;要同时支持就设 HOST=::(Linux 下双栈)。

服务端附带能力

| 路径 | 说明 |
| /api/ | 云湖接口反代(绕 CORS,一律 no-store) |
| /res/<host>/<path> | 数据床代理(补 Referer: http://myapp.jwznb.com) |
| /up/<host>/ | 七牛上传代理 |
| /webdav/ | 群网盘 WebDAV 网关(user=群ID 或 all,pass=token) |
| /satori/v1/* | 标准 Satori 协议 HTTP 端点(Bearer token) |
| /satori/v1/ws | Satori 事件推送(WebSocket,手写 RFC6455) |
| /config | 配置文件读写 |

六、控制台页面一览(13 个,全部由插件注册)

| 页面 | 提供插件 | 说明 |
| 聊天 | chat | 会话列表 / 消息 / 富消息 / 引用 / 多选 / 群功能全屏界面 |
| 快捷回复 | quickreply | 输入区按钮条 + 本页管理 |
| 通讯录 | social | 用户 / 群聊 / 机器人 + 好友申请 + 添加 + 创建群聊 |
| 表情 | stickers | 表情包(列表/创建/导入 zip/重命名/删除)+ 个人收藏(置顶/删除)|
| 机器人 | botconsole | 我创建的机器人:编辑 / 重置 Token / 指令 / 创建 |
| 板块 | community | 云湖「文章分区」:创建 / 编辑 / 权限 / 绑定群聊 / 进去看文章 |
| 事件 | adapter | Satori 统一事件流 |
| 日志 | logger | 分级筛选 / 搜索 / 复制 / 清空 |
| 主题 | theme | 配色 / 壁纸 / 圆角,15 套预设 |
| 设置 | group-admin | 账号信息 + 个人资料 |
| 配置 | config | 直接编辑 yunhu.config.yml |
| 插件 | console | 插件清单 + 热装载/卸载(真 cordis Fiber)|
| 账号 | auth | 登录态 / 连接 / 外观 |

七、插件清单(21 个)

内核/基础 cfg logger ui theme console api http auth 协议层 adapter(Satori) ws 业务层 chat message-actions group-admin quickreply charbg
session-event(session/event) social botconsole
community stickers commands

装/卸一个插件,它注册的服务 / 控制台页面 / 导航项 / 聊天菜单 / 输入区按钮 / 插槽 / 气泡渲染器会一起消失或恢复 —— 这是 Koishi 式的核心体验。

八、脚本

npm run preflight # 部署前自检(模板 import / 本地引用 / 服务声明),已接进 build
node scripts/api-coverage.mjs # 生成 docs/api-coverage.md(接口覆盖情况)
npx tsx scripts/test-a2ui.mts # A2UI 解析器单测(5 种形态)
bash deploy.sh # 类型检查 → 自检 → 构建 → 校验资源 → 同步 → 冒烟测试

九、文档

- docs/plugin-development.md —— 插件开发指南(从最小插件到完整案例 + 富消息 + 常见坑清单 + ctx.session/ctx.event)
- docs/cordis-notes.md —— 真 cordis 的实测笔记(哪些原生有、哪些坑)
- docs/api-coverage.md —— 云湖接口覆盖情况(已实现 94 / 文档 234 / 待实现 141)

十、两个端口

http://[2409:8a4c:9e33:4570::9]:8902 日常访问(双栈 HOST=::)
https://[2409:8a4c:9e33:4570::9]:8903 手机录音/录像用(安全上下文,自签证书)

浏览器只在安全上下文(https / localhost)才给 navigator.mediaDevices,所以纯 http 打开时手机上连录音权限弹窗都不会出。服务端会自动自签证书。
预览分享卡片长什么样 ↓FLY1919/yunhu-vue
分享 · DeepSeek Harness Hub
FLY1919/yunhu-vue

云湖第三方客户端 · Vue3 + cordis 内核 + Koishi 式控制台 + Satori…

回到顶部