Files
deepseek-harness/packages/credentials/authorization/tests/memory.ts
T
Yichen Jiang 732a7361f5 feat(authorization): obtain a credential by asking the human
Some credentials cannot be configured, only obtained: getting one means
a conversation — open this page, paste that code, pick an account. The
new seam owns that conversation and the one-attempt-per-key lifecycle,
and never the protocol, so a second authorization protocol arrives as
another flow rather than as another seam.

A flow is registered under the CredentialKey it writes, which is also
how the seam knows which plugin answers for the format inside that
record. The flow owns the write: run() resolving means the record is
already committed through ctx.credentials, and the seam confirms it.
That keeps a library persisting through its own store adapter the
single writer instead of being copied back out and written twice.

The interaction travels with the request rather than a registry,
because whoever starts an authorization is the one who can talk to the
human about it. A request already withdrawn never claims the key and
never starts the flow — relying on each flow to check its signal before
the first await would let one that does not hang holding the key.
2026-08-20 17:58:38 +08:00

68 lines
2.2 KiB
TypeScript

import { CredentialProvider } from '@deepseek-ai/dsh-credentials'
import type {
CredentialInfo,
CredentialKey,
CredentialRecord,
CredentialRecordEntry,
CredentialRecordInfo,
CredentialRef,
ResolvedCredential,
} from '@deepseek-ai/dsh-credentials'
/**
* In-memory credentials provider for the authorization suite. Only the record
* half is exercised — the seam's whole interest in this service is whether a
* flow left a record behind — so the reference half answers "nothing stored".
*/
export class MemoryCredentials extends CredentialProvider {
private readonly records = new Map<CredentialKey, CredentialRecord>()
override resolve(_ref: CredentialRef): Promise<ResolvedCredential | undefined> {
return Promise.resolve(undefined)
}
override describe(_ref: CredentialRef): Promise<CredentialInfo> {
return Promise.resolve({ configured: false, writable: true })
}
override set(_ref: CredentialRef, _value: string): Promise<void> {
return Promise.resolve()
}
override unset(_ref: CredentialRef): Promise<void> {
return Promise.resolve()
}
override readRecord(key: CredentialKey): Promise<CredentialRecord | undefined> {
return Promise.resolve(this.records.get(key))
}
override describeRecord(key: CredentialKey): Promise<CredentialRecordInfo> {
const stored = this.records.get(key)
return Promise.resolve(stored === undefined
? { configured: false, writable: true }
: { configured: true, kind: stored.kind, writable: true })
}
override listRecords(): Promise<readonly CredentialRecordEntry[]> {
return Promise.resolve([...this.records].map(([key, record]) => ({ key, kind: record.kind })))
}
override async modifyRecord(
key: CredentialKey,
mutate: (current: CredentialRecord | undefined) => Promise<CredentialRecord | undefined>,
): Promise<CredentialRecord | undefined> {
const current = this.records.get(key)
const next = await mutate(current)
if (next === undefined) return current
this.records.set(key, next)
this.ctx.emit('credentials/record-updated', key)
return next
}
override deleteRecord(key: CredentialKey): Promise<void> {
if (this.records.delete(key)) this.ctx.emit('credentials/record-updated', key)
return Promise.resolve()
}
}