refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:54:38 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions
+6
View 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/identity/README.md
README.md: ebbc7d937dfa793edf9a8617d86a1820e87866da
README.zh.md: 3d29fc3dee7599e366cb91c09d6a73c8acddab96
+9
View File
@@ -0,0 +1,9 @@
# identity/ — shared identity
English | [中文](README.zh.md)
Identity values shared across product domains. These values do not represent an authenticated account.
| Package | Role | ctx key |
|---|---|---|
| [`anonymous-user-id/`](anonymous-user-id/README.md) | Persists one anonymous Harness-home correlation id for telemetry, feedback, and DeepSeek requests | — |
+9
View File
@@ -0,0 +1,9 @@
# identity/ — 共享身份
[English](README.md) | 中文
跨产品领域共享的身份值。这些值不表示经过身份验证的账户。
| 包 | 职责 | ctx key |
|---|---|---|
| [`anonymous-user-id/`](anonymous-user-id/README.md) | 为遥测、反馈和 DeepSeek 请求持久化一个限定于 Harness home 的匿名关联 id | — |
@@ -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/identity/anonymous-user-id/README.md
README.md: fb9d8f8046f41aed7c0bc8aa8cede46f9bb8c93b
README.zh.md: 738289fd6b132e3fd5b6cc5a89dbe8ca1ffb5e42
@@ -0,0 +1,30 @@
# @deepseek-ai/dsh-anonymous-user-id
English | [中文](README.zh.md)
Shared anonymous identity for session telemetry, direct feedback acknowledgement, and DeepSeek provider requests. `getOrCreateAnonymousUserId()` returns a random UUID v4 scoped to one harness home, persisted as the bare line `$DSH_HOME/.anonymous-user-id` (`~/.dsh/.anonymous-user-id` when `DSH_HOME` is unset). The OpenTelemetry backend reports it as Resource `user.id`; `/feedback` includes the same value in its acknowledgement; and `dsh-llm-deepseek` sends it as `x-deepseek-harness-user-id`, allowing the receiving systems to correlate records without independently generated identities.
The identity is never derived from the hostname, network address, git remote, or another identifying source. Deleting `.anonymous-user-id` resets the identity on the next process launch. Separate harness homes have separate identities.
## Storage contract
Reads and writes are synchronous because both boot-time telemetry construction and direct command execution need one API. The result is memoized per resolved file path for the process lifetime. A first writer uses exclusive creation and a concurrent loser adopts the persisted winner; a corrupt file is replaced. Persistence is best-effort, so an unwritable home still receives a process-local UUID rather than blocking telemetry or feedback.
## Composition
This package is a shared library, not a Cordis plugin. Consumers import `getOrCreateAnonymousUserId()` directly. Its invariant companion is intentionally empty because the package owns no event stream or public mutable relation that can be checked without creating the identity as a side effect. `DSH_TELEMETRY_DISABLED` stops telemetry export only; it does not suppress direct feedback acknowledgement or the DeepSeek provider header.
## Model Experience
None, as the identifier reaches DeepSeek only as model-hidden HTTP transport metadata and never enters the request body, prompt, or model-visible content.
#### KV Cache effect
None; the transport header changes neither tokens nor the model-visible prefix.
## Known Limitations and Deferred Work
- **No recovery after deletion** — loss mints a new anonymous identity by design; recovery would require stable derivation material that weakens anonymity.
- **Best-effort concurrency** — a reader landing in the narrow interval between a concurrent process's exclusive create and completed write can use a different in-memory UUID for that run; later launches converge on the persisted value.
- **No cross-home identity** — different `$DSH_HOME` values cannot be correlated.
- **Configured DeepSeek gateways receive the id** — `dsh-llm-deepseek` sends the stable header to its resolved `baseURL`, including deployment overrides, independently of telemetry sharing mode.
@@ -0,0 +1,30 @@
# @deepseek-ai/dsh-anonymous-user-id
[English](README.md) | 中文
会话遥测、直接反馈确认与 DeepSeek 提供方请求共用的匿名身份。`getOrCreateAnonymousUserId()` 返回一个限定于单个 harness home 的随机 UUID v4,并以裸行形式持久化到 `$DSH_HOME/.anonymous-user-id`(未设置 `DSH_HOME` 时为 `~/.dsh/.anonymous-user-id`)。OpenTelemetry 后端将其作为 Resource 的 `user.id` 上报;`/feedback` 在确认文本中包含同一个值;`dsh-llm-deepseek` 则通过 `x-deepseek-harness-user-id` 发送该值,使接收系统无需独立生成身份即可关联记录。
该身份绝不从 hostname、网络地址、git remote 或其他可用于识别身份的来源派生。删除 `.anonymous-user-id` 后,下次启动进程时会重置身份。不同 harness home 拥有不同身份。
## 存储约定
读写采用同步方式,因为启动时构造遥测和直接执行命令都需要使用同一个 API。结果在进程生命周期内按解析后的文件路径缓存。首个写入方采用独占创建;并发竞争中失败的一方会采用已持久化的胜出值。损坏的文件会被替换。持久化采用 best-effort,因此即使 home 不可写,系统仍会返回进程本地 UUID,而不会阻塞遥测或反馈。
## 组合
本包是共享库,并非 Cordis 插件。消费方直接导入 `getOrCreateAnonymousUserId()`。其不变式伴生插件刻意留空,因为本包既不拥有事件流,也不拥有任何可以在不触发创建身份这一副作用的情况下检查的公开可变关系。`DSH_TELEMETRY_DISABLED` 只会停止遥测导出,不会禁止直接反馈确认或 DeepSeek 提供方标头。
## 模型体验
无,因为该标识符只会作为模型不可见的 HTTP 传输元数据发送给 DeepSeek,绝不会进入请求正文、提示词或模型可见内容。
#### KV Cache 影响
无;该传输标头既不会改变 token,也不会改变模型可见前缀。
## 已知限制与暂缓工作
- **删除后无法恢复**:身份丢失后会按设计生成新的匿名身份;若要恢复身份,就需要稳定的派生材料,这会削弱匿名性。
- **Best-effort 并发**:如果读取方恰好落在并发进程完成独占创建但尚未写完的狭窄时间窗内,本次运行可能使用不同的内存 UUID;后续启动会收敛到已持久化的值。
- **没有跨 home 身份**:不同 `$DSH_HOME` 值之间无法关联。
- **已配置的 DeepSeek gateway 会收到该 id**`dsh-llm-deepseek` 会把稳定标头发送至解析后的 `baseURL`(包括部署覆盖),且不受遥测共享模式影响。
@@ -0,0 +1,46 @@
{
"name": "@deepseek-ai/dsh-anonymous-user-id",
"description": "Shared anonymous user identity for DeepSeek Harness telemetry and feedback correlation",
"version": "0.0.1-rc.2",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/identity/anonymous-user-id"
},
"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"
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}
}
@@ -0,0 +1,100 @@
/**
* Per-harness-home anonymous user id shared by telemetry and feedback.
*
* The id is a random UUID persisted as a bare line in `.anonymous-user-id` inside the
* harness home resolved by {@link resolveDshHome} (`$DSH_HOME` > `~/.dsh`),
* and never derived from the hostname, network address, git remote, or any
* other identifying source. It is scoped to the harness home, not the
* machine: every process sharing one `$DSH_HOME` reports the same id, and
* deleting the file mints a fresh identity on the next launch.
*
* Reads and writes are synchronous so boot-time and command consumers can
* use one API. The result is memoized per resolved file path: one process
* touches the disk once, and a file deleted mid-run keeps the process's id
* until the next launch.
*
* @module @deepseek-ai/dsh-anonymous-user-id
*/
import { randomUUID } from 'node:crypto'
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import type { Branded } from '@deepseek-ai/dsh-brand'
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths'
/** A harness-home-scoped anonymous user id (random UUID v4). */
export type AnonymousUserId = Branded<'AnonymousUserId'>
/** File inside the harness home storing the id: a bare UUID line, no wrapper format. */
export const ANONYMOUS_USER_ID_FILE_NAME = '.anonymous-user-id'
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
/** Ambient hooks for locating and generating the id; every field has a default. */
export interface AnonymousUserIdOptions {
/** Environment consulted for `DSH_HOME`; defaults to `process.env`. */
env?: NodeJS.ProcessEnv
/** UUID generator; defaults to `crypto.randomUUID` (test hook). */
randomUUID?: () => string
}
/** Process-lifetime memo keyed by resolved file path, so distinct test homes never share an id. */
const memo = new Map<string, AnonymousUserId>()
/** Read a valid persisted id from the file, or `undefined` when absent/corrupt. */
function readPersistedId(file: string): AnonymousUserId | undefined {
let text: string
try {
text = readFileSync(file, 'utf8')
} catch {
// Absent or unreadable: the caller mints and persists a fresh id.
return undefined
}
const value = text.trim()
return UUID_PATTERN.test(value) ? (value as AnonymousUserId) : undefined
}
/**
* Return the harness home's anonymous user id, creating and persisting one on
* first use. A concurrent first launch is settled by an exclusive-create
* write: the loser rereads the winner's id. (A reread landing in the winner's
* narrow create-to-write window can still yield two per-process ids for that
* run; the next launch converges on the persisted one.) Persistence is
* best-effort — a write failure (read-only home) still returns a usable id
* for the current run so feedback and telemetry are never blocked.
* @param options - home-location and UUID-generation seams.
* @returns the stable per-harness-home anonymous user id.
*/
export function getOrCreateAnonymousUserId(options: AnonymousUserIdOptions = {}): AnonymousUserId {
const file = join(resolveDshHome(undefined, options.env ?? process.env), ANONYMOUS_USER_ID_FILE_NAME)
const cached = memo.get(file)
if (cached !== undefined) return cached
let id = readPersistedId(file)
if (id === undefined) {
const generate = options.randomUUID ?? randomUUID
const created = generate() as AnonymousUserId
try {
mkdirSync(dirname(file), { recursive: true })
writeFileSync(file, `${created}\n`, { encoding: 'utf8', flag: 'wx' })
id = created
} catch {
// A wx refusal (EEXIST) covers both a concurrent winner and a
// pre-existing corrupt file: the reread adopts a valid winner, and an
// invalid reread falls through to the overwrite path. Non-EEXIST
// failures (read-only home) land there too, accepted best-effort below.
id = readPersistedId(file)
if (id === undefined) {
try {
writeFileSync(file, `${created}\n`, 'utf8')
} catch {
// Best-effort persistence: keep the fresh id in memory even when the
// home is unwritable, so this run still reports a consistent id.
}
id = created
}
}
}
memo.set(file, id)
return id
}
@@ -0,0 +1,31 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-anonymous-user-id`.
* @module @deepseek-ai/dsh-anonymous-user-id/invariant
*/
/* jscpd:ignore-start */
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-anonymous-user-id'
/** Cordis companion plugin name. */
export const name = 'anonymous-user-id-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: the API owns one private memo and one best-effort
* file, with no independent event stream or public mutable relation for a
* companion to compare without creating the identity as a side effect.
*/
const install: InvariantInstaller = () => {}
/**
* 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))
/* jscpd:ignore-end */
@@ -0,0 +1,105 @@
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
ANONYMOUS_USER_ID_FILE_NAME,
getOrCreateAnonymousUserId,
} from '../src/index.ts'
const dirs: string[] = []
function tempHome(): string {
const dir = mkdtempSync(join(tmpdir(), 'dsh-userid-'))
dirs.push(dir)
return dir
}
afterEach(() => {
for (const dir of dirs.splice(0)) {
rmSync(dir, { recursive: true, force: true })
}
})
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
describe('getOrCreateAnonymousUserId', () => {
it('creates, persists, and returns a bare UUID line on first use', () => {
const home = tempHome()
const id = getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })
expect(id).toMatch(UUID)
expect(readFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), 'utf8')).toBe(`${id}\n`)
})
it('creates the home directory when missing', () => {
const home = join(tempHome(), 'nested', 'home')
const id = getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })
expect(readFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), 'utf8')).toBe(`${id}\n`)
})
it('returns the persisted id on subsequent calls, tolerating surrounding whitespace', () => {
const home = tempHome()
const existing = '01234567-89ab-4cde-8f01-23456789abcd'
writeFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), ` ${existing}\n\n`, 'utf8')
expect(getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })).toBe(existing)
})
it('overwrites a corrupt file with a fresh id', () => {
const home = tempHome()
writeFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), 'not-a-uuid\n', 'utf8')
const id = getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })
expect(id).toMatch(UUID)
expect(readFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), 'utf8')).toBe(`${id}\n`)
})
it('adopts a concurrent winner: exclusive create loses to an id written after the initial read', () => {
const home = tempHome()
const winner = 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee'
const file = join(home, ANONYMOUS_USER_ID_FILE_NAME)
// The generator hook runs between the initial read (absent) and the wx
// write, so planting the winner here simulates the concurrent first launch.
const id = getOrCreateAnonymousUserId({
env: { DSH_HOME: home },
randomUUID: () => {
writeFileSync(file, `${winner}\n`, 'utf8')
return 'ffffffff-0000-4000-8000-000000000000'
},
})
expect(id).toBe(winner)
})
it('returns a usable id when the home cannot contain files, without persisting', () => {
const home = tempHome()
const blocked = join(home, 'blocked')
writeFileSync(blocked, 'occupied\n')
const id = getOrCreateAnonymousUserId({ env: { DSH_HOME: blocked } })
expect(id).toMatch(UUID)
expect(existsSync(join(blocked, ANONYMOUS_USER_ID_FILE_NAME))).toBe(false)
})
it('memoizes per resolved home for the process lifetime: one read, deletion-proof', () => {
const home = tempHome()
const first = getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })
rmSync(join(home, ANONYMOUS_USER_ID_FILE_NAME))
expect(getOrCreateAnonymousUserId({ env: { DSH_HOME: home } })).toBe(first)
})
it('keeps distinct homes on distinct ids', () => {
const a = getOrCreateAnonymousUserId({ env: { DSH_HOME: tempHome() } })
const b = getOrCreateAnonymousUserId({ env: { DSH_HOME: tempHome() } })
expect(a).not.toBe(b)
})
it('reads process.env by default', () => {
const home = tempHome()
const previous = process.env.DSH_HOME
process.env.DSH_HOME = home
try {
const id = getOrCreateAnonymousUserId()
expect(readFileSync(join(home, ANONYMOUS_USER_ID_FILE_NAME), 'utf8')).toBe(`${id}\n`)
} finally {
if (previous === undefined) delete process.env.DSH_HOME
else process.env.DSH_HOME = previous
}
})
})
@@ -0,0 +1,12 @@
import { describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
import * as UserIdInvariant from '@deepseek-ai/dsh-anonymous-user-id/invariant'
describe('invariant companion', () => {
it('registers the package ownership with an empty installer', async () => {
const ctx = new Context()
await ctx.plugin(InvariantRegistry, { enabled: true })
await expect(ctx.plugin(UserIdInvariant).await()).resolves.toBeDefined()
})
})
@@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"include": [
"src"
],
"references": [
{
"path": "../../util/brand"
},
{
"path": "../../util/home-paths"
},
{
"path": "../../runtime-diagnostics/invariants"
}
]
}