last30days-skill 首次配置向导中 Digg CLI 的安装路径对齐与 Agent 子进程 PATH 实践
本篇指南基于仓库内的集成问题解决方案文档 digg-cli-agent-path-setup-wizard.md,讲解 last30days-skill 首次运行(NUX)自动安装 digg-pp-cli 时遇到的一个典型集成缺陷:安装器报告"成功"、引擎却因 Agent 子进程 PATH 缺失而静默丢弃 Digg 源。读完本篇,你能理解"已安装"与"引擎可激活"两个概念的差异、shutil.which 作为唯一激活门控的机制,以及配置向导如何通过对齐 printing-press-library 的默认安装目录、细分安装结果分类和 OpenClaw 服务端对等支持,实现诚实、可验证的 CLI 源引导流程。
问题背景:为什么"安装成功"不等于"数据源激活"
last30days-skill 的引擎通过外部 shell out 到 digg-pp-cli 二进制(只读、无需鉴权)来接入 Digg AI 1000 聚类源。该源是否可用,取决于一个非常朴素的判定:引擎子进程能否在 PATH 上按名字解析到这个二进制。
问题出在首次运行的自动安装环节(setup_wizard.py 的 run_auto_setup()):
- 在 Hermes / OpenClaw 这类 Agent 网关中,引擎子进程的 PATH 往往不包含
$HOME/.local/bin,而 printing-press-library npm 0.1.16 起正是把二进制默认装到该目录; - 早期实现使用了已弃用的
@mvanhorn/printing-press包,并且探测的是遗留的~/go/bin回退目录,而非当前的默认安装目录; - OpenClaw 的
setup --openclaw路径完全没有尝试安装 Digg(只覆盖桌面 NUX 场景)。
由此产生的用户可见症状(见 解决方案文档 的 Symptoms 一节):
--diagnose输出的available_sources中没有digg,即使pp-digg或 setup 已经"安装"了 CLI;- 之前跑过
npx @mvanhorn/printing-press-library install digg --cli-only的 Hermes/OpenClaw 用户,会因为探测逻辑不一致而收到虚假的失败或虚假的"now active"提示; - OpenClaw 服务端 setup 从不安装 Digg。
引擎侧的激活门控:shutil.which 是唯一标准
理解修复方案之前,先确认引擎侧的判定逻辑。在 digg.py 中:
CLI_BIN = "digg-pp-cli"
def _is_available() -> bool:
"""True when the digg-pp-cli binary is on PATH."""
return shutil.which(CLI_BIN) is not None
而 pipeline.py 的 available_sources() 中,Digg 的可用性判定同样是:
if which("digg-pp-cli"):
available.append("digg")
也就是说,引擎从不读取"pp-digg skill 是否已安装"这类状态,每一次研究运行都是按名字在 PATH 上 shell out 到 digg-pp-cli。这就是解决方案文档中"Why This Works"一节的核心结论:setup 向导必须镜像引擎自身的判定谓词,而不是发明另一套成功定义。如果 setup 用"某处存在二进制文件"当作成功,而引擎用 shutil.which 判定,两者就会漂移——用户在向导里看到"已激活",--diagnose 却显示源缺席。
修复要点一:固定(Pin)目录安装器与安装命令
setup_wizard.py 中固化了安装器版本与命令:
# Generous timeout: the install shells out to `npx`, which may download the
# Printing Press package and build the Go binary over the network.
DIGG_INSTALL_TIMEOUT = 300
DIGG_CLI_BIN = "digg-pp-cli"
# Pin the catalog installer; matches printing-press-library npm 0.1.16 default
# ($HOME/.local/bin on macOS/Linux).
PRINTING_PRESS_NPM = "@mvanhorn/printing-press-library@0.1.16"
DIGG_INSTALL_CMD = f"npx -y {PRINTING_PRESS_NPM} install digg --cli-only"
三个设计决策值得注意:
- npm 包名固定到 semver 0.1.16:该版本的目录安装器默认安装目录从
$GOPATH/bin迁移到了$HOME/.local/bin。固定版本号保证 setup 向导与 CONFIGURATION.md 中给用户的命令、以及引擎的 PATH 门控三者始终指向同一安装位置; - 只装 CLI(
--cli-only):last30days 把 Digg 内嵌为引擎数据源,而不是引入 pp-digg skill,所以不需要 skill 接线;Hermes/OpenClaw 上差异的只是 focused skill 的接线方式,二进制位置两者完全相同(都是$HOME/.local/bin); - 300 秒超时:安装过程可能涉及 npm 下载与 Go 二进制构建,超时预算按最坏网络条件给足。
CONFIGURATION.md 的数据源表格中对此有明确约定:Digg 的激活条件是"digg-pp-cli on PATH(首次运行 setup 时通过 npx -y @mvanhorn/printing-press-library@0.1.16 install digg --cli-only 自动安装;二进制默认落在 $HOME/.local/bin;Hermes/OpenClaw 的 Agent 子进程必须继承该目录的 PATH,Digg 才能激活)"。
修复要点二:安装结果五分类,杜绝虚假成功
修复后的 _install_digg_cli()(setup_wizard.py)返回四元组 (engine_active, action, stderr, off_path_binary),其中 action 是一个五值分类:
| action | 触发条件 | 引擎是否激活 | 状态文案行为 |
|---|---|---|---|
already_installed |
shutil.which("digg-pp-cli") 一开始就解析成功 |
是 | "Digg CLI already installed (AI-news clusters active)" |
installed |
npx 安装后 shutil.which 解析成功 |
是 | "Installed Digg CLI (... now active)" |
installed_off_path |
二进制在已知安装目录存在且可执行,但不在 PATH 上 | 否 | 给出具体路径与可复制粘贴的 PATH 目录,提示重启 Agent 会话/网关 |
install_failed |
npx 退出码非零或安装后两处探测均未找到 | 否 | 附 digg_stderr,提示手动执行 DIGG_INSTALL_CMD |
no_npx |
系统上找不到 npx |
否 | 提示先安装 Node/npx 再执行安装命令 |
关键约束是:already_installed / installed 只在 shutil.which 解析成功时才允许出现——与 pipeline.available_sources() 的门控完全同谓词。而 installed_off_path 是专门为"磁盘上存在但 PATH 不可见"这种 Hermes/OpenClaw 常见失败模式准备的诚实出口。
候选目录探测:与安装器共享单一来源
探测 off-path 二进制的目录列表没有另起炉灶,而是从 health 模块的共享清单派生(setup_wizard.py):
def _digg_bin_candidate_paths() -> list[Path]:
"""Known install locations for digg-pp-cli (Printing Press library defaults).
Order: current installer default (~/.local/bin), legacy Go bins, Windows
managed dir. ...
"""
from . import health
win_dir = health.windows_printing_press_bin_dir()
candidates: list[Path] = []
for directory in health.installer_bin_dirs():
if win_dir is not None and directory == win_dir:
candidates.append(directory / f"{DIGG_CLI_BIN}.exe")
else:
candidates.append(directory / DIGG_CLI_BIN)
return candidates
而 health.py 中这个清单的定义是:
~/.local/bin(当前安装器默认,macOS/Linux);$GOPATH/bin(若设置了GOPATH,遗留 Go 目录);~/go/bin(遗留 Go 目录);- Windows:
%LOCALAPPDATA%/Programs/PrintingPress/bin,且该目录下的候选名带.exe后缀(windows_printing_press_bin_dir()在非 Windows 上返回None)。
两个探测函数的分工在源码中写得很清楚:
def _digg_on_path() -> Optional[str]:
"""Return digg-pp-cli when the engine would activate Digg (PATH-resolvable)."""
return shutil.which(DIGG_CLI_BIN)
def _digg_off_path_binary() -> Optional[str]:
"""Return digg-pp-cli path from known install dirs when not on PATH."""
for candidate in _digg_bin_candidate_paths():
if candidate.is_file() and os.access(candidate, os.X_OK):
return str(candidate)
return None
注意注释里的一句话:"探测这些目录只用于 setup 验证与诚实的 off-PATH 消息,不用于引擎激活"。这正是"What Didn't Work"第一条的教训——旧逻辑把"某处存在二进制"当作已安装,探测 ~/go/bin 但没有 PATH 可见性,造成假阳性;引擎门控与 setup 验证必须各守其位。
安装流程的状态机
_install_digg_cli() 的完整判定顺序(setup_wizard.py):
_digg_on_path()命中 →already_installed,直接返回;- 未命中则查
_digg_off_path_binary():命中 →installed_off_path(复用之前 pp-digg 的安装,不重复装、也不谎称已激活); - 都未命中才调用
_run_npx_install("digg")执行npx -y @mvanhorn/printing-press-library@0.1.16 install digg --cli-only;该子过程(setup_wizard.py)用shutil.which("npx")解析出 npx 的绝对路径再作为 argv[0] 传入——这是为了修复 Windows 上 PATHEXT 不匹配的问题(裸字符串"npx"不会触发.CMD解析,会以WinError 2失败); - npx 返回非零或异常 →
install_failed并携带 stderr; - npx 返回 0 后重新执行双探测:PATH 上出现 →
installed;只出现在候选目录 →installed_off_path(把 npx 的非致命 stderr 一并记录);两处都没有 →install_failed("install completed but digg-pp-cli was not found")。
第 5 步的"安装后复验"是整个修复的关键:安装器进程退出码为 0 不代表二进制对当前进程可见,必须以引擎自己的门控再做一次判定。
状态文案:off-path 时给出可操作的 PATH 提示
get_setup_status_text()(setup_wizard.py)把五分类渲染成用户可读的文案。installed_off_path 分支会调用 _digg_bin_dir_hint() 把二进制路径折算成可复制粘贴的 PATH 目录($HOME/.local/bin 形式,Windows 用绝对路径),并明确提示"add ... to PATH and restart your agent session/gateway for Digg to activate"——重启 Agent 会话是必要步骤,因为子进程 PATH 在会话启动时继承。
修复要点三:OpenClaw 服务端对等支持
旧实现中 setup --openclaw 完全跳过 Digg 安装,只覆盖桌面 NUX。修复后的 run_openclaw_setup()(setup_wizard.py)在服务端 JSON 探针中执行同一套 _install_digg_cli():
digg_installed, digg_action, digg_stderr, digg_path = _install_digg_cli()
...
payload: Dict[str, Any] = {
"yt_dlp": yt_dlp,
"node": node,
"python3": python3,
"digg_cli": digg_installed,
"digg_action": digg_action,
"keys": keys,
"x_method": x_method,
}
if digg_path:
payload["digg_path"] = digg_path
if digg_action == "install_failed" and digg_stderr:
payload["digg_stderr"] = digg_stderr
JSON 输出字段 digg_cli(引擎是否激活)、digg_action(五分类之一)、可选的 digg_path(off-path 二进制路径)与可选的 digg_stderr(安装失败详情),让 SKILL.md 驱动的模型在服务器场景下也能呈现与桌面 NUX 一致的、诚实的 Digg 状态。
这套模式如何推广到其他 Printing Press 源
同一套"固定安装器 + 双探测 + 五分类"模式在 setup_wizard.py 中被参数化复用到了其他默认开启的 Printing Press 源上:PP_DEFAULT_SOURCES = [("arxiv", "arxiv", "arxiv-pp-cli"), ("techmeme", "techmeme", "techmeme-pp-cli")],由 _install_pp_cli() / install_default_pp_sources() 执行,返回结构与 Digg 完全一致。Trustpilot 则被有意排除在自动安装之外(它默认关闭,属于 INCLUDE_SOURCES opt-in 源,且使用无头 Chrome 收割 cookie,为默认关闭的源预装二进制是浪费);Bright Data 同样是"只报告、不安装"(它消耗用户自己的计费额度,获取与否由用户决定)。这些例外与 Digg 的主路径共同构成 setup_wizard.py 中统一的 action 分类学。
测试覆盖与验证
修复的关键行为都有对应测试用例,集中在 tests/test_setup_wizard.py:
- off-path 检测路径:
digg-pp-cli位于~/.local/bin但不在 PATH 上 → 期望digg_action == "installed_off_path"且不触发 npx(约 L384-L398);npx 安装 rc=0 但二进制落在$HOME/.local/bin且不可见 → 同样判定installed_off_path(约 L405-L424); - 状态文案路径:
installed_off_path的各种情形(含遗留~/go/bin位置、digg_path缺失或为空)在get_setup_status_text()下均生成带 PATH 修复指引的文案(约 L771-L805); - Bright Data 的三态报告(
already_installed/installed_off_path/not_installed)也验证了"引擎门控原样透传、从不夸大 active"的同一原则(约 L892、L931)。
这与解决方案文档 Prevention 一节的测试要求一致:Hermes/OpenClaw 场景要用重定向的 HOME 与 mock 的 PATH 覆盖,OpenClaw 服务端镜像桌面 NUX 时要在 JSON 中加入对应字段。
可复用的预防规则
解决方案文档最后沉淀了四条预防规则,适用于任何新增"CLI 门控数据源"的引导逻辑:
- 对齐上游安装器的默认 bin 目录,并固定 npm semver——安装位置是契约,不是假设;
- 成功消息必须使用与
available_sources()相同的探测谓词(shutil.which),磁盘上存在但 PATH 不可见的情况走独立的 off-PATH 结果分支; - 测试用重定向
HOME+ mock PATH 覆盖 Agent 网关场景;服务端 setup 镜像桌面 NUX 时同步扩展 JSON 字段; - 改动可选源引导前,先在
docs/solutions/下检索digg、setup-wizard、agent-path等关键词——本方案文档(docs/solutions/integration-issues/digg-cli-agent-path-setup-wizard.md)即为此类集成问题的登记入口。
关键文件索引
| 文件 | 作用 |
|---|---|
| setup_wizard.py | 首次运行向导:Digg 安装器常量、候选目录探测、五分类安装流程、OpenClaw JSON 探针 |
| digg.py | Digg 源适配器:shutil.which 可用性门控、search/posts CLI 调用 |
| pipeline.py | available_sources():引擎侧按 which("digg-pp-cli") 激活 Digg |
| health.py | installer_bin_dirs() / windows_printing_press_bin_dir():安装器目录单一来源 |
| test_setup_wizard.py | off-path 检测、状态文案与 OpenClaw 输出的测试覆盖 |
| CONFIGURATION.md | 数据源表格:Digg/arXiv 的 PATH 激活条件与安装命令 |
| SKILL.md、AGENTS.md、HERMES_SETUP.md | Step 0 引导与 CLI 门控源的文档约束 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00