DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

FYTXY /

FYTXY/dsh-hindsight-daemon

Verified

DSH 插件:Hindsight 记忆服务的随行守护。DSH 启动时拉起本地 daemon,退出时连同嵌入式 PostgreSQL 一起清理干净,并在设置里提供独立页面(含记忆仓库的新建/切换/删除)。支持 Windows 与 Linux。

★ 2 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@cad6e816

dsh-hindsight-daemon

给 DSH Desktop 用的 Hindsight 本地记忆 daemon 生命周期插件。

DSH 启动时自动把 Hindsight daemon 拉起来,DSH 退出时把它连同它依赖的嵌入式 PostgreSQL 一起停掉 —— 不产生任何常驻进程,也不需要开机自启项、计划任务或后台监视器。 安装完成后在「设置 → Hindsight 记忆」里有一个独立页面,可以看状态、装依赖、启停服务、改配置。

支持 Windows 10/11 与 Linux(x86_64)。Windows 用 PowerShell 脚本安装, Linux 用 scripts/install.sh;两端的进程收尾分别落在 taskkill 与 pg_ctl / pkill 上。


为什么需要这个插件

Hindsight 的 coding-agent 集成有三种模式:cloud、self-hosted、daemon。 选 daemon 时,记忆存在本机(嵌入式 PostgreSQL + pg0),只有"抽取事实"这一步会调用 你自己的 LLM —— 数据不出机器,也不需要任何云端账号。

但官方插件在 daemon 模式下有两个限制,导致它无法自己把 daemon 拉起来:

  1. 它只认进程环境变量。 detectLlm() 读的是 DSH 进程的 process.env (HINDSIGHT_API_LLM_PROVIDER 或 OPENAI_API_KEY / ANTHROPIC_API_KEY / …), 读不到 ~/.hindsight/profiles/<profile>.env 里已经配好的值。检测不到就直接放弃, 日志里留下 daemon mode needs an LLM for fact extraction。
  2. 它只等 12 秒。 会话启动时 seedIfCold 用 DAEMON_WAIT_SESSION_START_MS = 12e3 等待, 且每个进程只尝试一次。而 daemon 冷启动要 ~30 秒(Python 侧 import torch + import sentence_transformers 就要 12~13 秒),根本等不到。

这个插件把"拉起 daemon"这件事从官方插件手里接过来:在 DSH 自己的 Cordis 树里 做,时机更早、不占用任何额外进程,而且退出时能干净收尾。


它是怎么工作的

