docs: bilingual credentials/settings-consumer documentation, catalogs, and gates
New credentials data-structure page (type-equiv manifested), group README, rewritten llm-deepseek/llm-pi-ai READMEs (dynamic configuration, dict profiles, credential chain), capability-seams/service-role registration, Agent Note (bilingual), demo compositions mounting settings-local + credentials-local with no inline key plumbing, installSettingsSection consumer helper on the settings seam (deduplicating both adapters' wiring), jscpd symmetry markers for the provider twins, runtime-closure additions for python/sdk-runtime, and doc-budget ceilings AGENTS.md 1750→1755 / packages/README.md 850→865 for the structural one-line group rows.
This commit is contained in:
6
packages/credentials/README.i18n.yaml
Normal file
6
packages/credentials/README.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/credentials/README.md
|
||||
README.md: 1d450cbeef84750fa57ca0151563c496aed0ce12
|
||||
README.zh.md: 843230c3cebf35f234d3ad812165b16ea734678b
|
||||
14
packages/credentials/README.md
Normal file
14
packages/credentials/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# credentials/
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
The credential capability seam, as three-package shape dictates (interface / implementation / consumers):
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| [`credentials/`](credentials/README.md) | Abstract `ctx.credentials`: branded `CredentialRef` references, per-operation `resolve`, UI-safe `describe`, fail-loud `set`/`unset`, the `credentials/updated` commit event |
|
||||
| [`credentials-local/`](credentials-local/README.md) | File/environment provider: the live process environment (read-only, wins) layered over `$DSH_HOME/.env` (writable, byte-preserving line edits, hot-reloaded) |
|
||||
|
||||
Configuration files carry *references* to secrets (`apiKeyEnv: DEEPSEEK_API_KEY`), never the secrets: the settings document stays safe to sync and render, and rotating a value touches no configuration. The LLM adapters are the first consumers — they resolve their reference once per model request, which is what makes a key stored moments ago reach the very next request without restarting anything.
|
||||
|
||||
The seam shape leaves room for keyring-, helper-command-, and KMS-backed providers.
|
||||
14
packages/credentials/README.zh.md
Normal file
14
packages/credentials/README.zh.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# credentials/
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
凭据能力 seam,按三包形态的要求组织(接口/实现/消费方):
|
||||
|
||||
| 包 | 角色 |
|
||||
|---|---|
|
||||
| [`credentials/`](credentials/README.md) | 抽象 `ctx.credentials`:品牌化 `CredentialRef` 引用、按操作 `resolve`、对 UI 安全的 `describe`、响亮失败的 `set`/`unset`,以及 `credentials/updated` 提交事件 |
|
||||
| [`credentials-local/`](credentials-local/README.md) | 文件/环境 provider:活跃进程环境(只读、优先)叠加在 `$DSH_HOME/.env`(可写、保字节行级编辑、热重载)之上 |
|
||||
|
||||
配置文件携带的是对机密的*引用*(`apiKeyEnv: DEEPSEEK_API_KEY`),绝不携带机密本身:设置文档可以放心同步与渲染,轮换值不触碰任何配置。LLM 适配器是第一批消费方——它们每次模型请求解析一次引用,正因如此,片刻前存入的密钥无需重启任何组件即可作用于紧随其后的下一次请求。
|
||||
|
||||
seam 形状为 keyring、辅助命令与 KMS 后端的 provider 留有余地。
|
||||
6
packages/credentials/credentials-local/README.i18n.yaml
Normal file
6
packages/credentials/credentials-local/README.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/credentials/credentials-local/README.md
|
||||
README.md: 277c7db02836819e34a5c2db8aaf542c8eeec162
|
||||
README.zh.md: af1b840142d214b8cd8cf59690cb5773fae2e867
|
||||
@@ -32,7 +32,7 @@ External edits publish `credentials/updated` per changed reference after the sna
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly: resolved values authorize LLM adapter requests; the consuming adapter owns every model-visible surface.
|
||||
Indirectly, through the consuming LLM adapters: stored values authorize their provider requests, and the adapter owns every model-visible surface.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
文件型[凭据](../credentials/README.zh.md) provider:两层来源,一条诚实的优先级。
|
||||
文件型[凭据](../credentials/README.md) provider:两层来源,一条诚实的优先级。
|
||||
|
||||
| 层 | 来源 id | 可写 | 优先 |
|
||||
|---|---|---|---|
|
||||
| 活跃进程环境 | `env` | 否 | 恒定优先 |
|
||||
| `$DSH_HOME/.env` 文档 | `file` | 是(`set`/`unset`) | 其余情况 |
|
||||
|
||||
环境优先,因为启动时注入(`DEEPSEEK_API_KEY=… dsh`、CI secrets、加载了仓库 `.env` 的开发 shell)代表本次运行的操作者意图——而它无法从进程内部修改,就必须**可见地**只读:`describe()` 报告 `source: 'env', writable: false`,`set`/`unset` 直接拒绝,而不是写下一个读取方永远看不到的变更。解析实时读取 `process.env`,绝不写回。
|
||||
环境优先,因为启动时覆盖(`DEEPSEEK_API_KEY=… dsh`、CI 机密、加载了仓库 `.env` 的开发 shell)代表本次运行的操作者意图——而它无法从进程内部修改,就必须*可见地*只读:`describe()` 报告 `source: 'env', writable: false`,`set`/`unset` 直接拒绝,而不是写下一个读取方永远看不到的变更。解析实时读取 `process.env`,绝不写回。
|
||||
|
||||
## 配置
|
||||
|
||||
@@ -18,25 +18,25 @@
|
||||
| `path` | `<harness home>/.env` | 凭据文档位置。 |
|
||||
| `dshHome` | `$DSH_HOME` 或 `~/.dsh` | `path` 缺省时使用的 harness home。 |
|
||||
| `watch` | `true` | 热发布外部编辑。 |
|
||||
| `debounceMs` | `100` | watcher 写入沉降窗口。 |
|
||||
| `debounceMs` | `100` | watcher 写入稳定窗口。 |
|
||||
|
||||
## 文档本身
|
||||
|
||||
dotenv 格式,用 `dotenv` 解析;写回用行级编辑器,保留一切不属于本次编辑的字节:`set` 原位改写该键的第一条赋值行(丢弃后续重复行——dotenv 按最后一条生效,重复行会反过来覆盖这次编辑),`unset` 只删除所属行,注释与无关行逐字保留。落盘经 [`dsh-atomic-write`](../../util/atomic-write/README.zh.md),权限 `0600`。
|
||||
dotenv 格式,用 `dotenv` 解析;写回用行级编辑器,保留一切不属于本次编辑的字节:`set` 原位改写该键的第一条赋值行(丢弃后续重复行——dotenv 按最后一条生效,重复行会反过来覆盖这次编辑),`unset` 只删除所属行,注释与无关行逐字保留。落盘经 [`dsh-atomic-write`](../../util/atomic-write/README.md),权限 `0600`。
|
||||
|
||||
值按 dotenv 能逐字读回的最窄样式渲染——裸值,其次单引号(完全字面),再次双引号(仅限无反斜杠,双引号读取会展开转义)。任何样式都无法表示的值、以及已经跨越多个物理行的条目,响亮失败而不是被静默破坏。空的存储值等于不存在(seam 规则)。
|
||||
值按 dotenv 能逐字读回的最窄样式渲染——裸值,其次单引号(完全字面),再次双引号(仅限无反斜杠,双引号读取会展开转义)。任何样式都无法表示的值,以及已经跨越多个物理行的条目,都会响亮失败而不是被静默破坏。空的存储值等于不存在(seam 规则)。
|
||||
|
||||
## 热重载
|
||||
|
||||
外部编辑在快照**整体替换**后按变更引用逐个发布 `credentials/updated`——磁盘上删掉的条目绝不在内存滞留。provider 自己的写入按内容识别,只发布属于该次提交的一个事件。运行期文档不可读时保留最后一份好快照并告警;文件不存在即空存储;启动时不可读则响亮失败。非 POSIX 标识符的键属于被保留的文件内容,seam 无法寻址。
|
||||
外部编辑在快照**整体替换**后按变更引用逐个发布 `credentials/updated`——磁盘上删掉的条目绝不在内存滞留。provider 自己的写入按内容识别,只发布属于该次提交的一个事件。运行期文档不可读时保留最后可用快照并告警;文件不存在即空存储;启动时不可读则响亮失败。非 POSIX 标识符的键属于被保留的文件内容,seam 无法寻址。
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly: resolved values authorize LLM adapter requests; the consuming adapter owns every model-visible surface.
|
||||
经由消费它的 LLM 适配器间接生效:存储的值为适配器的提供方请求授权,每个模型可见面都归适配器所有。
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; credentials never enter a request prefix.
|
||||
无直接失效;凭据绝不进入请求前缀。
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
|
||||
@@ -118,6 +118,9 @@ function upsertLine(text: string | undefined, ref: CredentialRef, line: string |
|
||||
|
||||
/** File-backed credentials provider (`$DSH_HOME/.env`). */
|
||||
export class CredentialsLocal extends Credentials {
|
||||
/* jscpd:ignore-start -- deliberate config-surface and lifecycle symmetry with
|
||||
settings-local (prefer symmetry for parallel values); extracting the shared
|
||||
shape would couple the two providers' teardown semantics across packages. */
|
||||
static Config: z<Config> = z.object({
|
||||
path: z.string(),
|
||||
dshHome: z.string(),
|
||||
@@ -145,6 +148,7 @@ export class CredentialsLocal extends Credentials {
|
||||
private isClosed(): boolean {
|
||||
return this.closed
|
||||
}
|
||||
/* jscpd:ignore-end */
|
||||
|
||||
constructor(ctx: Context, public config: Config) {
|
||||
super(ctx)
|
||||
@@ -162,6 +166,9 @@ export class CredentialsLocal extends Credentials {
|
||||
}
|
||||
await this.loadInitial()
|
||||
if (!this.spec.watch) return
|
||||
/* jscpd:ignore-start -- same watcher discipline as settings-local by design:
|
||||
the serialized-refresh and quiesce-on-dispose shape is the reviewed
|
||||
lifecycle contract, not accidental repetition. */
|
||||
const watcher = chokidarWatch(this.spec.filename, {
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: {
|
||||
@@ -183,6 +190,7 @@ export class CredentialsLocal extends Credentials {
|
||||
this.ctx.logger.warn('credentials-local: watcher error on %s', this.spec.filename)
|
||||
this.ctx.logger.warn(error)
|
||||
})
|
||||
/* jscpd:ignore-end */
|
||||
yield async () => {
|
||||
// Quiesce: stop accepting events, close the watcher, then wait out any
|
||||
// queued or in-flight refresh so nothing publishes after disposal.
|
||||
|
||||
6
packages/credentials/credentials/README.i18n.yaml
Normal file
6
packages/credentials/credentials/README.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/credentials/credentials/README.md
|
||||
README.md: 1c18c4762360ad081227b7097cd82ddab4fcdefc
|
||||
README.zh.md: 751fb7c1e8326cef91b925c5f8b9f40d92e1bba6
|
||||
@@ -13,8 +13,11 @@ Abstract credential seam (`ctx.credentials`). One doctrine, three consequences:
|
||||
## Surface
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
|
||||
declare const ctx: Context
|
||||
|
||||
const ref = credentialRef('DEEPSEEK_API_KEY') // POSIX shell identifier, branded
|
||||
const hit = await ctx.credentials.resolve(ref) // { value, source } | undefined
|
||||
const info = await ctx.credentials.describe(ref) // { configured, source?, writable } — never the value
|
||||
@@ -32,7 +35,7 @@ The shadowing rule on `set`/`unset` is deliberate fail-loud: when a read-only so
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly: a resolved value authorizes provider requests; the consuming adapter owns every model-visible surface.
|
||||
Indirectly, through the consuming LLM adapters: a resolved value authorizes their provider requests, and the adapter owns every model-visible surface.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
|
||||
@@ -4,42 +4,45 @@
|
||||
|
||||
抽象凭据 seam(`ctx.credentials`)。一条准则,三个推论:
|
||||
|
||||
**配置只携带对秘密的引用,绝不携带秘密本身。** settings 分节或 `cordis.yml` 条目写 `apiKeyEnv: DEEPSEEK_API_KEY`,引用背后的值归凭据 provider 所有。于是设置文档可以放心同步、放心渲染进配置界面;`describe()` 无需持有值就能回答"配置了吗、来自哪层、能否写入";轮换秘密不触碰任何配置文件。
|
||||
**配置只携带对机密的引用,绝不携带机密本身。** settings 分节或 `cordis.yml` 条目写 `apiKeyEnv: DEEPSEEK_API_KEY`,引用背后的值归凭据 provider 所有。于是设置文档可以放心同步、放心渲染进配置界面;`describe()` 无需持有值就能回答「配置了吗、来自哪层、能否写入」;轮换机密不触碰任何配置文件。
|
||||
|
||||
**消费方按操作解析。** `resolve(ref)` 在每个操作开始时调用(LLM adapter 每次模型请求解析一次),绝不跨操作缓存——正是这次读取让改过的凭据无需重启任何插件就作用于下一次请求。
|
||||
**消费方按操作解析。** `resolve(ref)` 在每个操作开始时调用(LLM 适配器每次模型请求解析一次),绝不跨操作缓存——正是这次读取让改过的凭据无需重启任何插件就作用于下一次请求。
|
||||
|
||||
**空的存储值等于不存在。** 处处如此:`resolve` 跳过它,`describe` 报告未配置。空白永远不会伪装成已配置的秘密。
|
||||
**空的存储值等于不存在。**处处如此:`resolve` 跳过它,`describe` 报告未配置。空白永远不会伪装成已配置的机密。
|
||||
|
||||
## 接口面
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
|
||||
const ref = credentialRef('DEEPSEEK_API_KEY') // POSIX shell 标识符,品牌类型
|
||||
declare const ctx: Context
|
||||
|
||||
const ref = credentialRef('DEEPSEEK_API_KEY') // POSIX shell identifier, branded
|
||||
const hit = await ctx.credentials.resolve(ref) // { value, source } | undefined
|
||||
const info = await ctx.credentials.describe(ref) // { configured, source?, writable } —— 绝不含值
|
||||
await ctx.credentials.set(ref, 'sk-…') // 被只读来源遮蔽时拒绝
|
||||
await ctx.credentials.unset(ref) // 不存在时为 no-op;同样的遮蔽规则
|
||||
const info = await ctx.credentials.describe(ref) // { configured, source?, writable } — never the value
|
||||
await ctx.credentials.set(ref, 'sk-…') // rejects while a read-only source shadows the ref
|
||||
await ctx.credentials.unset(ref) // no-op when absent; same shadowing rule
|
||||
```
|
||||
|
||||
`credentials/updated (ref)` 在 provider 管理的来源发生已提交变更后触发——`set`、`unset` 或在存储中观察到的外部编辑。进程环境变量的变化不可观测,永不触发。消费方不需要该事件(它们按操作重新解析);它服务于配置界面刷新"已配置"徽标。
|
||||
`credentials/updated (ref)` 在 provider 管理的来源发生已提交变更后触发——`set`、`unset` 或在存储中观察到的外部编辑。进程环境变量的变化不可观测,永不触发。消费方不需要该事件(它们按操作重新解析);它服务于配置界面刷新「已配置」徽标。
|
||||
|
||||
`set`/`unset` 的遮蔽规则是刻意的 fail-loud:当只读来源(本地 provider 中即活跃进程环境)正在提供该引用时,写入会表面成功而解析仍返回遮蔽值——seam 选择直接拒绝,并通过 `describe().writable` 让界面提前把该引用渲染为只读。
|
||||
`set`/`unset` 的遮蔽规则是刻意的响亮失败:当只读来源(本地 provider 中即活跃进程环境)正在提供该引用时,写入会表面成功而解析仍返回遮蔽值——seam 选择直接拒绝,并通过 `describe().writable` 让界面提前把该引用渲染为只读。
|
||||
|
||||
## Providers
|
||||
|
||||
[`dsh-credentials-local`](../credentials-local/README.md) 把活跃进程环境叠加在 `$DSH_HOME/.env` 文件之上。seam 形状为 keyring、辅助命令、KMS 后端的 provider 留好了位置;远端 settings provider 永远不必携带秘密。
|
||||
[`dsh-credentials-local`](../credentials-local/README.md) 把活跃进程环境叠加在 `$DSH_HOME/.env` 文件之上。seam 形状为 keyring、辅助命令、KMS 后端的 provider 留好了位置;远端 settings provider 永远不必携带机密。
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly: a resolved value authorizes provider requests; the consuming adapter owns every model-visible surface.
|
||||
经由消费它的 LLM 适配器间接生效:解析出的值为适配器的提供方请求授权,每个模型可见面都归适配器所有。
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; credentials never enter a request prefix.
|
||||
无直接失效;凭据绝不进入请求前缀。
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **不提供枚举**——seam 只回答被问到的引用;配置界面从 settings schema 得知引用集合,`list()` 没有当前消费者。
|
||||
- **不提供枚举**——seam 只回答被问到的引用;配置界面从 settings schema 得知引用集合,`list()` 没有当前消费方。
|
||||
- **引用限定为环境变量形状**——在有 provider 需要更丰富寻址前,保持单一扁平的 POSIX 标识符命名空间。
|
||||
- **进程环境变化不可见**——不可能为其发事件;界面只能在自身导航时重新读取 `describe()`。
|
||||
|
||||
@@ -47,5 +47,3 @@ export class MemoryCredentials extends Credentials {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoryCredentials
|
||||
|
||||
Reference in New Issue
Block a user