mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
feat(credentials): abstract credential seam (ctx.credentials)
References-not-values doctrine: settings carry env-shaped CredentialRefs, providers own storage. Per-operation resolve, UI-safe describe, fail-loud set/unset under read-only shadowing, credentials/updated commit event with a live-service invariant.
This commit is contained in:
45
packages/credentials/credentials/README.md
Normal file
45
packages/credentials/credentials/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# dsh-credentials
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Abstract credential seam (`ctx.credentials`). One doctrine, three consequences:
|
||||
|
||||
**Configuration carries references to secrets, never the secrets.** A settings section or `cordis.yml` entry says `apiKeyEnv: DEEPSEEK_API_KEY`; the value behind that reference lives with a credential provider. So the settings document stays safe to sync and to render in a configuration UI, `describe()` can answer "is this configured, where from, can I write it" without ever holding a value, and rotating a secret touches no configuration file.
|
||||
|
||||
**Consumers resolve per operation.** `resolve(ref)` is called at the start of each operation (the LLM adapters resolve once per model request) and never cached across operations — that read is what makes a changed credential reach the very next request without restarting any plugin.
|
||||
|
||||
**An empty stored value is absent.** Everywhere: `resolve` skips it, `describe` reports it unconfigured. A blank can never masquerade as a configured secret.
|
||||
|
||||
## Surface
|
||||
|
||||
```ts
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
|
||||
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
|
||||
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)` fires after a committed change to a provider-managed source — a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Consumers do not need the event (they re-resolve per operation); it exists for configuration UIs refreshing a "configured" badge.
|
||||
|
||||
The shadowing rule on `set`/`unset` is deliberate fail-loud: when a read-only source (the live process environment, in the local provider) currently supplies the reference, a write would appear to succeed while resolution keeps returning the shadowing value — the seam rejects instead, and `describe().writable` lets a UI render the reference read-only up front.
|
||||
|
||||
## Providers
|
||||
|
||||
[`dsh-credentials-local`](../credentials-local/README.md) layers the live process environment over a `$DSH_HOME/.env` file. The seam shape leaves room for keyring-, helper-command-, and KMS-backed providers; a remote settings provider never needs to carry secrets.
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly: a resolved value authorizes provider requests; the consuming adapter owns every model-visible surface.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; credentials never enter a request prefix.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **No enumeration** — the seam answers questions about references it is given; configuration surfaces learn the references from settings schemas, so a `list()` has no current consumer.
|
||||
- **References are environment-variable-shaped** — one flat POSIX-identifier namespace until a provider needs richer addressing.
|
||||
- **Process-environment changes are invisible** — no event can fire for them; a UI only re-reads `describe()` on its own navigation.
|
||||
45
packages/credentials/credentials/README.zh.md
Normal file
45
packages/credentials/credentials/README.zh.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# dsh-credentials
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
抽象凭据 seam(`ctx.credentials`)。一条准则,三个推论:
|
||||
|
||||
**配置只携带对秘密的引用,绝不携带秘密本身。** settings 分节或 `cordis.yml` 条目写 `apiKeyEnv: DEEPSEEK_API_KEY`,引用背后的值归凭据 provider 所有。于是设置文档可以放心同步、放心渲染进配置界面;`describe()` 无需持有值就能回答"配置了吗、来自哪层、能否写入";轮换秘密不触碰任何配置文件。
|
||||
|
||||
**消费方按操作解析。** `resolve(ref)` 在每个操作开始时调用(LLM adapter 每次模型请求解析一次),绝不跨操作缓存——正是这次读取让改过的凭据无需重启任何插件就作用于下一次请求。
|
||||
|
||||
**空的存储值等于不存在。** 处处如此:`resolve` 跳过它,`describe` 报告未配置。空白永远不会伪装成已配置的秘密。
|
||||
|
||||
## 接口面
|
||||
|
||||
```ts
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
|
||||
const ref = credentialRef('DEEPSEEK_API_KEY') // POSIX shell 标识符,品牌类型
|
||||
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;同样的遮蔽规则
|
||||
```
|
||||
|
||||
`credentials/updated (ref)` 在 provider 管理的来源发生已提交变更后触发——`set`、`unset` 或在存储中观察到的外部编辑。进程环境变量的变化不可观测,永不触发。消费方不需要该事件(它们按操作重新解析);它服务于配置界面刷新"已配置"徽标。
|
||||
|
||||
`set`/`unset` 的遮蔽规则是刻意的 fail-loud:当只读来源(本地 provider 中即活跃进程环境)正在提供该引用时,写入会表面成功而解析仍返回遮蔽值——seam 选择直接拒绝,并通过 `describe().writable` 让界面提前把该引用渲染为只读。
|
||||
|
||||
## Providers
|
||||
|
||||
[`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.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
No direct invalidation; credentials never enter a request prefix.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **不提供枚举**——seam 只回答被问到的引用;配置界面从 settings schema 得知引用集合,`list()` 没有当前消费者。
|
||||
- **引用限定为环境变量形状**——在有 provider 需要更丰富寻址前,保持单一扁平的 POSIX 标识符命名空间。
|
||||
- **进程环境变化不可见**——不可能为其发事件;界面只能在自身导航时重新读取 `describe()`。
|
||||
39
packages/credentials/credentials/package.json
Normal file
39
packages/credentials/credentials/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-credentials",
|
||||
"description": "Abstract credential seam (ctx.credentials): settings carry references to secrets, providers own the values",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"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"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/types/**/*.d.ts",
|
||||
"lib/types/**/*.d.ts.map",
|
||||
"src"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-brand": "^0.0.1",
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-brand": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
}
|
||||
}
|
||||
114
packages/credentials/credentials/src/index.ts
Normal file
114
packages/credentials/credentials/src/index.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Credential seam (`ctx.credentials`). Settings and composition files carry
|
||||
* *references* to secrets — environment-variable names — while providers own
|
||||
* the actual values and their storage. Consumers resolve a reference once per
|
||||
* operation, so a changed credential reaches the next operation without any
|
||||
* plugin restart, and configuration surfaces describe a reference without
|
||||
* ever seeing its value.
|
||||
* @module @deepseek-ai/dsh-credentials
|
||||
*/
|
||||
|
||||
import { Context, Service } from 'cordis'
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
|
||||
/** Nominal reference to one credential: a POSIX-style environment-variable name. */
|
||||
export type CredentialRef = Branded<'CredentialRef'>
|
||||
|
||||
const REF_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/
|
||||
|
||||
/**
|
||||
* Brand a raw string as a {@link CredentialRef}.
|
||||
* @param value - candidate reference; a POSIX shell identifier such as `DEEPSEEK_API_KEY`.
|
||||
* @returns the branded reference.
|
||||
*/
|
||||
export function credentialRef(value: string): CredentialRef {
|
||||
if (!REF_PATTERN.test(value)) {
|
||||
throw new TypeError(`credential ref "${value}" must match ${String(REF_PATTERN)}`)
|
||||
}
|
||||
return value as CredentialRef
|
||||
}
|
||||
|
||||
/** One resolved credential value and the source layer that supplied it. */
|
||||
export interface ResolvedCredential {
|
||||
/** The non-empty secret value. */
|
||||
value: string
|
||||
/** Provider-defined source layer id (the local provider uses `env` and `file`). */
|
||||
source: string
|
||||
}
|
||||
|
||||
/** Source and writability facts for one reference, safe for configuration UIs — never the value. */
|
||||
export interface CredentialInfo {
|
||||
/** Whether {@link Credentials.resolve} would currently return a value. */
|
||||
configured: boolean
|
||||
/** Source layer currently supplying the value; absent while unconfigured. */
|
||||
source?: string
|
||||
/** Whether {@link Credentials.set} would currently succeed for this reference. */
|
||||
writable: boolean
|
||||
}
|
||||
|
||||
declare module 'cordis' {
|
||||
interface Context {
|
||||
credentials: Credentials
|
||||
}
|
||||
|
||||
interface Events {
|
||||
/**
|
||||
* Committed change to a provider-managed credential source: a `set`, an
|
||||
* `unset`, or an external edit observed in storage. Ambient
|
||||
* process-environment changes are not observable and never emit.
|
||||
* @param ref - the reference whose stored value changed.
|
||||
* @mode emit
|
||||
*/
|
||||
'credentials/updated'(ref: CredentialRef): void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract credential service. Providers implement the four operations over
|
||||
* their source layers; one seam-wide rule binds them all: an empty stored
|
||||
* value is absent everywhere — `resolve` skips it, `describe` reports it
|
||||
* unconfigured — so a blank never masquerades as a configured secret.
|
||||
*/
|
||||
export abstract class Credentials extends Service {
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'credentials')
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve one reference to its current value. Resolution is per call:
|
||||
* consumers re-resolve at each operation and must not cache across
|
||||
* operations — that per-operation read is what makes a changed credential
|
||||
* reach the next operation without a restart.
|
||||
* @param ref - the reference to resolve.
|
||||
* @returns the value and its source, or `undefined` while unconfigured.
|
||||
*/
|
||||
abstract resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined>
|
||||
|
||||
/**
|
||||
* Describe one reference for configuration surfaces without exposing the
|
||||
* value.
|
||||
* @param ref - the reference to describe.
|
||||
* @returns configured state, supplying source, and writability.
|
||||
*/
|
||||
abstract describe(ref: CredentialRef): Promise<CredentialInfo>
|
||||
|
||||
/**
|
||||
* Durably store one value in the provider-managed writable source. Rejects
|
||||
* while a read-only source shadows the reference — the write would appear
|
||||
* to succeed while resolution keeps returning the shadowing value — and
|
||||
* rejects an empty value (use {@link unset}).
|
||||
* @param ref - the reference to store.
|
||||
* @param value - the non-empty secret value.
|
||||
*/
|
||||
abstract set(ref: CredentialRef, value: string): Promise<void>
|
||||
|
||||
/**
|
||||
* Remove one reference from the provider-managed writable source; removing
|
||||
* an absent reference is a no-op. Rejects while a read-only source shadows
|
||||
* the reference, like {@link set}.
|
||||
* @param ref - the reference to remove.
|
||||
*/
|
||||
abstract unset(ref: CredentialRef): Promise<void>
|
||||
}
|
||||
|
||||
export default Credentials
|
||||
38
packages/credentials/credentials/src/invariant.ts
Normal file
38
packages/credentials/credentials/src/invariant.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Package-owned invariant companion for `@deepseek-ai/dsh-credentials`.
|
||||
* @module @deepseek-ai/dsh-credentials/invariant
|
||||
*/
|
||||
|
||||
import type { Context } from 'cordis'
|
||||
import type { InvariantFailure, InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-credentials'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'credentials-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* Install the commit-event lifecycle contract: `credentials/updated` names a
|
||||
* committed provider-source change, so it can only fire while a credentials
|
||||
* service is live — an emission after disposal means a provider leaked work
|
||||
* past its teardown quiescence. The value relation itself (`describe`
|
||||
* agreeing with `resolve`) is asynchronous provider I/O and stays pinned by
|
||||
* each provider's own suite.
|
||||
*/
|
||||
const install: InvariantInstaller = (ctx: Context, fail: InvariantFailure) => {
|
||||
ctx.on('credentials/updated', (ref) => {
|
||||
if (ctx.get('credentials') === undefined) {
|
||||
fail(`credentials/updated for "${ref}" emitted without a live credentials service`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this package's invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
71
packages/credentials/credentials/tests/credentials.spec.ts
Normal file
71
packages/credentials/credentials/tests/credentials.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { credentialRef } from '../src/index.ts'
|
||||
import type { CredentialRef } from '../src/index.ts'
|
||||
import { MemoryCredentials } from './memory.ts'
|
||||
|
||||
const REF = credentialRef('DEEPSEEK_API_KEY')
|
||||
|
||||
async function boot(seed: Record<string, string> = {}): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(MemoryCredentials, seed)
|
||||
return ctx
|
||||
}
|
||||
|
||||
describe('credentialRef', () => {
|
||||
it('brands POSIX shell identifiers', () => {
|
||||
expect(credentialRef('DEEPSEEK_API_KEY')).toBe('DEEPSEEK_API_KEY')
|
||||
expect(credentialRef('_private')).toBe('_private')
|
||||
expect(credentialRef('lower_case9')).toBe('lower_case9')
|
||||
})
|
||||
|
||||
it('rejects every other shape', () => {
|
||||
for (const invalid of ['', '9LEADING', 'WITH-DASH', 'WITH SPACE', 'ns:key']) {
|
||||
expect(() => credentialRef(invalid)).toThrow(TypeError)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('the credentials seam through the memory provider', () => {
|
||||
it('mounts as ctx.credentials and resolves a seeded reference with its source', async () => {
|
||||
const ctx = await boot({ DEEPSEEK_API_KEY: 'sk-seeded' })
|
||||
expect(await ctx.credentials.resolve(REF)).toEqual({ value: 'sk-seeded', source: 'memory' })
|
||||
expect(await ctx.credentials.describe(REF)).toEqual({ configured: true, source: 'memory', writable: true })
|
||||
})
|
||||
|
||||
it('treats an empty stored value as absent everywhere', async () => {
|
||||
const ctx = await boot({ DEEPSEEK_API_KEY: '' })
|
||||
expect(await ctx.credentials.resolve(REF)).toBeUndefined()
|
||||
expect(await ctx.credentials.describe(REF)).toEqual({ configured: false, writable: true })
|
||||
})
|
||||
|
||||
it('stores through set, removes through unset, and emits the committed change', async () => {
|
||||
const ctx = await boot()
|
||||
const events: CredentialRef[] = []
|
||||
ctx.on('credentials/updated', ref => void events.push(ref))
|
||||
|
||||
await ctx.credentials.set(REF, 'sk-live')
|
||||
expect(await ctx.credentials.resolve(REF)).toEqual({ value: 'sk-live', source: 'memory' })
|
||||
await ctx.credentials.unset(REF)
|
||||
expect(await ctx.credentials.resolve(REF)).toBeUndefined()
|
||||
expect(events).toEqual([REF, REF])
|
||||
})
|
||||
|
||||
it('rejects an empty set and keeps an absent unset silent', async () => {
|
||||
const ctx = await boot()
|
||||
const events: CredentialRef[] = []
|
||||
ctx.on('credentials/updated', ref => void events.push(ref))
|
||||
|
||||
await expect(ctx.credentials.set(REF, '')).rejects.toThrow(/empty value/)
|
||||
await ctx.credentials.unset(REF)
|
||||
expect(events).toEqual([])
|
||||
})
|
||||
|
||||
it('removes the service with its fiber', async () => {
|
||||
const ctx = new Context()
|
||||
const fiber = await ctx.plugin(MemoryCredentials)
|
||||
expect(ctx.get('credentials')).toBeDefined()
|
||||
await fiber.dispose()
|
||||
expect(ctx.get('credentials')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
37
packages/credentials/credentials/tests/invariant.spec.ts
Normal file
37
packages/credentials/credentials/tests/invariant.spec.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import { credentialRef } from '../src/index.ts'
|
||||
import * as CredentialsInvariant from '../src/invariant.ts'
|
||||
import { MemoryCredentials } from './memory.ts'
|
||||
|
||||
const REF = credentialRef('DEEPSEEK_API_KEY')
|
||||
|
||||
describe('credentials invariant companion', () => {
|
||||
it('accepts a committed change emitted by a live service', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(CredentialsInvariant)
|
||||
await ctx.plugin(MemoryCredentials)
|
||||
|
||||
await expect(ctx.credentials.set(REF, 'sk-live')).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('fails an update event emitted without a live service', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(CredentialsInvariant)
|
||||
|
||||
expect(() => { ctx.emit('credentials/updated', REF) }).toThrow(/invariant violated by "@deepseek-ai\/dsh-credentials"/)
|
||||
})
|
||||
|
||||
it('reserves the package name against duplicate registration', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(CredentialsInvariant)
|
||||
|
||||
expect(() => {
|
||||
ctx.invariants.register('@deepseek-ai/dsh-credentials', () => {})
|
||||
}).toThrow(/already registered/)
|
||||
})
|
||||
})
|
||||
51
packages/credentials/credentials/tests/memory.ts
Normal file
51
packages/credentials/credentials/tests/memory.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { Context } from 'cordis'
|
||||
import { Credentials } from '../src/index.ts'
|
||||
import type { CredentialInfo, CredentialRef, ResolvedCredential } from '../src/index.ts'
|
||||
|
||||
/**
|
||||
* In-memory credentials provider for interface and consumer tests: one
|
||||
* always-writable `memory` source seeded from plugin config.
|
||||
*/
|
||||
export class MemoryCredentials extends Credentials {
|
||||
private readonly store = new Map<string, string>()
|
||||
|
||||
constructor(ctx: Context, seed: Record<string, string> = {}) {
|
||||
super(ctx)
|
||||
for (const [key, value] of Object.entries(seed)) this.store.set(key, value)
|
||||
}
|
||||
|
||||
override resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {
|
||||
const value = this.store.get(ref)
|
||||
return Promise.resolve(value === undefined || value.length === 0
|
||||
? undefined
|
||||
: { value, source: 'memory' })
|
||||
}
|
||||
|
||||
override describe(ref: CredentialRef): Promise<CredentialInfo> {
|
||||
const value = this.store.get(ref)
|
||||
const configured = value !== undefined && value.length > 0
|
||||
return Promise.resolve({
|
||||
configured,
|
||||
...configured ? { source: 'memory' } : {},
|
||||
writable: true,
|
||||
})
|
||||
}
|
||||
|
||||
override set(ref: CredentialRef, value: string): Promise<void> {
|
||||
if (value.length === 0) {
|
||||
return Promise.reject(new Error('memory credentials: an empty value cannot be stored; use unset'))
|
||||
}
|
||||
this.store.set(ref, value)
|
||||
this.ctx.emit('credentials/updated', ref)
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
override unset(ref: CredentialRef): Promise<void> {
|
||||
if (this.store.delete(ref)) {
|
||||
this.ctx.emit('credentials/updated', ref)
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoryCredentials
|
||||
24
packages/credentials/credentials/tsconfig.json
Normal file
24
packages/credentials/credentials/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../vendor/cosmokit"
|
||||
},
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../../util/brand"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -2062,6 +2062,18 @@ importers:
|
||||
specifier: ^4.0.0-rc.7
|
||||
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
||||
|
||||
packages/credentials/credentials:
|
||||
devDependencies:
|
||||
'@deepseek-ai/dsh-brand':
|
||||
specifier: workspace:^
|
||||
version: link:../../util/brand
|
||||
'@deepseek-ai/dsh-invariants':
|
||||
specifier: workspace:^
|
||||
version: link:../../support/invariants
|
||||
cordis:
|
||||
specifier: ^4.0.0-rc.7
|
||||
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
||||
|
||||
packages/examples/acp-demo:
|
||||
devDependencies:
|
||||
'@cordisjs/plugin-include':
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"./packages/session-projection/*/src/invariant.ts",
|
||||
"./packages/session-query/*/src/invariant.ts",
|
||||
"./packages/settings/*/src/invariant.ts",
|
||||
"./packages/credentials/*/src/invariant.ts",
|
||||
"./packages/telemetry/*/src/invariant.ts",
|
||||
"./packages/acp/*/src/invariant.ts",
|
||||
"./packages/storage/*/src/invariant.ts",
|
||||
@@ -173,6 +174,7 @@
|
||||
"./packages/session-query/*/src",
|
||||
"./packages/session-title/*/src",
|
||||
"./packages/settings/*/src",
|
||||
"./packages/credentials/*/src",
|
||||
"./packages/telemetry/*/src",
|
||||
"./packages/acp/*/src",
|
||||
"./packages/storage/*/src",
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
{ "path": "./packages/session-query/session-query-sqlite" },
|
||||
{ "path": "./packages/settings/settings" },
|
||||
{ "path": "./packages/settings/settings-local" },
|
||||
{ "path": "./packages/credentials/credentials" },
|
||||
{ "path": "./packages/session-query/tool-session-query" },
|
||||
{ "path": "./packages/storage/storage" },
|
||||
{ "path": "./packages/storage/storage-json" },
|
||||
|
||||
Reference in New Issue
Block a user