DSH 启动
  └─ Cordis 加载本插件 (lib/index.js → apply)
       ├─ 注册 /hindsight-daemon/api/* 路由(设置页用)
       └─ 后台 spawn: hindsight-embed -p <profile> daemon start
            └─ daemon (pythonw) 监听 127.0.0.1:9077
                 └─ pg0 拉起嵌入式 PostgreSQL (:5432)

DSH 退出(三条通道,命中任意一条就够)
  ├─ ① Cordis disposer:桌面端 main.js 的 before-quit 会 dispose 整棵树(宽限 5 秒)
  ├─ ② process 'exit' 钩子:进程正常结束 / 被信号终止且运行时有 signal-exit 时
  └─ ③ SIGTERM / SIGHUP / SIGINT 钩子:宿主没注册信号处理器时兜底接管
       └─ 清理动作(全程同步,信号钩子的约束):
            ├─ 掐掉 detached 的 hindsight-embed CLI
            ├─ 按端口找到 daemon PID 并杀掉
            └─ pg_ctl stop -m fast -D <data>   ← 优雅关库,下次不用回放 WAL

三条通道都只是入口,真正干活的是同一段幂等清理(shared.closed 只放行一次)。

实测:apply() 返回耗时约 2 ms(不阻塞 DSH 启动),清理约 500–600 ms, 远在 5 秒宽限之内。冷启动到健康约 20 秒(Linux,模型与依赖已就绪时)。

为什么需要三条通道:不同宿主给的退出保证不一样。

  • Windows 桌面端会先拦下退出再 dispose 树,通道 ① 就够。
  • Linux 上的 dsh CLI 不注册任何信号处理器(实测 process.listenerCount('SIGTERM') 为 0), 被 kill 时进程直接终止,通道 ① 完全没机会跑。
  • 但真实进程里往往已经有别的库(如 signal-exit)注册了信号监听器, 它会在终止前触发 Node 的 exit 事件 —— 所以通道 ② 才是 Linux 上实际生效的那条。 通道 ③ 只在「确实没人管这个信号」时接管,避免抢跑打断宿主的异步收尾。

清理必须显式做三件事,原因都在实测里踩过:

  • hindsight-embed daemon start 这个 CLI 是 detached 的,daemon 被杀后它会把 daemon 再拉起来 —— 所以第一步先掐 CLI。
  • hindsight-embed daemon stop 不会停 PostgreSQL,而且实测要 4 秒,贴着 5 秒宽限,不安全 —— 所以改按端口 PID 强杀 daemon,再用 pg_ctl stop -m fast 单独关库(约 160 ms)。

环境要求

项目 要求
系统 Windows 10 / 11,或 Linux(x86_64)
DSH Desktop 2.0.x(依赖 apply() 返回函数即 disposer 的 Cordis 4 语义)
Node.js ≥ 22(DSH 自身要求;scripts/install-deps.mjs 也用它)
uv 由安装流程检查;缺失时会提示如何安装
磁盘 约 3 GB(Python 依赖 1.5 GB + 本地模型 1.2 GB + 数据库)
模型服务 一个 [OI] 兼容的 LLM 接口(只用于抽取事实)

安装

方式一:一键脚本(推荐)

git clone https://github.com/FYTXY/dsh-hindsight-daemon.git
cd dsh-hindsight-daemon

Windows(PowerShell):

powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1

install.ps1 会追加 insert 块,并接着调用 scripts/install-deps.mjs 安装 Python 依赖与 两个本地模型(想跳过就加 -SkipDeps,改在设置页里点「一键安装」)。

可用的参数:

-Profile <名>    DSH profile 名,默认 desktop
-DshHome <路径>  DSH home 目录,默认 $env:DSH_HOME 或 %USERPROFILE%\.dsh
-SkipDeps        只挂插件,依赖留到设置页里点「一键安装」

Linux:

chmod +x ./scripts/install.sh
./scripts/install.sh                     # 默认装进 web profile
./scripts/install.sh --profile desktop   # 或指定 profile

install.sh 只做一件事:在 profile 的 cordis.patch.yml 里追加 insert 块(写的是本仓库 lib/index.js 的绝对路径,所以 git pull 升级后立即生效)。依赖同样留给设置页。

两个脚本都是幂等的:重复执行不会写入第二个 insert 块。

装完重启 DSH,然后打开 设置 → Hindsight 记忆,填入你自己的模型服务。

方式二:手动挂载

把插件目录放到任意位置,然后在 <DSH home>/profiles/<profile>/cordis.patch.yml 末尾追加:

- insert:
    - id: hindsight-daemon
      name: '/absolute/path/to/dsh-hindsight-daemon/lib/index.js'

注意 name 用正斜杠(Windows 上也写 C:/path/to/...),指向 lib/index.js。之后在设置页点「一键安装」补齐依赖。

方式三:npm

dsh plugin --profile desktop add dsh-hindsight-daemon

package.json 里带了 dsh.bundle.patch,安装后由包自带的 cordis.patch.yml 自动挂载。

三种方式只能选一种。重复挂载会让 /hindsight-daemon/api 前缀路由注册两次, DSH 启动时报 webserver: duplicate prefix route。


设置页

设置 → Hindsight 记忆(导航里排在靠后的位置)包含六块:

每个按钮都有明确反馈:点击后按钮文字立刻变成 保存中… / 启动中… / 切换中… 这类进行中状态并禁用,避免重复点击;操作结果紧跟在底部 保存配置 / 重新探测 两个按钮下方 —— 成功是绿色回执(如「配置已保存」),5 秒后自动消失;失败是红色错误,一直保留到下一次操作成功。

服务状态

显示阶段(已停止 / 启动中 / 运行中 / 安装中 / 异常)、地址与已运行时长, 并提供 启动 / 停止 按钮。每 2 秒刷新一次。

环境检测

分别显示 uv、hindsight-embed、PostgreSQL 与数据库目录的就绪状态。 任何一项缺失都可以用一键安装补齐。探测优先级:设置里填的路径 > 环境变量 > PATH > 常见安装位置。

一键安装

幂等,可以重复点。依次执行五个阶段:

阶段 做什么
check 定位 uv
packages uv tool install hindsight-embed --with hindsight-api --force
profile 创建 / 对齐 hindsight-embed 的 profile(写环境变量)
models 用 venv 里的 Python 通过 huggingface_hub 下载 embedding 与 reranker
verify import torch, sentence_transformers, hindsight_api 自检

--with hindsight-api 这一步很关键:把 API 装进同一个 venv,daemon 启动时才能直接找到 hindsight-api.exe;否则它会退回 uvx hindsight-api@<version>,在临时环境里重新下载一遍 600 MB。

当前记忆

只读,回答「记忆到底存在哪」:数据库目录、占用空间,以及每个记忆库的节点 / 关联 / 文档数。

一个代码仓库对应一个记忆库,按仓库名自动命名,不同项目之间互不可见。这些信息不能在这里修改 —— 位置由下面的「高级」决定,想换地方见「文件都放在哪」。

记忆仓库(Profile)

列出本机所有 profile,标明哪个正在使用,并可以直接新建、切换、删除。

一个 profile 就是一间独立的仓库:自己的数据库、自己的 env(含网关密钥)、自己的日志。 新建只写 env 文件,不建数据库、不启进程 —— 等它被启用、daemon 第一次启动时才建出来, 所以新建本身不占磁盘。启用某个仓库会停掉当前 daemon 再按新仓库拉起(约 30 秒), 原来的仓库原封不动,随时能切回来。

名字只能包含字母、数字、点、下划线和连字符,以字母或数字开头,最多 32 个字符。 只允许切到已经存在的 profile —— 手输一个拼错的名字会静默建出空库,比直接报错更难察觉。

删除:每个仓库右侧(「启用」之后)有删除按钮,点一下会先弹确认框,确认后才会真的删。 删除是不可恢复的,会连数据库目录、env 文件(含网关密钥)、日志和登记表条目一起清掉; 正在使用的那一个按钮是灰的 —— 想删它,先把别的仓库切成当前使用。 删除不会碰其他仓库,也不会停 daemon。

模型服务

字段 说明
Provider 目前走 OI 兼容协议,选 openai 即可
模型名 抽取事实用的模型,例如 gpt-4o-mini
Base URL 兼容网关地址,例如 https://api.example.com/v1
API Key 落盘明文保存;设置页只回显圆点(••••)表示已设置,留空保存表示不修改

填完 Base URL / 模型名 / API Key 之后,点 测试连通性 会立刻用当前草稿发一次最小请求, 直接告诉你通不通、失败原因、耗时和接口实际回显的模型名 —— 不必等到写记忆时才发现配错。 密钥留空时自动回退到已保存的那一份,所以「刚填完还没保存就想测」也能用。 测试只读,不写配置、不影响正在运行的 daemon。

高级

字段 说明
Profile 名 只读,显示当前使用的仓库。要换仓库请用上面的「记忆仓库(Profile)」列表 —— 直接改名等于指向一个全新的空库:旧记忆不会被删除,但也不会跟过来
记忆库命名模板 留空 = 每个代码仓库自动一个库,模板为 coding-agent::{gitProject}。填了会当成字面模板 —— 填 work 就是所有项目共用 work 这一个库
本地端口 daemon 监听端口,默认 9077,改动需重启 DSH
HF 镜像 默认 https://hf-mirror.com,只影响首次下载模型,装好后不再起作用
PyPI 镜像 默认清华源,只影响安装依赖,装好后不再起作用

前两项决定记忆存在哪,后三项只影响下载与监听 —— 所以界面上把危险项排在前并单独加了警示。

记忆库到底叫什么

上游插件按模板算库名,默认模板是 coding-agent::{gitProject},{gitProject} 取 git 仓库根目录名:

当前工作区 推导出的库名
~/projects/my-app(git 仓库) coding-agent::my-app
~/scratch/notes(不是 git 仓库) coding-agent::notes(取目录名)

可用占位符:{gitProject}(git 仓库根目录名,worktree 会追到主仓库)、{project}(当前目录名,不追仓库根)、{harness}、{channel}、{user}。

所以换一个工作区目录,只要它是另一个 git 仓库,就会自动写进另一个库 —— 互不串味,不需要任何配置。库里没东西就是还没在这个仓库里聊过天。

页面底部合并展示插件日志与 daemon 日志(各取尾部),排错时先看这里。


文件都放在哪

内容 路径
插件配置 <DSH home>/hindsight-daemon/config.json
profile 环境变量 ~/.hindsight/profiles/<profile>.env
hindsight 全局配置 ~/.hindsight/coding-agent.json
daemon 日志 ~/.hindsight/profiles/<profile>.log
数据库 ~/.pg0/instances/hindsight-embed-<profile>/data
Python 运行时 <UV_TOOL_DIR>/hindsight-embed(默认 ~/.local/share/uv/tools,Windows 为 %LOCALAPPDATA%\uv\tools)
本地模型 $HF_HOME,未设置则在 ~/.cache/huggingface

上表用 ~ 表示用户主目录:Windows 上是 %USERPROFILE%(C:\Users\<你>),Linux 上是 $HOME。


卸载

powershell -ExecutionPolicy Bypass -File .\scripts\uninstall.ps1
chmod +x ./scripts/uninstall.sh
./scripts/uninstall.sh                     # 默认从 web profile 移除
./scripts/uninstall.sh --profile desktop   # 或指定 profile

只从 cordis.patch.yml 里移除 insert 块(同样会备份)。重启 DSH 后插件不再加载。 Python 依赖、模型与数据库都保留,方便之后重新挂上;要彻底清理请按上面表格手动删除对应目录。


常见问题

首次启动很慢? 正常。首次要下 1.5 GB 依赖 + 1.2 GB 模型,之后冷启动约 30 秒。装好依赖和模型后, 每次启动就只是 Python 导入的开销。

日志里出现 daemon mode needs an LLM for fact extraction? 这是官方 @vectorize-io/hindsight-coding-agents 插件在报怨它自己检测不到 LLM —— 它只看 DSH 进程的环境变量。本插件不依赖那条路径,看到这条可以忽略,只要 daemon 状态是「运行中」即可。

依赖安装到一半失败? 装依赖对网络比较敏感。默认已经走清华 PyPI 镜像和 hf-mirror;如果在公司网络里,把 「PyPI 镜像」和「HF 镜像」改成内网地址,或留空走官方源。装到一半失败可以直接重试, 已下载的轮子有缓存。

端口 9077 被占用? 改「本地端口」并重启 DSH。插件启动前会先探测 /health,如果端口上已经有一个健康的 daemon(可能是你自己手动起的),会直接复用而不是再拉一个。

改了端口 / Profile 名没生效? 这两项要重启 DSH 才生效(要重建 profile 与全局配置)。

DSH 非正常退出(崩溃 / 强杀)后 daemon 还在? 说明清理通道都没跑到(例如被 SIGKILL,任何语言都拦不住)。手动收一次即可:

# Windows
taskkill /F /IM hindsight-embed.exe /T
Get-NetTCPConnection -LocalPort 9077 -State Listen | ForEach-Object { taskkill /F /PID $_.OwningProcess /T }
& "$env:USERPROFILE\.pg0\installation\*\bin\pg_ctl.exe" stop -m fast -D "$env:USERPROFILE\.pg0\instances\hindsight-embed-<profile>\data"
# Linux
pkill -f hindsight-embed
pgrep -f "hindsight" | xargs -r kill -9          # 或按端口:ss -ltnp | grep 9077
"$HOME/.pg0/installation/"*/bin/pg_ctl stop -m fast -D "$HOME/.pg0/instances/hindsight-embed-<profile>/data"

