refactor(credentials,llm): remove speculative mutation and route lifecycle

This commit is contained in:
Tianyi Cui
2026-07-31 01:08:54 +08:00
parent 16802cd612
commit f9f8148e79
100 changed files with 559 additions and 2752 deletions
@@ -27,7 +27,6 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-atomic-write": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-paths": "^0.0.1",
"@deepseek-ai/dsh-settings": "^0.0.1",
@@ -39,7 +38,6 @@
"yaml": "^2.9.0"
},
"devDependencies": {
"@deepseek-ai/dsh-atomic-write": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
+68 -12
View File
@@ -10,10 +10,10 @@
import { Context, Service } from 'cordis'
import z from 'schemastery'
import { watch as chokidarWatch } from 'chokidar'
import { mkdir, readFile } from 'node:fs/promises'
import { randomBytes } from 'node:crypto'
import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises'
import { dirname, extname, join, resolve } from 'node:path'
import { Document, parseDocument } from 'yaml'
import { withFileLock, writeFileAtomic } from '@deepseek-ai/dsh-atomic-write'
import { resolveDshHome } from '@deepseek-ai/dsh-paths'
import { Settings, deepEqualJson, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
@@ -96,6 +96,17 @@ function isENOENT(error: unknown): boolean {
return (error as NodeJS.ErrnoException | null)?.code === 'ENOENT'
}
/** Whether an exclusive create failed because the path already exists. */
function isEEXIST(error: unknown): boolean {
return (error as NodeJS.ErrnoException | null)?.code === 'EEXIST'
}
/** Writer-lock retry constants for the private settings document protocol. */
const LOCK_RETRY_INITIAL_MS = 20
const LOCK_RETRY_MAX_MS = 200
const LOCK_TIMEOUT_MS = 2_000
const LOCK_STALE_MS = 5_000
/** File-backed settings provider (`settings.yaml`/`.json`). */
export class SettingsLocal extends Settings {
static Config: z<Config> = z.object({
@@ -180,11 +191,8 @@ export class SettingsLocal extends Settings {
}
private async persistSection(ns: SettingsNamespace, section: Record<string, unknown>): Promise<void> {
// The writer lock's exclusive create needs the parent to exist before
// writeFileAtomic gets its own chance to create it.
// 0700: the harness home holds user-private documents.
await mkdir(dirname(this.spec.filename), { recursive: true, mode: 0o700 })
await withFileLock(this.spec.filename, async () => {
await this.withWriterLock(async () => {
// Read-modify-write: fold in any on-disk state this process has not
// observed yet — an external edit still inside the watcher debounce
// window, a change the watcher missed, or another process's write — so
@@ -195,16 +203,64 @@ export class SettingsLocal extends Settings {
const output = this.spec.format === 'yaml'
? this.renderYaml(ns, section)
: this.renderJson(ns, section)
// 0600: a document that may hold personal values is never world-readable.
await writeFileAtomic(this.spec.filename, output, { mode: 0o600, dirMode: 0o700 })
const temp = `${this.spec.filename}.${randomBytes(6).toString('hex')}.tmp`
// TODO(settings-atomic-durability): Use a replacement that fsyncs the file
// and parent directory and preserves owner-only permissions on Windows.
try {
await writeFile(temp, output, { mode: 0o600, flag: 'wx' })
await rename(temp, this.spec.filename)
} catch (error) {
await rm(temp, { force: true })
throw error
}
this.text = output
}, {
onStaleBreak: (lockPath) => {
this.ctx.logger.warn('settings-local: breaking a stale writer lock at %s', lockPath)
},
})
}
/** Hold the private cross-process writer lock around one read-render-rename cycle. */
private async withWriterLock<T>(operation: () => Promise<T>): Promise<T> {
const lockPath = `${this.spec.filename}.lock`
const deadline = Date.now() + LOCK_TIMEOUT_MS
let delay = LOCK_RETRY_INITIAL_MS
for (;;) {
try {
await writeFile(lockPath, `${process.pid}\n`, { mode: 0o600, flag: 'wx' })
break
} catch (error) {
if (!isEEXIST(error)) throw error
}
const ageMs = await this.lockAgeMs(lockPath)
if (ageMs === undefined) continue
if (ageMs > LOCK_STALE_MS) {
// TODO(settings-lock-ownership): Replace age-only takeover with ownership-safe
// acquisition and release so a slow writer cannot remove a successor's lock.
this.ctx.logger.warn('settings-local: breaking a stale writer lock at %s', lockPath)
await rm(lockPath, { force: true })
continue
}
if (Date.now() >= deadline) {
throw new Error(`settings-local: timed out waiting for the writer lock at ${lockPath}`)
}
await new Promise(resolvePause => setTimeout(resolvePause, delay))
delay = Math.min(delay * 2, LOCK_RETRY_MAX_MS)
}
try {
return await operation()
} finally {
await rm(lockPath, { force: true })
}
}
/** Age of the writer lock, or `undefined` when it vanished after a failed create. */
private async lockAgeMs(lockPath: string): Promise<number | undefined> {
try {
return Date.now() - (await stat(lockPath)).mtimeMs
} catch (error) {
if (!isENOENT(error)) throw error
return undefined
}
}
override async* [Service.init](): AsyncGenerator<() => Promise<void> | void, void, void> {
// The base init loads and publishes; a parse failure there is a boot
// failure: an existing-but-invalid document must fail loud, never be
@@ -16,7 +16,7 @@ export const inject = ['invariants']
/**
* No runtime invariant: this provider's contracts are file round-trip,
* watcher timing, and atomic-write behavior — IO effects proven by package
* watcher timing, and atomic replacement behavior — IO effects proven by package
* tests; the in-process commit relation is owned by `@deepseek-ai/dsh-settings`.
*/
const install: InvariantInstaller = () => {}
@@ -17,9 +17,6 @@
{
"path": "../../../vendor/schemastery"
},
{
"path": "../../util/atomic-write"
},
{
"path": "../../util/paths"
},