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

记忆守护进程 FYTXY/dsh-hindsight-daemon

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

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

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

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

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

状态速览

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

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

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

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 83 处,展示前 62 条)
  • 读取本地文件
    lib/api.js:124
    const text = readFileSync(profileLogPath(profile), 'utf8')
  • 读取本地文件
    lib/lifecycle.js:121
    if (!existsSync(profileEnvPath(config.profile))) {
  • 读取本地文件
    lib/lifecycle.js:448
    if (!existsSync(profileEnvPath(trimmed))) {
  • 读取本地文件
    lib/config.js:152
    if (!existsSync(path)) return defaultConfig()
  • 读取本地文件
    lib/config.js:154
    return mergeDefaults(JSON.parse(readFileSync(path, 'utf8')))
  • 写入本地文件
    lib/config.js:174
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    lib/config.js:176
    writeFileSync(temp, JSON.stringify(next, null, 2), 'utf8')
  • 写入本地文件
    lib/config.js:177
    renameSync(temp, path)
  • 发起网络请求
    lib/core/llmTest.js:106
    const response = await fetch(url, {
  • 执行外部命令来自配置/变量
    lib/core/profile.js:62
    const result = spawnSync(embedExe, args, {
  • 读取本地文件
    lib/core/profile.js:86
    if (!existsSync(path)) return []
  • 读取本地文件
    lib/core/profile.js:88
    const original = readFileSync(path, 'utf8')
  • 写入本地文件
    lib/core/profile.js:120
    if (changed.length > 0) writeFileSync(path, lines.join('\n'), 'utf8')
  • 写入本地文件
    lib/core/profile.js:134
    mkdirSync(dirname(path), { recursive: true })
  • 读取本地文件
    lib/core/profile.js:137
    if (existsSync(path)) {
  • 读取本地文件
    lib/core/profile.js:139
    current = JSON.parse(readFileSync(path, 'utf8'))
  • 写入本地文件
    lib/core/profile.js:158
    writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
  • 读取本地文件
    lib/core/profile.js:174
    if (!existsSync(path)) return false
  • 读取本地文件
    lib/core/profile.js:176
    for (const line of readFileSync(path, 'utf8').split('\n')) {
  • 读取本地文件
    lib/core/daemon.js:50
    return existsSync(logPath) ? statSync(logPath).size : 0
  • 读取本地文件
    lib/core/daemon.js:50
    return existsSync(logPath) ? statSync(logPath).size : 0
  • 读取本地文件
    lib/core/daemon.js:64
    if (!existsSync(logPath)) return ''
  • 读取本地文件
    lib/core/daemon.js:65
    const size = statSync(logPath).size
  • 发起网络请求
    lib/core/daemon.js:102
    const request = http.get(
  • 执行外部命令来自配置/变量
    lib/core/daemon.js:155
    const child = spawn(embedExe, ['-p', profile, 'daemon', 'start'], {
  • 执行外部命令来自配置/变量
    lib/core/daemon.js:239
    spawnSync(pgCtl, ['stop', '-m', 'fast', '-D', dataDir], {
  • 读取本地文件
    lib/core/daemon.js:267
    if (!existsSync(instancesDir)) return stopped
  • 读取本地文件
    lib/core/daemon.js:271
    entries = readdirSync(instancesDir, { withFileTypes: true })
  • 执行外部命令来自配置/变量
    lib/core/daemon.js:287
    const result = spawnSync(pgCtl, ['stop', '-m', 'fast', '-D', dataDir], {
  • 执行外部命令来自配置/变量
    lib/core/installer.js:43
    const result = spawnSync(uvExe, ['tool', 'dir'], { encoding: 'utf8', windowsHide: true, timeout: 15_000 })
  • 读取本地文件
    lib/core/installer.js:58
    return existsSync(candidate) ? candidate : null
  • 读取本地文件
    lib/core/installer.js:73
    return existsSync(candidate) ? candidate : null
  • 读取本地文件
    lib/core/installer.js:77
    for (const entry of readdirSync(libDir, { withFileTypes: true })) {
  • 读取本地文件
    lib/core/installer.js:81
    if (existsSync(candidate)) return candidate
  • 读取本地文件
    lib/core/installer.js:113
    const embedOk = embedExe !== null && existsSync(embedExe)
  • 读取本地文件
    lib/core/installer.js:122
    const ok = sitePackages !== null && existsSync(join(sitePackages, moduleName))
  • 执行外部命令来自配置/变量
    lib/core/installer.js:180
    child = spawn(command, args, {
  • 执行外部命令来自配置/变量
    lib/core/installer.js:358
    const check = spawnSync(python, ['-c', 'import torch, sentence_transformers, hindsight_api'], {
  • 读取本地文件
    lib/core/memoryInfo.js:46
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    lib/core/memoryInfo.js:58
    total += statSync(full).size
  • 发起网络请求
    lib/core/memoryInfo.js:76
    const response = await fetch(url, { signal: AbortSignal.timeout(HTTP_TIMEOUT_MS) })
  • 读取本地文件
    lib/core/paths.js:55
    if (existsSync(candidate)) return candidate
  • 读取本地文件
    lib/core/paths.js:67
    return readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    lib/core/paths.js:99
    if (explicit !== null && existsSync(explicit)) return explicit
  • 读取本地文件
    lib/core/paths.js:108
    if (existsSync(candidate)) return candidate
  • 读取本地文件
    lib/core/paths.js:112
    if (existsSync(local)) return local
  • 读取本地文件
    lib/core/paths.js:124
    if (explicit !== null && existsSync(explicit)) return explicit
  • 读取本地文件
    lib/core/profiles.js:60
    return readdirSync(dir)
  • 读取本地文件
    lib/core/profiles.js:73
    return statSync(path).size
  • 读取本地文件
    lib/core/profiles.js:106
    if (!existsSync(path)) return { profiles: {} }
  • 读取本地文件
    lib/core/profiles.js:108
    const parsed = JSON.parse(readFileSync(path, 'utf8'))
  • 读取本地文件
    lib/core/profiles.js:161
    envExists: existsSync(envPath),
  • 读取本地文件
    lib/core/profiles.js:165
    dbExists: existsSync(dbPath),
  • 写入本地文件
    lib/core/profiles.js:229
    writeFileSync(path, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8')
  • 执行外部命令来自配置/变量
    lib/core/profiles.js:271
    spawnSync(pgCtl, ['stop', '-m', 'fast', '-D', dbPath], {
  • 删除本地文件
    lib/core/profiles.js:291
    rmSync(path, { recursive, force: true, maxRetries: 3, retryDelay: 120 })
  • 执行外部命令固定命令
    lib/core/platform.js:92
    spawnSync('taskkill', ['/F', '/PID', String(pid), '/T'], {
  • 执行外部命令固定命令
    lib/core/platform.js:120
    spawnSync('taskkill', ['/F', '/IM', `${pattern}.exe`, '/T'], {
  • 执行外部命令固定命令
    lib/core/platform.js:127
    spawnSync('pkill', ['-f', pattern], { timeout: STEP_TIMEOUT_MS })
  • 执行外部命令固定命令
    lib/core/platform.js:148
    output = spawnSync('netstat', ['-ano'], {
  • 执行外部命令固定命令
    lib/core/platform.js:171
    ssOut = spawnSync('ss', ['-tlnp'], {
  • 执行外部命令固定命令
    lib/core/platform.js:191
    const lsofOut = spawnSync('lsof', ['-t', `-i:${port}`, '-sTCP:LISTEN'], {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

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

README

由 DeepSeek 最新模型翻译生成
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 上。 Features - 生命周期即语义:DSH 在 = daemon 在,DSH 关 = 进程全清。不装计划任务、不常驻监视器。 - 三条退出通道:Cordis disposer、process.on('exit')、信号钩子,命中任意一条就能干净收尾,覆盖 Windows 桌面端与 Linux CLI 两种完全不同的退出保证。 - 独立设置页:服务状态与启停、环境检测、一键装依赖、当前记忆占用统计、多仓库(profile)新建/切换/删除、模型服务配置与连通性测试。
展开完整文档(README 剩余 1 节)
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 上。

Features

- 生命周期即语义:DSH 在 = daemon 在,DSH 关 = 进程全清。不装计划任务、不常驻监视器。
- 三条退出通道:Cordis disposer、process.on('exit')、信号钩子,命中任意一条就能干净收尾,覆盖 Windows 桌面端与 Linux CLI 两种完全不同的退出保证。
- 独立设置页:服务状态与启停、环境检测、一键装依赖、当前记忆占用统计、多仓库(profile)新建/切换/删除、模型服务配置与连通性测试。
- 跨平台:Windows 与 Linux 同一套逻辑,只在 lib/core/platform.js 里分叉。
- 零运行时依赖:配置读写、设置页渲染、HTTP 路由全部自己实现,不绑定 DSH 内部包版本。
- 自保护:插件自身初始化失败只会降级(没有设置页),绝不让 DSH 起不来。

为什么需要这个插件

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 树里 做,时机更早、不占用任何额外进程,而且退出时能干净收尾。

How it works

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 ← 优雅关库,下次不用回放 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)。

更细的进程拓扑、通道取舍与调试方法见 docs/architecture.md。

环境要求

| 项目 | 要求 |
| 系统 | 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 + 数据库) |
| 模型服务 | 一个 OpenAI 兼容的 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 | 目前走 OpenAI 兼容协议,选 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 走。

开发

零运行时依赖,不需要 npm install:

npm test # node --test,全套一条命令
npm run check # 对所有入口做 node --check
npm run pack:dry # 检查打包白名单

测试全部不依赖 DSH 与真实 daemon:它们把 HOME / USERPROFILE / DSH_HOME 都指到临时目录,再用最小化的 hooks 实现把浏览器半边跑起来。改动约定见 CONTRIBUTING.md。

单独跑某个文件:

node --test tests/units.test.mjs
node --test tests/packaging.test.mjs

在 DSH 之外调试安装流程:

node scripts/install-deps.mjs

目录结构

lib/
index.js Cordis 入口:什么时候挂接口、什么时候拉起、什么时候收摊 lifecycle.js 生命周期:探测 → 启动 → 停止 → 安装 → 保存配置 api.js /hindsight-daemon/api/* 路由 + Host 白名单 config.js 配置读写(原子写、密钥脱敏、扁平化转换) client.js 浏览器半边:设置页 core/ paths.js 磁盘探测:uv / hindsight-embed / pg_ctl / 数据目录 daemon.js 健康探测、按端口找 PID、spawn、三段式关闭 profile.js profile 创建与 env 行级补丁、全局配置写入 profiles.js profile 列举 / 新建 / 删除(含目录大小缓存) installer.js 五阶段安装(流式读取子进程输出) platform.js 平台分叉:可执行名、venv 目录、进程控制 llmTest.js 连通性测试用的最小请求 memoryInfo.js 数据库目录占用与记忆库统计 status.js 状态与日志环形缓冲 scripts/ install.ps1 / install.sh 一键安装(写 cordis.patch.yml) uninstall.ps1 / uninstall.sh 从 cordis.patch.yml 移除 install-deps.mjs 在 DSH 之外执行依赖与模型安装 tests/ node:test 用例(不需要 DSH) docs/architecture.md 进程拓扑、三条退出通道、取舍理由

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

相关文档

- docs/architecture.md — 进程拓扑、三条退出通道与设计取舍
- CHANGELOG.md — 版本变更
- CONTRIBUTING.md — 开发约定与测试规范
- SECURITY.md — 安全边界与漏洞上报

许可

MIT
预览分享卡片长什么样 ↓FYTXY/dsh-hindsight-daemon
分享 · DeepSeek Harness Hub
FYTXY/dsh-hindsight-daemon

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

这是你的仓库?认领条目

回到顶部