refactor(api): converge the Remote failure vocabulary and client surface

Single RemoteError with a merge-extensible, domain-prefixed code map;
owners throw at the failure point; streams surface marked failures;
clients consume ctx.remote directly with isRemoteFailure as the only
discrimination point and construct no failure instances.
This commit is contained in:
imccyu
2026-08-28 22:37:36 +08:00
parent 12d7b4ed0c
commit 804b1ffbfc
252 changed files with 3182 additions and 3832 deletions
@@ -9,7 +9,7 @@ import { Context } from '@deepseek-ai/cordis'
import { credentialRef } from '@deepseek-ai/dsh-credentials'
import type { CredentialProvider } from '@deepseek-ai/dsh-credentials'
import type { CredentialInfo } from '@deepseek-ai/dsh-credentials/types'
import { Remote, TypertRemoteFailure, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
import { z } from 'zod'
/**
@@ -30,11 +30,7 @@ const unsetRequestSchema = z.object({ ref: credentialRefSchema })
function parseRequest<T>(method: string, schema: z.ZodType<T>, value: unknown): T {
const parsed = schema.safeParse(value)
if (!parsed.success) {
throw new TypertRemoteFailure({
code: 'bad-request',
message: `invalid payload for ${method}`,
details: { issues: parsed.error.issues },
})
throw new RemoteError('gateway/bad-request', `invalid payload for ${method}`, { issues: parsed.error.issues })
}
return parsed.data
}
@@ -78,9 +74,10 @@ export class CredentialsController extends TypertRemoteService {
* Describe several references for one configuration surface. Batched because
* a settings page describes every reference its rows name at once, and one
* round trip keeps those rows from settling separately.
* @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar rejects the whole call as `bad-request`.
* @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar
* rejects the whole call as `gateway/bad-request`.
* @returns one view per requested name, keyed by that name.
* @throws TypertRemoteFailure when the request is invalid or no credential provider is mounted.
* @throws RemoteError when the request is invalid or no credential provider is mounted.
*/
@Remote
async describe(refs: string[]): Promise<Record<string, CredentialInfo>> {
@@ -97,7 +94,7 @@ export class CredentialsController extends TypertRemoteService {
* this direction only: no read path returns it.
* @param ref - reference name to store under.
* @param value - the non-empty secret value.
* @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
* @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
*/
@Remote
async set(ref: string, value: string): Promise<void> {
@@ -110,7 +107,7 @@ export class CredentialsController extends TypertRemoteService {
/**
* Remove one reference from a configuration surface.
* @param ref - reference name to remove.
* @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
* @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
*/
@Remote
async unset(ref: string): Promise<void> {
@@ -124,17 +121,17 @@ export class CredentialsController extends TypertRemoteService {
private provider(): CredentialProvider {
const credentials = this.ctx.get('credentials')
if (credentials === undefined) {
throw new TypertRemoteFailure({
code: 'internal',
message: 'credentials service is absent: this deployment does not mount a credential provider (e.g. @deepseek-ai/dsh-credentials-local) in its composition',
details: {},
})
throw new RemoteError(
'gateway/internal',
'credentials service is absent: this deployment does not mount a credential provider (e.g. @deepseek-ai/dsh-credentials-local) in its composition',
{},
)
}
return credentials
}
/**
* Run one remote write and report every refusal as `credential-rejected`
* Run one remote write and report every refusal as `credential/rejected`
* carrying the seam's own message: a read-only source shadowing the reference
* is what a configuration surface must show verbatim. Callers brand the
* reference before entering, so a name outside the grammar never reaches this
@@ -145,11 +142,12 @@ export class CredentialsController extends TypertRemoteService {
try {
await write()
} catch (error: unknown) {
throw new TypertRemoteFailure({
code: 'credential-rejected',
message: error instanceof Error ? error.message : String(error),
details: { ref },
})
throw new RemoteError(
'credential/rejected',
error instanceof Error ? error.message : String(error),
{ ref },
{ cause: error },
)
}
}
}