Repository navigation
本地网络服务 + vixel:// 深链接 + MCP(HTTP 与 stdio):搜索结果可链接进笔记、AI 客户端可查询 - #6
Merged
Merged
Conversation
…注册,hash 定位活过重建索引 - shared/deeplink.ts:构建/解析纯函数(hash 优先,id 兜底;t 秒级时间点),单元测试 - main/deeplink.ts:macOS open-url(含冷启动队列)+ Windows/Linux 单实例锁 second-instance argv - 解析命中复用快速面板的"在主窗口打开结果"通道(含未加载完的暂存);时间点落到包含它的已索引片段 - 右键菜单 / 检查器新增"拷贝链接""拷贝 Markdown 链接"(网页版隐藏) - electron-builder protocols + dev-bundle.mjs 给开发期 Electron.app 写 CFBundleURLTypes
- src/server/app.ts:把 HTTP 应用从 server/index.ts 抽成 createServerApp(services, 配置), 认证/API/媒体/网页/WS 原样搬移(scripts/server-security.mjs 16 项全过,行为未变) - src/core/mcp/*:无依赖的 MCP 核心(JSON-RPC + initialize/tools),三个只读工具 search_media / get_media / find_similar(结果带 vixel:// 深链接与 http 媒体链接),单元测试 - src/server/mcp.ts:Streamable HTTP 传输(POST JSON、通知 202、GET 405),Bearer 常数时间 比较 + Origin 校验(防 DNS rebinding),单元测试 - 服务器版:VIXEL_MCP_TOKEN(≥16 位)开启 /mcp,docs/server/README.md 中英文档 - 桌面:设置 › 通用 › 本地接口(默认关、127.0.0.1、端口可配;允许局域网访问需口令、绑定 0.0.0.0), MCP 令牌生成/拷贝/重新生成;LAN 地址列表;i18n zh/en - 打包:dist/web 进 extraResources(prepackage 构建网页前端)
- src/cli/mcp.ts:stdin 每行一条 JSON-RPC,响应写 stdout;日志全走 stderr(mcp 命令下 console.log 重定向),库按 CLI 惯例只读打开,工具与 /mcp 完全相同(core/mcp 共用) - README zh/en:Claude Desktop / Cursor 配置片段(Electron 作运行时 + ELECTRON_RUN_AS_NODE=1, 解释 better-sqlite3 ABI 的原因),新特性条目;CHANGELOG Unreleased
实测桌面全链路后修正两处: - core/mcp/tools.ts:音视频的 photo 行是代表帧,path/name 改查 videos 表给源文件 - main/index.ts:t 恰为片段起点(拷贝链接的产物)才用整段(时间轴整段高亮), 其余时间点起点保持精确,播放器按 t seek(之前被吸附到片段起点,t 丢失) - deeplink 成功打开记一条日志;localapi 无口令拒开 LAN 时记警告
…binding) The desktop local API binds 127.0.0.1 without a password and relied on the X-Vixel header against CSRF. A page that rebinds its own domain to 127.0.0.1 is same-origin, can send that header and would read the whole library. Without a password only accept Host localhost / 127.0.0.1 / [::1] (HTTP and WebSocket). Verified live: normal request 200, forged Host 403 for /api and /media.
This was referenced Oct 17, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3(客户端版本增加本地网络服务,把搜索到的文件链接集成到笔记里),并给 AI 客户端(MCP)提供同一套只读查询。三个里程碑各自独立可用、按序提交。
1. 深链接(无需任何网络服务)
vixel://open?hash=<内容哈希>&id=<photoId>(图片)/vixel://open?hash=…&video=<videoId>&t=<秒>(音视频),构建与解析在src/shared/deeplink.ts(纯函数 + 单测)。open。protocol.handle('vixel')只响应渲染进程发起的thumb/image/media/sprite/face请求;OS 转交的外部链接走open-url/ argv,两条路径互不相见,所以沿用vixel://而不是再立一个 scheme(笔记里的链接长得和应用内资源一致,也少占一个系统 scheme)。app.setAsDefaultProtocolClient+ electron-builderprotocols(打包版 Info.plist / 注册表);macOSopen-url(含 app ready 之前的冷启动队列),Windows/Linux 单实例锁 +second-instanceargv 转交(拿不到锁的新实例递完链接即退出)。开发期scripts/dev-bundle.mjs顺手给 Electron.app 写CFBundleURLTypes,npm run dev下也能点链接。t恰为片段起点("拷贝链接"的产物)时用整段(时间轴整段高亮),任意时间点则精确 seek。链接指向的内容不在库里 → 聚焦主窗口 + 双语说明对话框。[文件名 @ 0:42](vixel://…)),网页版隐藏(等价物是下面的 http 链接)。2. 本地接口(桌面内嵌服务器栈)+ /mcp
src/server/index.ts的 HTTP 层抽成src/server/app.ts的createServerApp(services, 配置):认证、/api/*、/media/*、网页前端、WebSocket 原样搬移,服务器版与桌面共用,无复制代码。行为未变:scripts/server-security.mjs16 项全过。127.0.0.1:41721(端口可配);「允许局域网访问」绑定0.0.0.0且强制要求口令(没口令拒绝开启,开启后本机也要登录)—— 手机浏览器即可访问桌面资料库(设置页列出各网卡地址)。dist/web进打包 extraResources(prepackage构建)。VIXEL_MCP_TOKEN,≥16 位),常数时间比较 + Origin 校验(防 DNS rebinding),与会话 cookie 无关、天然免 CSRF。@modelcontextprotocol/sdk是 ESM-only 且带运输层依赖,与 electron-vite 主进程包和 CommonJS 的 tsconfig.server/cli 都不合;我们只需要无状态 initialize/tools,自写核心约 120 行、全部只读、可审计(src/core/mcp/)。search_media(四路融合搜索;返回 id、name、源文件 path、type、score、matchedBy、命中片段时间段、vixel:// 深链接、http 媒体链接)、get_media(详情含 OCR 文字、标签、人物、片段表)、find_similar。不暴露任何写操作。3.
vixel mcp(stdio)src/cli/mcp.ts:stdin 每行一条 JSON-RPC、响应写 stdout、日志全走 stderr(mcp命令下console.log重定向),库按 CLI 惯例只读打开,语义模型尽力加载(没有也能按 OCR/文件名/标签搜)。ELECTRON_RUN_AS_NODE=1(better-sqlite3 按 Electron ABI 构建,普通 node 加载不了;该变量下 Electron 即纯 Node、不弹窗)。打包安装器不含 CLI,文档注明需要源码 checkout(已安装 Vixel.app 的模型会被自动找到)。验证(均实测)
npm run typecheck、npm test(385 项,新增 25 项:链接构建/解析、MCP 协议核心、HTTP 传输令牌/Origin/批量/通知)、npm run test:db、npx tsc --noEmit -p tsconfig.server.json、tsconfig.cli.json全过;npm run test:server:security16/16(锁定重构后行为不变)。--user-data-dir):curl /api/search(带 X-Vixel)返回瀑布视频;不带头 403;LAN 无口令拒绝启动(日志),设口令后绑定0.0.0.0、未登录 401、登录后正常、/login页正常。/mcp:无令牌 401;initialize → 协议版本/能力正确;notifications/initialized→ 202;tools/list → 三个工具;tools/callsearch_media("瀑布")→ waterfall.mp4,含时间段 0–10.36s、vixel://open?hash=…&video=2、httpUrl。open 'vixel://open?hash=…&video=2&t=8'→ 主进程日志[deeplink] opening …,CDP 查渲染进程确认<video src="vixel://media/2">已按 t seek(3 秒后 currentTime=10.36=片尾,自 0 播放只会到 ~3s);hash-only 图片链接同样导航;伪造 hash → "不在库中"对话框路径命中。已知边界
open vixel://可能被路由到别的 Electron(LaunchServices 按 bundle id 挑);打包版 bundle id 唯一,不受影响。/mcp的 http 媒体链接在浏览器里打开仍需会话登录(LAN 模式);MCP 客户端一般只用link(深链接)。electron out/...跑时app.getVersion()是 Electron 版本(dev/打包版正常)。