{"_id":"@buddhilive/dsh-credentials-local","name":"@buddhilive/dsh-credentials-local","dist-tags":{"alpha":"0.1.2-alpha.3","latest":"0.1.2-alpha.3"},"versions":{"0.1.2-alpha.3":{"name":"@buddhilive/dsh-credentials-local","description":"File-backed credentials provider ($DSH_HOME/.env under the live process environment) for the DeepSeek Harness","version":"0.1.2-alpha.3","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/credentials/credentials-local"},"type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./src/*":"./src/*","./package.json":"./package.json"},"license":"MIT","peerDependencies":{"@buddhilive/dsh-atomic-write":"^0.1.2-alpha.3","@buddhilive/dsh-invariants":"^0.1.2-alpha.3","@buddhilive/dsh-launch-environment":"^0.1.2-alpha.3","@buddhilive/dsh-credentials":"^0.1.2-alpha.3","@buddhilive/dsh-home-paths":"^0.1.2-alpha.3","@deepseek-ai/cordis":"^4.0.2"},"dependencies":{"chokidar":"^4.0.3","yaml":"^2.9.0","@deepseek-ai/schemastery":"^3.18.2"},"devDependencies":{"@buddhilive/dsh-atomic-write":"^0.1.2-alpha.3","@buddhilive/dsh-credentials":"^0.1.2-alpha.3","@buddhilive/dsh-launch-environment":"^0.1.2-alpha.3","@buddhilive/dsh-invariants":"^0.1.2-alpha.3","@deepseek-ai/cordis":"^4.0.2","@buddhilive/dsh-home-paths":"^0.1.2-alpha.3"},"_id":"@buddhilive/dsh-credentials-local@0.1.2-alpha.3","bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","_integrity":"sha512-1Z6l5Cu7EM0XB3uM2n+m3Et+jSV88x+fYfyZRkedYMbiJ2x0lXEMKtzHeXKhjSVth/fwYtAMiV6iFLuIKeyn7g==","_resolved":"C:\\DevDojo\\Buddhi\\buddhi-ai-harness\\dist\\npm\\buddhilive-dsh-credentials-local-0.1.2-alpha.3.tgz","_from":"file:C:/DevDojo/Buddhi/buddhi-ai-harness/dist/npm/buddhilive-dsh-credentials-local-0.1.2-alpha.3.tgz","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-1Z6l5Cu7EM0XB3uM2n+m3Et+jSV88x+fYfyZRkedYMbiJ2x0lXEMKtzHeXKhjSVth/fwYtAMiV6iFLuIKeyn7g==","shasum":"b043b7451dc391869db85b1492246aee673d5b45","tarball":"https://registry.npmjs.org/@buddhilive/dsh-credentials-local/-/dsh-credentials-local-0.1.2-alpha.3.tgz","fileCount":9,"unpackedSize":77644,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCBYkimNpb3miIlyEV8YH2k9mm63JZXJwzJIks2Y+nLHAIhAMNAD7RIYL5OnJlYR3uwCUKdILZDQWm6oijEZFwQU6H3"}]},"_npmUser":{"name":"buddhilive","email":"visitbudkavin@gmail.com"},"directories":{},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-credentials-local_0.1.2-alpha.3_1788165592434_0.11415570023152366"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T08:39:52.261Z","0.1.2-alpha.3":"2026-08-31T08:39:52.575Z","modified":"2026-08-31T08:39:52.797Z"},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"description":"File-backed credentials provider ($DSH_HOME/.env under the live process environment) for the DeepSeek Harness","homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/credentials/credentials-local"},"bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"license":"MIT","readme":"---\ndescription: \"面向用户与维护者的文件型凭据提供方：选择、配置或排查本地凭据存储及其环境分层。\"\nkind: \"package-reference\"\n---\n\n# @buddhilive/dsh-credentials-local\n\n[English](README.md) | 中文\n\n## 概述\n\n`dsh-credentials-local` 是产品默认的本机凭据存储：harness home 下的一个私有文件，存放 API 密钥与其他机密，可由配置界面写入，你自己编辑该文件时也会自动重载。该文件是带版本的文档，含一个存放密钥值的 `refs` 分节和一个存放持久化按插件记录的 `records` 分节，因此授权 grant 或提供方环境值能与密钥一起跨重启保留。密钥来自四个位置，顺序固定：你启动时的环境优先，其次是存储文件，再次是项目和主目录的 `.env` 文件。你保存的密钥会立即生效，即使某个 `.env` 里还留着更旧的密钥。只有你的 OS 用户能读取该文件，而且产品绝不把文件路径交给 agent（智能体）。\n\n## 目录\n\n- [使用本包](#use-this-package)\n- [理解实现](#understand-the-implementation)\n- [进一步探索](#further-exploration)\n- [模型体验](#model-experience)\n- [已知限制与延期工作](#known-limitations-and-deferred-work)\n- [开发备注](#dev-note)\n\n-----\n\n<a id=\"use-this-package\"></a>\n## 使用本包\n\n本包为组合提供本地凭据存储：API 密钥与其他机密只需保存一次，之后每个按名引用它们的请求都会用到。常用路径是显式的：加载存储、通过配置界面或 `ctx.credentials` 保存密钥，然后在需要时由产品解析。\n\n### 何时使用\n\n把它作为默认本地存储：产品的 base 组合会加载它，你通过配置界面保存的密钥会立即生效。当部署必须让提供方密钥远离自身 agent 时选择其他存储——文件权限做不到这一点，因为 agent 的工具进程以你的 OS 用户身份运行（见「谁能读取该文件」）。\n\n### 设置\n\n```yaml\n- name: '@buddhilive/dsh-credentials-local'\n  config:\n    path: /absolute/path/to/.credentials.yaml\n```\n\n| 字段 | 默认值 | 含义 |\n|---|---|---|\n| `path` | `<harness home>/.credentials.yaml` | 凭据文件所在位置 |\n| `dshHome` | `$DSH_HOME` 或 `~/.dsh` | `path` 缺省时使用的 harness home |\n| `watch` | `true` | 文件在磁盘上变化时自动重载 |\n| `debounceMs` | `100` | 变化后等待这么久再重载，单位为毫秒 |\n\n生成的[配置目录](../../../docs/config-catalog.zh.md#buddhilivedsh-credentials-local)是每个受支持字段及其 JSDoc 的穷尽式真源。\n\n### 存储与移除密钥\n\n用 `set` 保存密钥、用 `unset` 移除、用 `describe` 检查密钥是否已配置——与凭据 API 提供的操作相同：\n\n```ts\nimport type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@buddhilive/dsh-credentials'\n\ndeclare const ctx: Context\n\nconst ref = credentialRef('DEEPSEEK_API_KEY')\nawait ctx.credentials.set(ref, 'sk-…')          // save\nawait ctx.credentials.describe(ref)             // { configured, source?, writable } — never the value\nawait ctx.credentials.unset(ref)                // remove\n```\n\n你保存的密钥会被下一个按名引用它的请求使用；`describe` 报告它是否已设置、来自哪里、能否写入——绝不返回值本身。记录也持久化在同一文件中：插件按 `<owner>/<id>` 寻址一条记录，并用 seam 的记录操作（`readRecord`、`describeRecord`、`listRecords`、`modifyRecord`、`deleteRecord`）管理它。\n\n### 密钥从哪里来\n\n密钥按一个固定顺序解析——先有值的位置胜出：\n\n| 位置 | 可写？ | 优先于 |\n|---|---|---|\n| 你启动时的环境（`DEEPSEEK_API_KEY=… dsh`） | 否 | 一切 |\n| 存储文件 | 是（`set`/`unset`） | 两个 `.env` 文件 |\n| 项目的 `.env`（`<invocation cwd>/.env`） | 不在此处 | 主目录 `.env` |\n| 主目录的 `.env`（`$DSH_HOME/.env`） | 不在此处 | 无 |\n\n启动环境优先，因为按次覆盖——`DEEPSEEK_API_KEY=… dsh`、CI 机密、容器 `-e`——代表本次运行的明确意图；它无法从产品内部修改，因此被报告为只读，写入会被拒绝。其他一切来源都输给存储文件，这正是你保存的密钥会立即生效的原因，即使某个 `.env` 里还留着更旧的密钥；没有存储任何东西时，那两个 `.env` 层会参与解析。环境层是启动时拍摄的启动器[环境快照](../../util/launch-environment/README.zh.md)，因此启动之后才导出的变量不会被看到。\n\n### 凭据文件本身\n\n带版本的 YAML 文档，每个键空间一个分节，除此之外别无他物：\n\n```yaml\nversion: 1\n\nrefs:\n  DEEPSEEK_API_KEY: sk-…\n  OPENAI_API_KEY: sk-…\n\nrecords:\n  llm-pi-ai/openai-codex:\n    kind: grant\n    payload:                    # written verbatim; this provider does not interpret it\n      type: oauth\n      access: eyJhbGciOi…\n      refresh: rft_9f8e7d…\n      expires: 1786000000000\n  llm-pi-ai/amazon-bedrock:\n    kind: api-key               # environment values, no key: this route uses an AWS profile\n    env:\n      AWS_PROFILE: prod\n  llm-pi-ai/amazon-bedrock-dev:\n    kind: api-key               # neither: the owner confirmed the ambient credential chain\n```\n\n你可以直接编辑该文件——存储会自动重载并接收变更，包括你删除的密钥或记录。`refs` 按环境变量名存放密钥值；`records` 按 `<owner>/<id>` 存放按插件凭据，每条都带 `api-key` 或 `grant` 标签，其中 grant 的 payload 由存储逐字保留，因为只有它的拥有者能解释。产品写入时会保留注释与未触及条目的排版；直接位于某条目上方的注释属于该条目的注解，会随它一起删除。文件只存放凭据，因此任何其他内容都会被明确拒绝，而不是被静默忽略：非 mapping 的根、未知的顶层键、在其键空间内不可寻址的键、类型错误或空的值、未知的记录标签或字段、重复键以及格式错误的 YAML 都会在启动时失败；运行期热重载时则保留最后可用内容并告警。\n\n密钥的值可以是任意文本，包括多行值——不需要任何引号技巧。空值等于「没有密钥」，这正是文件中的空字符串被拒绝的原因：移除密钥是删除它，而不是把它置空。`grant` 的 payload 必须经受 JSON 往返，进出两个方向都会强制这一点，因此存储会拒绝无法逐字读回的值。如果磁盘上的文件已无法解析，保存会失败，而不是覆盖产品读不懂的内容。\n\n### 谁能读取该文件\n\n只有你的 OS 用户能读取该文件：产品以仅属主可访问的权限创建它，在 POSIX 上还会拒绝加载任何其他用户可读的文件——错误会提示你运行 `chmod 600`。Windows 没有可检查的 mode，因此在那里跳过该检查而不是伪造它。agent 不是另一个用户：它的工具进程以你的身份运行，因此它们读这个文件与读你拥有的任何其他文件毫无二致。产品绝不把文件路径交给 agent，也绝不把文件载入环境，因此要拿到某个值，需要刻意去读一条并未交给 agent 的路径。这是审慎，不是边界：必须让提供方密钥远离自身 agent 的部署无法靠文件权限做到。\n\n### 可能出错的地方\n\n- **启动环境提供的密钥是只读的**——`DEEPSEEK_API_KEY=… dsh` 在本轮运行中优先，保存或移除它都会被拒绝。请先在启动 shell 中清除该变量。\n- **空值无法保存**——存储空字符串会被拒绝；请改为移除密钥。\n- **存储拒绝加载它无法信任的文件**——任何其他用户可读的文件、格式错误的 YAML 或无法到达的路径都会在启动时失败；运行期热重载则保留最后可用内容并告警。\n- **同一时刻的修改都会被保留**——如果你在产品写入的同时编辑文件，你的变更会被并入，而不是被覆盖。\n\n-----\n\n<a id=\"understand-the-implementation\"></a>\n## 理解实现\n\n<details>\n<summary>实现细节——点击展开</summary>\n\n本节解释提供方背后的设计决策，并指出实现它们的代码位置；可观察行为已在[使用本包](#use-this-package)中完整说明。\n\n### 设计理念\n\n- **一套明确的优先级。** 继承环境优先，因为它是本次运行的明确意图且无法从进程内部修改；它之下的所有来源都输给受管存储，因此已存密钥永远不会被陈旧的 `.env` 挤掉。\n- **文档只存放凭据。** 它是带 `refs` 与 `records` 分节的版本化文档，而不是 dotenv 文件：一个 harness 拥有、且绝不物化进环境的存储，不能同时充当用户的环境层，否则会以自己的优先级遮蔽非机密条目。\n- **写入打补丁，重载整体替换。** 行编辑在跨进程写锁下保留注释与未触及条目；重载整体交换解析后的快照，已删除条目绝不在内存滞留。\n- **信任攸关处明确报错。** 启动与重载都会拒绝不可读、无效或可被属主之外读取的文档；失败的活动重载保留最后可用快照并告警，而不是拖垮进程。\n\n### 源码地图\n\n| 文件 | 职责 |\n|---|---|\n| [`src/index.ts`](src/index.ts) | 提供方：层解析、严格文档解析、写锁下的引用与记录写路径、watcher 生命周期、权限检查 |\n| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件（无运行时不变式；事件生命周期约定归 seam 伴生插件） |\n\n### 解析与写入路径\n\n`resolve` 与 `describe` 按优先级顺序读取继承环境快照、已解析文档快照与 `.env` 后备层。`set`/`unset` 排入同一条独占操作链：入口检查提前拒绝（已释放、空值、被环境遮蔽），队列在运行时会重新判定，随后在写锁下执行读-改-写、提交，并恰好触发一次 `credentials/reference-updated`。\n\n`modifyRecord` 走同一条链与同一把锁：它重新读取文档、把当前记录交给变更函数、准入其结果——非空的 api key、能经受 JSON 往返的 grant payload——整体渲染该记录并提交，恰好触发一次 `credentials/record-updated`。并非由产品 CLI 启动的组合只有继承环境这一层。\n\n### 重载生命周期\n\nwatcher 事件或 ready 对账把一次刷新排到同一链条之后。`reconcileFromDisk` 重新检查权限、重读文本，文本有差异时整体替换两个快照，并按变更引用或记录逐个发布事件；与文本缓存一致的内容——包括提供方自己的写入——是 no-op。释放时设置 closed 标志、停止接收事件、关闭 watcher，并等待排队操作结算完毕，确保 teardown 之后不再有任何发布。\n\n### 文档版本化\n\n文档携带 `version: 1`，每次写入都会盖上版本戳。启动时若识别出预发布扁平布局——没有 `version` 的裸引用名 mapping——会在写锁下就地升级，把原始行嵌套到 `refs:` 之下，值、注释与拼写逐字节保留；任何其他无版本形态都会按名拒绝，而不会被当作空存储。运行期热重载绝不迁移：中途恢复的扁平文档会保留最后可用快照，直到下一次启动。\n\n### 诊断信息绝不引用值\n\nYAML 解析器自己的消息会引用出错的那行源码，而在这份文档里那行就是机密本身。因此每条诊断只携带错误码与位置——键名可以安全打印，值不行。\n\n</details>\n\n-----\n\n<a id=\"further-exploration\"></a>\n## 进一步探索\n\n当提供方级约定不够用时阅读以下页面。它们从 seam 约定逐步进入环境快照、原子写入原语与启动期环境层。\n\n- [凭据引用 seam](../credentials/README.zh.md)——`resolve`、`describe`、`set`、`unset`、记录操作与 seam 的更新事件。\n- [凭据子系统参考](../../../docs/subsystems/credentials.zh.md)——`CredentialRef`、按操作解析、对 UI 安全的 `CredentialInfo`、提供方层。\n- [启动环境快照](../../util/launch-environment/README.zh.md)——解析读取的冻结层快照，而非 `process.env`。\n- [原子写入](../../util/atomic-write/README.zh.md)——每次写入所用的写锁与原子替换。\n- [应用启动与 Harness home 各层](../../boot/app-boot/README.zh.md)——产品 CLI 如何把 `.env` 载入快照与 `process.env`。\n\n-----\n\n<a id=\"model-experience\"></a>\n## 模型体验\n\n经由 `ctx.credentials` 的消费方间接生效：消费方拥有存储值所启用的全部模型可见行为。\n\n#### KV Cache 影响\n\n无直接失效；存储值绝不进入请求前缀。\n\n## 已知限制与延期工作\n\n<a id=\"known-limitations-and-deferred-work\"></a>\n\n\n这些限制说明本提供方何时不合适，或何时需要特别的运维注意。它们是当前包约束，不是任务积压。\n\n- **同一引用的并发写入是后写胜出**——写锁加读-改-写让并发写入者不会丢掉彼此的条目，但两个写入者编辑同一个引用时仍以较后的写入为准；没有修订检查。\n- **同 UID 进程可以读取该文档**——文件效果沙箱模式不会拒绝读取，OS 钥匙串提供方仍是延后项。\n- **环境变化不可见**——快照在启动时冻结，因此启动之后 export 的变量既不会进入解析，也不会进入 `describe`；要更换来自环境的凭据需要重启。\n- **原子但不具备崩溃持久性**——继承自 `dsh-atomic-write`；存储在启动时重新读取。\n\n<a id=\"dev-note\"></a>\n### 开发备注\n\n<details>\n<summary>维护者的工作上下文——点击展开</summary>\n\n本开发备注是维护者的工作上下文：开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为与限制以上文和包代码为准。\n\nOS 钥匙串提供方——一种模型进程根本无法读取的存储——是同 UID 限制的延后答案，应当作为平级包与本提供方并列。seam 的接口还为辅助命令与 KMS 后端提供方预留了空间；目前没有任何一种随附。\n\n</details>\n","readmeFilename":"README.zh.md","_rev":"1-6c6a9a89462bec8289d3ae857f321633"}