【免费下载链接】Hy-Memory
MemHub(本仓库memhub/目录下的多端内存应用套件)在发布时不仅交付功能代码,还会随包附带一份合规清单:THIRD_PARTY_NOTICES.md逐一记录自身许可证、打包进运行时的第三方依赖及其许可证文本。本文以 THIRD_PARTY_NOTICES.md 为线索,完整解读这份文档的章节结构,并深入其背后由 generate_sbom.py 实现的“从已安装依赖树自动生成、人工禁止手改”的 SBOM/声明体系,说明它如何接入pnpm release:check、运行时打包与公开发布门禁。读完本文,你将理解 MemHub 如何保证“声明与真正随包分发的内容永远一致”,并掌握重新生成、校验与发布该文档的完整操作路径。
一、文档定位:一份“禁止手改”的运行时依赖声明
文档开篇即给出三条关键事实:
- MemHub 自身采用 Apache-2.0 许可,参见仓库根目录的 LICENSE(生成脚本会直接读取该文件内容来判断许可证,见下文第五节);
- 它随包捆绑(bundles)了下述运行时依赖,因此需要向再分发者完整交代这些第三方组件的归属;
- 文档是由
python3 scripts/generate_sbom.py在 2026-10-17T13:52:48Z 从“已安装依赖树”生成的,并明确标注“Do not edit by hand”(禁止手工编辑)。
这最后一条是整份文档的设计核心:它刻意不采用人工维护的依赖清单,而是每次从真实的虚拟环境与包管理器锁文件中重新推导,从而避免声明与实际随包内容“悄悄漂移”(generate_sbom.py 的模块 docstring 原文即指出:“Both artifacts are derived from what is actually installed rather than from a hand-maintained list, so they cannot quietly drift away from the shipped app.”)。
文档还预告了一个重要策略:当前阶段不逐包复制许可证原文,license 列在发布审查(release review)时才填充;在此之前,配套的 SBOM 对每个第三方包记录NOASSERTION(不声明),而不是靠猜。这与《第四部分:NOASSERTION 策略》中源码的“拒绝猜测”逻辑完全对应。
二、内容解读:文档的四个核心章节
1. hy-memory 2.0.4(Apache-2.0,腾讯 Tencent)
文档第一段声明主体是hy-memory 2.0.4,许可证为 Apache-2.0,并附完整许可证文本。其版权声明明确指出:
- 腾讯(Tencent)向社区提供 Hy-Memory;
- Hy-Memory 在 Apache-2.0 之外不施加任何额外限制;
- 该声明仅指腾讯依据 Apache-2.0 公开发布的代码、参数与权重。
从源码看,这段文本不是写死的模板:generate_sbom.py的hy_memory_notice()(L56-L91)会定位apps/core/.venv中已安装的hy_memory-2.0.4.dist-info,从其中的METADATA读取 Name/Version/License-Expression,并读取licenses/LICENSE文件得到版权行与正文;任何一项缺失或不匹配(例如版本不是 2.0.4、许可证不是 Apache-2.0)都会抛出SbomError使生成失败(fail closed)。测试 test_generate_sbom.py 也专门验证了“从 dist-info 取声明”与“缺失 dist-info 必须拒绝”两个行为。
2. MemTensor memmy-agent(adapted,MIT)
文档第二段声明:MemTensor memmy-agent的适配版本,上游固定提交(pinned commit)为25acd5f29fc7df73b943713abe61f1b2f9839887,许可证为 MIT,版权归 MemTensor(2026-present),并附完整 MIT 许可证文本。
随后文档明确划出适配边界(Adaptation boundary):
packages/host-bridge/src/memmy-adapter/claude-code/是 Memmy Claude 集成的源码级适配,为符合 One Memory 的授权与存储契约而被大幅重写;完整的上游/适配边界记录在该包的PROVENANCE.md中。且不随包分发任何 Memmy 品牌、图标、字体或吉祥物资产。
对应仓库实现事实:
- 适配代码位于 memmy-adapter/claude-code/(含
target.ts、install-plan.ts、hook-template.ts、event-adapter.ts、runtime.ts、session-state.ts、recall-trigger.ts等模块); - 上游许可原文保留在 LICENSE.memmy,并由
license.ts在候选安装中嵌入同一份声明; - 详细的上游/适配逐模块对照表(如把上游的“子串删除+目录替换”改为“精确属主条目、预览指纹、冲突拒绝与可逆日志”)记录在 PROVENANCE.md;
- 生成脚本通过
MEMMY_PIN = "25acd5f29fc7df73b943713abe61f1b2f9839887"(L37)固定该提交,并在 SPDX 中为它单独建立SPDXRef-Package-memmy-agent-derived条目,声明 MIT、filesAnalyzed: True,通过CONTAINS关系挂到主包下。
3. JavaScript (npm) — 10 个包
文档以“ecosystem — 数量”作为小节标题,完整列出 10 个运行时 npm 依赖(版本号精确锁定):
| 包名 | 版本 | 包名 | 版本 |
|---|---|---|---|
| @tanstack/react-query | 5.103.2 | axios | 1.20.0 |
| fflate | 0.8.3 | https-proxy-agent | 7.0.0 |
| lucide-react | 1.43.0 | proxy-from-env | 2.1.0 |
| react | 19.2.8 | react-dom | 19.2.8 |
| tar | 7.5.22 | three | 0.185.1 |
从这组清单可以读出 MemHub 桌面端/Web 端的技术栈轮廓:React 19 渲染层、@tanstack/react-query 数据请求、three 用于三维可视化、axios + https-proxy-agent 处理网络代理、tar 用于归档处理。生成侧对应javascript_packages()(L112-L131):它执行pnpm list --prod --depth 0 -r --json获取生产依赖,并主动跳过link:前缀的 workspace 内部链接包,因此不会把 Monorepo 自己的包误报成第三方。
4. Python (PyPI) — 126 个包
文档列出的 PyPI 运行时依赖共 126 个,对应python_packages()(L134-L161)——它用uv pip list --python apps/core/.venv/bin/python --format json读取受管虚拟环境中的全部包,并剔除one-memory与hy-memory两个自有包后按名称排序。按功能可归类如下(版本均以文档原文为准):
- 向量检索与图数据库:
chromadb 1.5.9、kuzu 0.11.3(对应 Hy-Memory 的向量存储与图存储后端); - 机器学习/Embedding 运行时:
numpy、scipy、scikit-learn、onnxruntime 1.23.2、tokenizers 0.23.1、tiktoken 0.14.0、huggingface-hub 1.29.0、hf-xet 1.6.0、mmh3 5.3.0、joblib、threadpoolctl; - 中文分词与检索:
jieba 0.42.1、rank-bm25 0.2.2、langdetect 1.0.9、regex 2026.8.31; - Web/API 服务层:
fastapi 0.141.1、starlette、uvicorn 0.52.4、uvloop、httpx、httpcore、h11、httptools、openai 2.54.0、websockets 17.1、websocket-client、aiohttp 3.14.3及其配套(aiohappyeyeballs、aiosignal、multidict、yarl、frozenlist、propcache); - 数据库驱动:
aiomysql 0.3.2、pymysql 1.2.3、pypika 0.51.1; - 序列化/校验:
orjson、json-repair、protobuf 7.36.0、flatbuffers、pyyaml、pydantic 2.13.5、pydantic-core、pydantic-settings、jsonschema、referencing、rpds-py、attrs、six、annotated-types; - 可观测性(OpenTelemetry):
opentelemetry-api、opentelemetry-sdk、opentelemetry-proto、opentelemetry-exporter-otlp-proto-common、opentelemetry-exporter-otlp-proto-grpc、opentelemetry-semantic-conventions(均为 1.44.0 / 0.65b0); - CLI/日志/工具:
click、typer、rich、coloredlogs、humanfriendly、pygments、markdown-it-py、python-dotenv、keyring、oauthlib、requests-oauthlib、kubernetes 36.0.3、distro、shellingham; - 打包与分发:
pyinstaller 6.22.2、pyinstaller-hooks-contrib、altgraph、macholib、setuptools 84.0.0、build、pyproject-hooks; - 测试与质量:
pytest 9.1.1、pytest-xdist 3.8.0、execnet、mypy 2.3.1、ruff 0.16.5、pluggy、iniconfig、pathspec; - 其他工具链:
tenacity 9.1.4、tqdm、zstandard、pybase64、bcrypt 5.0.0、grpcio 1.83.1、googleapis-common-protos、requests 2.34.2、urllib3 2.7.0、certifi、charset-normalizer、idna、filelock、fsspec、importlib-resources、more-itertools、jaraco-*系列、narwhals、overrides、typing-extensions、typing-inspection、jiter、watchfiles、mpmath、sympy、durationpy、cloudpickle、anyio、sniffio等。
完整的 126 项逐条清单请直接查看 THIRD_PARTY_NOTICES.md 原文。
三、生成机制:generate_sbom.py如何从“已安装依赖树”推导两份工件
memhub/scripts/generate_sbom.py是整份声明的唯一事实来源,一次运行同时产出两个文件(main()):
SBOM.spdx.json(SPDX-2.3 格式的软件物料清单);THIRD_PARTY_NOTICES.md(本文主题文档)。
生成流水线如下:
- 枚举 JS 生产依赖:
pnpm list --prod --depth 0 -r --json(工作区根目录执行),跳过link:workspace 内部包; - 枚举 Python 依赖:
uv pip list --python apps/core/.venv/bin/python --format json,剔除one-memory与hy-memory; - 读取 hy-memory 的已安装 wheel:从
.venv的hy_memory-2.0.4.dist-info读METADATA与licenses/LICENSE,不猜测许可证; - 判定项目自身许可证:
project_license()(L176-L190)直接读取 LICENSE 文本,若包含 “Apache License / Version 2.0, January 2004” 则判定 Apache-2.0,若为 MIT 文本则判定 MIT,否则报错——文档中“命名一个仓库并未携带的许可证,是 SBOM 绝不能犯的错误”正是这段代码的注释; - 拼装 SPDX:每个第三方包获得
SPDXRef-Package-{ecosystem}-{name}唯一 ID 与pkg:npm/...或pkg:pypi/...的 purl 外部引用,主包通过DEPENDS_ON关系指向所有运行时依赖,通过CONTAINS指向 memmy 适配模块; - 两份工件都成功后才落盘:若任一步失败,先前的文件不会被覆盖,避免给发布门禁留下“半更新”的 SBOM。
重生成命令(在memhub/目录下)即 package.json 中定义的脚本(package.json):
pnpm sbom # 等价于 apps/core/.venv/bin/python scripts/generate_sbom.py前置条件:apps/core/.venv已通过uv sync建立、pnpm依赖已安装、Python 版本 ≥ 3.10(脚本入口有显式版本守卫,见 L12-L16)。
四、NOASSERTION 策略与项目级许可证的防猜机制
THIRD_PARTY_NOTICES.md明言:“per-package license texts are not reproduced here yet; the license column is filled in as part of release review, and the SBOM recordsNOASSERTIONuntil then rather than guessing.”
对应的 SPDX 构建逻辑在 build_spdx() 中清晰分层:
- MemHub 自身(one-memory):
licenseConcluded/licenseDeclared来自project_license()的真实读取结果,copyrightText为NOASSERTION; - 126 个 PyPI + 10 个 npm 第三方包:
licenseConcluded、licenseDeclared、copyrightText一律NOASSERTION,代码注释解释这是“解析每个上游许可证文本是独立、需审查的步骤,在此处声明任何许可证都等于猜测”; - hy-memory:许可证与版权来自已安装 wheel 的真实元数据(Apache-2.0);
- memmy 适配模块:MIT 与版权来自固定提交与
LICENSE.memmy。
这套“宁可写 NOASSERTION 也不猜”的策略,将许可证审查刻意保留在发布审查(release review)这个人工环节,避免自动化工具在缺少证据时擅自给出错误的合规结论。
五、发布流水线中的硬门禁:没有声明文件就拒绝发布
THIRD_PARTY_NOTICES.md不是装饰性文档,它被发布检查、运行时打包、公开发布三个环节强制引用:
发布就绪检查(
pnpm release:check):verify_release_ready.py将LICENSE、NOTICE、THIRD_PARTY_NOTICES.md、SBOM.spdx.json四者列为REQUIRED_RELEASE_FILES(L8-L13),任一缺失即输出missing release file: ...并以非零码退出;同时还会拒绝“许可证仍为 pending”的LICENSE-PENDING.md占位文件,并检查 Core 的uv.lock不再依赖腾讯内部 Python 源,防止内部依赖泄漏到公开包。运行时打包:
build_runtime_distribution.cjs(L397-L412)把SBOM.spdx.json、THIRD_PARTY_NOTICES.md直接复制进产物根目录,并把LICENSE→licenses/LICENSE.one-memory、LICENSE.memmy→licenses/LICENSE.memmy、PROVENANCE.md→licenses/PROVENANCE.memmy.md一起随 Runtime 分发;随后在必产物清单中逐一断言这些文件存在(L429-L449)。也就是说,用户拿到的每个 Runtime 包里都物理携带这三份声明。公开发布清单:
build-release-assets.mjs(L159-L179)将SBOM.spdx.json作为role: sbom(spdx-json)、THIRD_PARTY_NOTICES.md作为role: notices(markdown)的发布元数据工件登记,计算 sha256 并对外公布;文件缺失、为空或为符号链接都会抛出release_metadata_invalid。公开发布门禁二次校验:
verify_public_release_ready.py(L254-L267)还会重新解析SBOM.spdx.json:JSON 无法解析则记录sbom_invalid阻断项;若包名集合中出现hy-memory-internal这类内部依赖则记录sbom_contains_internal_dependency阻断项——确保公开清单里绝不出现内部包名。README 官方指引:仓库 README.md 的许可证一节同样指向 THIRD_PARTY_NOTICES.md 与 SBOM.spdx.json,作为第三方归属与许可证的唯一权威出处。
六、可复现性与测试保障
THIRD_PARTY_NOTICES.md头部的生成时间戳2026-10-17T13:52:48Z默认取自datetime.now(timezone.utc),但生成脚本支持环境变量SOURCE_DATE_EPOCH(L356-L369):设置后将从该 Unix 秒数换算 UTC 时间,从而支持可复现的发布构建(reproducible build);值为负数或非法时生成直接失败。对应测试test_source_date_epoch_drives_reproducible_release_timestamp验证了该行为(test_generate_sbom.py)。
围绕生成器,test_generate_sbom.py 提供四组回归测试:
test_hy_memory_notice_comes_from_the_installed_dist_info:构造临时hy_memory-2.0.4.dist-info,验证声明名称/版本/许可证/版权均来自其中真实元数据;test_hy_memory_notice_refuses_missing_dist_info:dist-info 缺失时必须抛SbomError(fail closed);test_script_has_a_modern_python_guard_before_type_annotations:保证 Python 3.10 版本守卫先于Path | None等新语法出现,避免旧解释器在解析阶段就崩溃;test_source_date_epoch_drives_reproducible_release_timestamp:验证时间戳可复现路径。
此外,构建侧测试(如 build_runtime_distribution.test.cjs)断言打包产物必然包含SBOM.spdx.json与THIRD_PARTY_NOTICES.md,build-release-assets.test.mjs 验证发布清单中两份文件的 sha256 与内容一致性。
七、对开发者与使用者的实践建议
- 作为再分发者:若你基于 MemHub 构建衍生发行版,Apache-2.0 要求保留版权与归属声明。请沿用
THIRD_PARTY_NOTICES.md+SBOM.spdx.json+licenses/目录的完整随包结构,尤其不要遗漏LICENSE.memmy与PROVENANCE.memmy.md,因为 memmy 适配模块的 MIT 归属与适配边界声明都在其中。 - 作为 MemHub 维护者:任何依赖变更后应运行
pnpm sbom重新生成两份工件,再执行pnpm release:check验证发布就绪;生成前的uv sync与pnpm install是必要前置步骤,因为脚本坚持从真实安装树而非手工清单推导。 - 作为审查者:第三方包的
NOASSERTION条目应在发布审查时逐包填充许可证结论,但禁止在填充前以猜测方式把NOASSERTION替换成具体许可证——这是生成器刻意保留的人工把关点。
综上,THIRD_PARTY_NOTICES.md 表面上是一份法务声明,实质上是一套由 generate_sbom.py 驱动、被发布检查与打包脚本强制执行的“依赖归属可追溯”工程机制:声明永远来自真实安装树,未知信息宁可留白(NOASSERTION)也不猜测,最终让随包分发的每一份许可证文本都有源码级出处可查。
【免费下载链接】Hy-Memory
相关推荐
libnvidia-container 第三方依赖与许可证合规全解析:从 THIRD_PARTY_NOTICES.md 读懂构建系统的依赖管理
libnvidia container 第三方依赖与许可证合规全解析:从 THIRD_PARTY_NOTICES.md 读懂构建系统的依赖管理 本篇技术指南以
容器运行时云原生openJiuwen 代码许可证治理指南:贡献白名单、第三方依赖许可与特殊许可证评审机制
openJiuwen 代码许可证治理指南:贡献白名单、第三方依赖许可与特殊许可证评审机制 openJiuwen 社区遵循 Open Source Definit
文档开源治理Blender 第三方许可证全景解读:从依赖合规清单到 `make license` 自动化生成机制
Blender 第三方许可证全景解读:从依赖合规清单到 make license 自动化生成机制 本文以 release/license/license.md
图形学3D渲染桌面应用音视频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考