下次启动 DSH 时如果数据库没干净关闭,PostgreSQL 会自己回放 WAL 恢复,不会丢数据。

想看清退出路径到底哪条通道生效? 设置环境变量 DSH_HINDSIGHT_DEBUG=1 再启动 DSH,插件会把钩子注册与清理决策写进 ~/.dsh/hindsight-daemon/debug.log:

退出钩子已注册 pid=58065 SIGTERM=2 SIGINT=2 exit=3
收到 SIGTERM,listeners=2 pid=58065
cleanup(exit) enabled=true stopOnExit=true busy=null

listeners 数量很关键:大于 1 说明宿主/依赖已经有人处理这个信号,信号钩子会让位, 真正的清理走 exit 通道;等于 1 时才是信号钩子自己接管。

升级插件后行为没变? 多半是加载了另一份旧副本。DSH 的 profile 有两条挂载路子 —— dsh.profile.bundles(按包名从 node_modules 解析)和 profile 自己的 cordis.patch.yml (可以写绝对路径)。如果 cordis.patch.yml 里留着指向源码目录的绝对路径,它会覆盖 bundle 那条,于是 dsh plugin add 装的新版本根本没被加载。检查:

grep -rn "hindsight" ~/.dsh/profiles/<profile>/cordis.patch.yml

