Pull Request已成功合入, 合并人@openeuler-ci-bot
(感谢 xujing99 的贡献)Welcome To openEuler Community
Hey @xujing99 , thanks for your contribution to the community.
Bot Usage Manual
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands. You can self-configure the PR merge rules for this repository. For more details, please refer to Here.
Contact Guide
If you have any questions, please contact the SIG: sig-intelligence ,
and any of the maintainers: @fromhsc, @gogoxiaoxiao, @huzhengce, @kaitiandu, @leon-wang2021, @liumiao27, @lyhu, @wqianli, @zxstty ,
and any of the committers: @extinctfire, @hypothesiser, @xiangyou_xie, @xujing99 .


门禁正在运行, 您可以通过以下链接查看实时门禁检查结果.
若您对门禁结果含义不清晰或者遇到问题不知如何解决,可参考门禁指导手册
门禁入口及编码规范检查: multiarch/openeuler/trigger/xiaoO/1027


门禁正在运行, 您可以通过以下链接查看实时门禁检查结果.
若您对门禁结果含义不清晰或者遇到问题不知如何解决,可参考门禁指导手册
门禁入口及编码规范检查: multiarch/openeuler/trigger/xiaoO/1028


门禁正在运行, 您可以通过以下链接查看实时门禁检查结果.
若您对门禁结果含义不清晰或者遇到问题不知如何解决,可参考门禁指导手册
门禁入口及编码规范检查: multiarch/openeuler/trigger/xiaoO/1029


/lgtm


/approve


当前PR是否有AI参与:
[ ] 否
[x] 是
__1. AI Agent 平台: opencode
__2. AI 模型: GLM-5.3
__3. Prompt上下文 : NA
参考:openEuler社区《生成式AI工具使用与开源贡献政策》
PR功能描述 / 为什么需要这个合入**:
1.1 修改目的
本项目把大模型的 API 密钥加密存放在
config.toml同目录的llm_secrets.json里,进程启动时再把这些密钥“注入”回环境变量供下游 SDK 使用。设计文档承诺“首次启动时自动把环境变量里的密钥收进加密存储”,但实现这一承诺的函数(auto_save_from_env())在整个代码库里没有任何调用方——自动保存从未真正发生过。本次提交让三个启动入口真正执行它,并顺带把存储写入改造为原子写 + 跨进程加锁,使新引入的启动期写入不会引发撕裂文件和并发覆盖问题。1.2 关键变更点
init_llm_secrets(),daemon / TUI / CLI run 三个启动路径全部改走它,修复自动保存零调用、功能失效的问题。flock排他锁,并发进程不会互相覆盖更新。1.3 实现思路与机制原理
背景:密钥存储由共享库
apps/shared/src/llm_secrets.rs提供,支持两种写入来源——显式的config set-secret命令 / TUI 弹窗,以及文档承诺但从未生效的“首启从环境变量自动保存”。本次的整体方案是“补接线 + 顺手加固”:不造旁路,而是新增引导函数init_llm_secrets(),把既有的“自动保存”和“注入”两个函数按正确顺序串起来,三个启动入口统一换用它;同时把存储写入从裸写升级为原子写 + 文件锁,因为“每次启动都可能写盘”从假设变成了现实。启动链路(以 daemon 为例):
run_daemon()解析配置路径后调用init_llm_secrets()(apps/serverside/src/main.rs:545;TUI 在apps/endside/src/main.rs:81,CLI run 在apps/endside/src/cli/entry.rs:224)。llm.api_key_env指定的变量名(如OPENAI_API_KEY),环境变量有非空值才继续,否则静默跳过。顺序是核心机制:注入会覆盖同名环境变量。若先注入,用户在环境里提供的新 key 会被存储里的旧值盖掉,随后的“首次保存”存不进任何东西。“先保存后注入”从结构上杜绝了这一点。
“仅首次”语义防回滚,一个最小示例:
[llm] api_key_env = "OPENAI_API_KEY"首次启动时环境中
OPENAI_API_KEY=sk-a→ 自动写入加密存储;之后用户通过set-secret显式换成sk-b→ 再启动时 shell 里残留的sk-a被忽略。旧实现复用save_llm_secret()(无条件插入覆盖),有回滚风险;新实现自带存在性检查,且检查与写入在同一锁临界区内完成——否则两步之间插入一个并发的set-secret,仍可能被覆盖。崩溃与并发安全:写入端先写同目录临时文件(
.llm_secrets.json.tmp.<pid>)再rename覆盖,rename 在同一文件系统上是原子的,读者只会看到旧或新的完整文件;三条“读-改-写”路径全程持有基于flock的排他锁(StoreLock,锁文件内容恒为空、仅当句柄用,进程崩溃时内核自动释放,不会死锁);注入 / 查询等只读路径不加锁,靠 rename 原子性读到完整文件,无额外开销。错误分层:配置缺失、环境变量未设或为空都视为正常情况静默跳过(首次无密钥很常见);真实的 I/O 或加解密失败则带阶段上下文向上传播,让启动报错而非带着坏存储继续跑。
与前版差异:旧逻辑三处缺陷分别对应本次三个机制——零调用失效 → 统一入口 + 顺序约束;无条件覆盖有回滚风险 → 仅首次语义;裸写可撕裂、并发丢更新 → 原子写 + 文件锁。
1.4 影响范围/兼容性影响
llm_secrets.json;配置目录新增.llm_secrets.json.lock(内容恒为空)与瞬时的.tmp.<pid>临时文件。该PR关联的issue
fixes #149