推荐只留一种:把 name 写成包名 dsh-hindsight-daemon,让版本跟着 dsh plugin add 走。


开发

node --check lib/index.js          # 语法
node scripts/install-deps.mjs      # 在 DSH 之外跑一遍安装流程,便于调试

目录结构:

lib/
  index.js        Cordis 入口:什么时候挂接口、什么时候拉起、什么时候收摊
  lifecycle.js    生命周期:探测 → 启动 → 停止 → 安装 → 保存配置
  api.js          /hindsight-daemon/api/* 路由 + 同源围栏
  config.js       配置读写(原子写、密钥脱敏、扁平化转换)
  client.js       浏览器半边:设置页
  core/
    paths.js      磁盘探测:uv / hindsight-embed / pg_ctl / 数据目录
    daemon.js     健康探测、按端口找 PID、spawn、三段式关闭
    profile.js    profile 创建与 env 行级补丁、全局配置写入
    installer.js  五阶段安装(流式读取子进程输出)
    status.js     状态与日志环形缓冲
scripts/
  install.ps1     Windows 一键安装(写 cordis.patch.yml + 跑依赖安装)
  install.sh      Linux 一键安装(只写 cordis.patch.yml)
  uninstall.ps1   Windows:从 cordis.patch.yml 移除
  uninstall.sh    Linux:从 cordis.patch.yml 移除
  install-deps.mjs 在 DSH 之外执行依赖与模型安装

设计上刻意零运行时依赖:配置自己读写,设置页自己渲染,不引 schemastery / dsh-shared / dsh-api-gateway。原因是这些包在公开 npm 上的版本(0.0.1-rc.1 系列) 与本机 DSH 内置版本(0.2.0-rc.2)不一致,绑定它们会让插件在 DSH 升级后随时崩掉。


许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit cad6e81626f7

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout