Merge remote-tracking branch 'origin/master' into mergebot/pr711
# Conflicts: # apps/cli/README.i18n.yaml # docs/module-graph.md # packages/client/connection/README.i18n.yaml # packages/client/runtime/README.i18n.yaml # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/src/api-proxy.ts
This commit is contained in:
455 files changed
+9824
-1402
No files matched your search
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* credentials domain zod schemas (names derived from map keys:
|
||||
* credentialsDescribeRequestSchema / credentialsDescribeValueSchema / …).
|
||||
* The reference-name pattern mirrors the seam's `credentialRef` guard so an
|
||||
* invalid name fails as `bad-request` before reaching the service.
|
||||
*/
|
||||
|
||||
import { z } from 'zod'
|
||||
import type { RequestPayload, ResponseValue } from './rpc-map.ts'
|
||||
import type { Wire } from './rpc.schema.ts'
|
||||
import type { CredentialView } from './credentials.ts'
|
||||
|
||||
/** POSIX-portable environment-variable name (the seam's `credentialRef` pattern). */
|
||||
export const credentialRefNameSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/)
|
||||
|
||||
/** CredentialView entry of credentials.describe. */
|
||||
export const credentialViewSchema = z.object({
|
||||
configured: z.boolean(),
|
||||
source: z.string().optional(),
|
||||
writable: z.boolean(),
|
||||
}) satisfies z.ZodType<Wire<CredentialView>>
|
||||
|
||||
/** credentials.describe request payload. */
|
||||
export const credentialsDescribeRequestSchema = z.object({
|
||||
refs: z.array(credentialRefNameSchema).max(64),
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'credentials.describe'>>>
|
||||
|
||||
/** credentials.describe response value. */
|
||||
export const credentialsDescribeValueSchema = z.object({
|
||||
credentials: z.record(z.string(), credentialViewSchema),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'credentials.describe'>>>
|
||||
|
||||
/** credentials.set request payload: the one direction a value crosses this wire. */
|
||||
export const credentialsSetRequestSchema = z.object({
|
||||
ref: credentialRefNameSchema,
|
||||
value: z.string().min(1),
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'credentials.set'>>>
|
||||
|
||||
/** credentials.set response value. */
|
||||
export const credentialsSetValueSchema = z.object({}) satisfies z.ZodType<Wire<ResponseValue<'credentials.set'>>>
|
||||
|
||||
/** credentials.unset request payload. */
|
||||
export const credentialsUnsetRequestSchema = z.object({
|
||||
ref: credentialRefNameSchema,
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'credentials.unset'>>>
|
||||
|
||||
/** credentials.unset response value. */
|
||||
export const credentialsUnsetValueSchema = z.object({}) satisfies z.ZodType<Wire<ResponseValue<'credentials.unset'>>>
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* credentials domain contract: the web face of the credential-reference seam
|
||||
* (`ctx.credentials`). Reads are structurally value-free — a credential view
|
||||
* carries configured/source/writable and has no slot for the value — and the
|
||||
* value crosses the wire in exactly one direction, inside `credentials.set`.
|
||||
* There is no enumeration method by design: clients learn which references
|
||||
* exist from settings schemas and values (`apiKeyEnv` fields).
|
||||
*/
|
||||
|
||||
import type { RpcRequest, RpcResponse } from './rpc.ts'
|
||||
|
||||
/** Wire view of one credential reference's state. */
|
||||
export interface CredentialView {
|
||||
/** Whether any layer currently supplies a non-empty value. */
|
||||
configured: boolean
|
||||
/** Winning layer when configured (`env`, `file`, …); provider vocabulary. */
|
||||
source?: string
|
||||
/** Whether `credentials.set`/`credentials.unset` can affect this reference. */
|
||||
writable: boolean
|
||||
}
|
||||
|
||||
/** Credentials-domain unary methods (the map keys credentials.* of RpcMethodMap). */
|
||||
export interface CredentialsApi {
|
||||
/**
|
||||
* Describe the named references (batch): configured state, winning source,
|
||||
* and writability — never values. An invalid reference name is a
|
||||
* `bad-request`; an unknown-but-valid one describes as unconfigured.
|
||||
*/
|
||||
describe(request: RpcRequest<{ refs: string[] }>): Promise<RpcResponse<{ credentials: Record<string, CredentialView> }>>
|
||||
|
||||
/**
|
||||
* Store one credential value in the writable layer. Rejected with
|
||||
* `credential-rejected` while a read-only layer (the live environment)
|
||||
* shadows the reference — the write would otherwise appear to succeed while
|
||||
* resolution keeps returning the shadowing value.
|
||||
*/
|
||||
set(request: RpcRequest<{ ref: string; value: string }>): Promise<RpcResponse<{}>>
|
||||
|
||||
/**
|
||||
* Remove one credential from the writable layer; same shadowing rejection
|
||||
* as `set`. Unsetting an absent reference succeeds (idempotent).
|
||||
*/
|
||||
unset(request: RpcRequest<{ ref: string }>): Promise<RpcResponse<{}>>
|
||||
}
|
||||
@@ -72,5 +72,8 @@ export const hostFrameSchema = z.discriminatedUnion('type', [
|
||||
z.object({ type: z.literal('host/workspace-changed'), workspace: workspaceViewSchema }),
|
||||
z.object({ type: z.literal('host/workspace-removed'), workspaceId: workspaceIdSchema }),
|
||||
z.object({ type: z.literal('host/commands-changed') }),
|
||||
z.object({ type: z.literal('host/settings-changed'), ns: z.string() }),
|
||||
z.object({ type: z.literal('host/credentials-changed'), ref: z.string() }),
|
||||
z.object({ type: z.literal('host/models-changed') }),
|
||||
z.object({ type: z.literal('stream/error'), error: rpcErrorSchema }),
|
||||
]) as unknown as z.ZodType<HostFrame>
|
||||
@@ -116,4 +116,23 @@ export type HostFrame =
|
||||
* background rather than diffing.
|
||||
*/
|
||||
| { type: 'host/commands-changed' }
|
||||
/**
|
||||
* One settings namespace's resolved value changed (`settings/updated`
|
||||
* passthrough) — an RPC write, an external `settings.yaml` edit, or a
|
||||
* provider reload all converge here. Clients refetch `settings.describe`;
|
||||
* values never ride the frame (they would need redaction and can go stale).
|
||||
*/
|
||||
| { type: 'host/settings-changed'; ns: string }
|
||||
/**
|
||||
* One credential reference's state changed (`credentials/updated`
|
||||
* passthrough): a set/unset over this wire or an external `.env` edit.
|
||||
* The ref is an environment-variable NAME — never a value.
|
||||
*/
|
||||
| { type: 'host/credentials-changed'; ref: string }
|
||||
/**
|
||||
* The provider topology changed (`llm/adapters-updated` passthrough):
|
||||
* routes registered or dropped, or the configurable directory moved. Pure
|
||||
* invalidation: clients refetch `llm.providers`/`llm.models`/`session.models`.
|
||||
*/
|
||||
| { type: 'host/models-changed' }
|
||||
| { type: 'stream/error'; error: RpcError }
|
||||
@@ -11,6 +11,9 @@ import type { CommandsApi } from './commands.ts'
|
||||
import type { SkillsApi } from './skills.ts'
|
||||
import type { EventsApi } from './events.ts'
|
||||
import type { GoalsApi } from './goals.ts'
|
||||
import type { SettingsApi } from './settings.ts'
|
||||
import type { CredentialsApi } from './credentials.ts'
|
||||
import type { LlmApi } from './llm.ts'
|
||||
import type { ClientResponse, RpcReceipt } from './rpc.ts'
|
||||
|
||||
/** Root interface of the unified API surface. New client-request domain = one new file pair + one field here + one map row. */
|
||||
@@ -22,6 +25,9 @@ export interface ApiProxy {
|
||||
skills: SkillsApi
|
||||
events: EventsApi
|
||||
goals: GoalsApi
|
||||
settings: SettingsApi
|
||||
credentials: CredentialsApi
|
||||
llm: LlmApi
|
||||
/** Response entry for server-requests (client-response, echoing their rpcId); not a domain method (four-quadrant model). */
|
||||
respond(message: ClientResponse): Promise<RpcReceipt>
|
||||
}
|
||||
@@ -38,6 +44,9 @@ export type { CommandsApi, CommandDescriptor } from './commands.ts'
|
||||
export type { SkillsApi, SkillEntry } from './skills.ts'
|
||||
export type { EventsApi, MuxFrame, HostFrame, QueuedInboxItem, ToolCallView, ToolEventView, ToolResultView } from './events.ts'
|
||||
export type { GoalsApi, GoalId, GoalRef } from './goals.ts'
|
||||
export type { SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView } from './settings.ts'
|
||||
export type { CredentialsApi, CredentialView } from './credentials.ts'
|
||||
export type { ConfigurableProviderView, LlmApi } from './llm.ts'
|
||||
export type { ApprovalResponsePayload } from './approvals.ts'
|
||||
export type { QuestionResponsePayload } from './questions.ts'
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* llm domain zod schemas (names derived from map keys: llmProvidersRequestSchema /
|
||||
* llmProvidersValueSchema / llmModelsRequestSchema / llmModelsValueSchema).
|
||||
*/
|
||||
|
||||
import { z } from 'zod'
|
||||
import type { RequestPayload, ResponseValue } from './rpc-map.ts'
|
||||
import type { Wire } from './rpc.schema.ts'
|
||||
import type { ConfigurableProviderView } from './llm.ts'
|
||||
import { modelCatalogFailureSchema, modelProviderGroupSchema } from './sessions.schema.ts'
|
||||
|
||||
/** ConfigurableProviderView row of llm.providers. */
|
||||
export const configurableProviderViewSchema = z.object({
|
||||
provider: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
settingsNs: z.string(),
|
||||
settingsPath: z.array(z.string()),
|
||||
active: z.boolean(),
|
||||
}) satisfies z.ZodType<Wire<ConfigurableProviderView>>
|
||||
|
||||
/** llm.providers request payload. */
|
||||
export const llmProvidersRequestSchema = z.object({}) satisfies z.ZodType<Wire<RequestPayload<'llm.providers'>>>
|
||||
|
||||
/** llm.providers response value. */
|
||||
export const llmProvidersValueSchema = z.object({
|
||||
providers: z.array(configurableProviderViewSchema),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'llm.providers'>>>
|
||||
|
||||
/** llm.models request payload. */
|
||||
export const llmModelsRequestSchema = z.object({}) satisfies z.ZodType<Wire<RequestPayload<'llm.models'>>>
|
||||
|
||||
/** llm.models response value. */
|
||||
export const llmModelsValueSchema = z.object({
|
||||
groups: z.array(modelProviderGroupSchema),
|
||||
failures: z.array(modelCatalogFailureSchema),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'llm.models'>>>
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* llm domain contract: host-scoped provider topology for configuration
|
||||
* surfaces. `llm.providers` merges the configurable-provider directory
|
||||
* (which providers CAN be configured, and where their settings live) with the
|
||||
* live route registry; `llm.models` is the session-independent model catalog
|
||||
* (`session.models` minus the per-session current/unlisted logic). Both
|
||||
* invalidate on the `host/models-changed` frame.
|
||||
*/
|
||||
|
||||
import type { RpcRequest, RpcResponse } from './rpc.ts'
|
||||
import type { ModelCatalogFailure, ModelProviderGroup } from './sessions.ts'
|
||||
|
||||
/** Wire view of one configurable provider. */
|
||||
export interface ConfigurableProviderView {
|
||||
/** Provider route key (`deepseek-official`, `openai`, …). */
|
||||
provider: string
|
||||
/** Human-readable name for configuration surfaces. */
|
||||
displayName: string
|
||||
/** Settings namespace whose section configures this provider. */
|
||||
settingsNs: string
|
||||
/** Path from that section's root to the provider's profile object (empty = whole section). */
|
||||
settingsPath: string[]
|
||||
/** Whether the route is currently registered (its models are requestable). */
|
||||
active: boolean
|
||||
}
|
||||
|
||||
/** Llm-domain unary methods (the map keys llm.* of RpcMethodMap). */
|
||||
export interface LlmApi {
|
||||
/**
|
||||
* List every configurable provider with its live/dormant state, in
|
||||
* directory declaration order. Routes registered outside the directory
|
||||
* (an adapter that never declared configurability) are appended with their
|
||||
* registration identity and no settings address.
|
||||
*/
|
||||
providers(request: RpcRequest<{}>): Promise<RpcResponse<{ providers: ConfigurableProviderView[] }>>
|
||||
|
||||
/**
|
||||
* Host-scoped model catalog over every registered provider route: the
|
||||
* settings surface's models view, needing no session. Per-provider listing
|
||||
* failures ride `failures` without failing the sound groups.
|
||||
*/
|
||||
models(request: RpcRequest<{}>): Promise<RpcResponse<{ groups: ModelProviderGroup[]; failures: ModelCatalogFailure[] }>>
|
||||
}
|
||||
@@ -10,6 +10,9 @@ import type { WorkspaceApi } from './workspace.ts'
|
||||
import type { CommandsApi } from './commands.ts'
|
||||
import type { SkillsApi } from './skills.ts'
|
||||
import type { GoalsApi } from './goals.ts'
|
||||
import type { SettingsApi } from './settings.ts'
|
||||
import type { CredentialsApi } from './credentials.ts'
|
||||
import type { LlmApi } from './llm.ts'
|
||||
import type { RpcResponse } from './rpc.ts'
|
||||
|
||||
/**
|
||||
@@ -48,6 +51,15 @@ export interface RpcMethodMap {
|
||||
'goal.resume': GoalsApi['resume']
|
||||
'goal.complete': GoalsApi['complete']
|
||||
'goal.clear': GoalsApi['clear']
|
||||
'settings.describe': SettingsApi['describe']
|
||||
'settings.update': SettingsApi['update']
|
||||
'settings.replace': SettingsApi['replace']
|
||||
'settings.mutate': SettingsApi['mutate']
|
||||
'credentials.describe': CredentialsApi['describe']
|
||||
'credentials.set': CredentialsApi['set']
|
||||
'credentials.unset': CredentialsApi['unset']
|
||||
'llm.providers': LlmApi['providers']
|
||||
'llm.models': LlmApi['models']
|
||||
}
|
||||
|
||||
/** Business request payload of method K (reaches through the RpcRequest narrow form to payload). */
|
||||
|
||||
@@ -50,6 +50,10 @@ export const rpcErrorSchema: z.ZodType<RpcError> = z.discriminatedUnion('code',
|
||||
z.object({ code: z.literal('queue-item-not-found'), message: z.string(), details: z.object({ itemId: z.string() }) }),
|
||||
z.object({ code: z.literal('command-error'), message: z.string(), details: z.object({}) }),
|
||||
z.object({ code: z.literal('unknown-command'), message: z.string(), details: z.object({}) }),
|
||||
z.object({ code: z.literal('settings-rejected'), message: z.string(), details: z.object({ ns: z.string() }) }),
|
||||
z.object({ code: z.literal('settings-not-exposed'), message: z.string(), details: z.object({ ns: z.string() }) }),
|
||||
z.object({ code: z.literal('settings-conflict'), message: z.string(), details: z.object({ ns: z.string(), expected: z.number(), actual: z.number() }) }),
|
||||
z.object({ code: z.literal('credential-rejected'), message: z.string(), details: z.object({ ref: z.string() }) }),
|
||||
z.object({ code: z.literal('title-invalid'), message: z.string(), details: z.object({ sessionId: z.string() }) }),
|
||||
z.object({ code: z.literal('fork-unavailable'), message: z.string(), details: z.object({ sessionId: z.string() }) }),
|
||||
z.object({ code: z.literal('internal'), message: z.string(), details: z.object({}) }),
|
||||
|
||||
@@ -50,6 +50,25 @@ export interface RpcErrorDetailsMap {
|
||||
'command-error': {}
|
||||
/** A leading-/ prompt named no registered command; the message names the token. */
|
||||
'unknown-command': {}
|
||||
/**
|
||||
* A settings write was refused (schema validation, unknown namespace,
|
||||
* read-only provider, or storage failure); the message is the seam's text.
|
||||
*/
|
||||
'settings-rejected': { ns: string }
|
||||
/**
|
||||
* A settings namespace exists in the seam but is outside the configuration
|
||||
* plane's model-provider boundary, so this proxy neither reads nor writes
|
||||
* it; the message names the namespace.
|
||||
*/
|
||||
'settings-not-exposed': { ns: string }
|
||||
/**
|
||||
* A settings write carried an `expectedRevision` the namespace has already
|
||||
* moved past: another writer (tab, editor, or an external file edit) landed
|
||||
* first. The details carry both revisions so a client can re-read and retry.
|
||||
*/
|
||||
'settings-conflict': { ns: string; expected: number; actual: number }
|
||||
/** A credential write was refused (read-only shadowing layer or storage failure); the message is the seam's own text. */
|
||||
'credential-rejected': { ref: string }
|
||||
'title-invalid': { sessionId: SessionId }
|
||||
'fork-unavailable': { sessionId: SessionId }
|
||||
'internal': {}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* settings domain zod schemas (names derived from map keys: settingsDescribeRequestSchema /
|
||||
* settingsDescribeValueSchema / settingsUpdate* / settingsReplace*).
|
||||
*/
|
||||
|
||||
import { z } from 'zod'
|
||||
import type { RequestPayload, ResponseValue } from './rpc-map.ts'
|
||||
import type { Wire } from './rpc.schema.ts'
|
||||
import type { SettingsNamespaceView, SettingsPathOpView, SettingsSecretView } from './settings.ts'
|
||||
|
||||
/** One redacted secret slot. */
|
||||
export const settingsSecretViewSchema = z.object({
|
||||
path: z.array(z.string()),
|
||||
set: z.boolean(),
|
||||
}) satisfies z.ZodType<Wire<SettingsSecretView>>
|
||||
|
||||
/** SettingsNamespaceView row of settings.describe and the write responses. */
|
||||
export const settingsNamespaceViewSchema = z.object({
|
||||
ns: z.string().min(1),
|
||||
schema: z.unknown(),
|
||||
value: z.unknown(),
|
||||
base: z.unknown().optional(),
|
||||
user: z.unknown().optional(),
|
||||
applies: z.union([z.literal('live'), z.literal('restart')]),
|
||||
secrets: z.array(settingsSecretViewSchema),
|
||||
revision: z.number(),
|
||||
}) satisfies z.ZodType<Wire<SettingsNamespaceView>>
|
||||
|
||||
/** settings.describe request payload. */
|
||||
export const settingsDescribeRequestSchema = z.object({}) satisfies z.ZodType<Wire<RequestPayload<'settings.describe'>>>
|
||||
|
||||
/** settings.describe response value. */
|
||||
export const settingsDescribeValueSchema = z.object({
|
||||
writable: z.boolean(),
|
||||
namespaces: z.array(settingsNamespaceViewSchema),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'settings.describe'>>>
|
||||
|
||||
/** settings.update request payload. */
|
||||
export const settingsUpdateRequestSchema = z.object({
|
||||
ns: z.string().min(1),
|
||||
patch: z.record(z.string(), z.unknown()),
|
||||
expectedRevision: z.number().optional(),
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'settings.update'>>>
|
||||
|
||||
/** settings.update response value: the namespace's new redacted view. */
|
||||
export const settingsUpdateValueSchema = settingsNamespaceViewSchema satisfies z.ZodType<Wire<ResponseValue<'settings.update'>>>
|
||||
|
||||
/** settings.replace request payload. */
|
||||
export const settingsReplaceRequestSchema = z.object({
|
||||
ns: z.string().min(1),
|
||||
section: z.record(z.string(), z.unknown()),
|
||||
expectedRevision: z.number().optional(),
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'settings.replace'>>>
|
||||
|
||||
/** One path-addressed edit of settings.mutate. */
|
||||
export const settingsPathOpSchema = z.discriminatedUnion('op', [
|
||||
z.object({ op: z.literal('set'), path: z.array(z.string()), value: z.unknown() }),
|
||||
z.object({ op: z.literal('unset'), path: z.array(z.string()) }),
|
||||
]) as unknown as z.ZodType<Wire<SettingsPathOpView>>
|
||||
|
||||
/** settings.mutate request payload. */
|
||||
export const settingsMutateRequestSchema = z.object({
|
||||
ns: z.string().min(1),
|
||||
ops: z.array(settingsPathOpSchema),
|
||||
expectedRevision: z.number().optional(),
|
||||
}) satisfies z.ZodType<Wire<RequestPayload<'settings.mutate'>>>
|
||||
|
||||
/** settings.mutate response value: the namespace's new redacted view. */
|
||||
export const settingsMutateValueSchema = settingsNamespaceViewSchema satisfies z.ZodType<Wire<ResponseValue<'settings.mutate'>>>
|
||||
|
||||
/** settings.replace response value. */
|
||||
export const settingsReplaceValueSchema = settingsNamespaceViewSchema satisfies z.ZodType<Wire<ResponseValue<'settings.replace'>>>
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* settings domain contract: the web face of the user-settings seam
|
||||
* (`ctx.settings`). Every payload that leaves this domain is redacted by the
|
||||
* seam (`describe({ redactSecrets: true })` semantics): `role('secret')`
|
||||
* fields never ride a response in any layer, and the `secrets` slot list is
|
||||
* how a form learns a write-only field exists and whether it is configured.
|
||||
*/
|
||||
|
||||
import type { RpcRequest, RpcResponse } from './rpc.ts'
|
||||
|
||||
/** One schema-declared secret slot inside a redacted namespace value. */
|
||||
export interface SettingsSecretView {
|
||||
/** Path from the section root to the removed field. */
|
||||
path: string[]
|
||||
/** Whether the slot currently holds a value (the value itself never rides). */
|
||||
set: boolean
|
||||
}
|
||||
|
||||
/** Wire view of one registered settings namespace. */
|
||||
export interface SettingsNamespaceView {
|
||||
/** Namespace key (`llm-deepseek`, `llm-pi-ai`, …). */
|
||||
ns: string
|
||||
/** Serialized schemastery schema envelope (`schema.toJSON()`); rehydrate with `new Schema(json)`. */
|
||||
schema: unknown
|
||||
/** Redacted resolved value (schema defaults → composition base → user layer). */
|
||||
value: unknown
|
||||
/** Redacted composition base layer, when the registrant declared one. */
|
||||
base?: unknown
|
||||
/** Redacted raw user section, when one exists; a field's presence here marks it user-overridden. */
|
||||
user?: unknown
|
||||
/** When the owner applies changes. */
|
||||
applies: 'live' | 'restart'
|
||||
/** Every schema-declared secret slot with its configured state. */
|
||||
secrets: SettingsSecretView[]
|
||||
/**
|
||||
* Monotonic revision of the raw user section this view was read at. Send it
|
||||
* back as `expectedRevision` on a write so a stale editor is refused rather
|
||||
* than silently overwriting a concurrent change.
|
||||
*/
|
||||
revision: number
|
||||
}
|
||||
|
||||
/**
|
||||
* One path-addressed edit carried by `settings.mutate`. `set` writes the
|
||||
* value at the path (creating intermediate objects); `unset` removes it. The
|
||||
* empty path addresses the section root.
|
||||
*/
|
||||
export type SettingsPathOpView =
|
||||
| { op: 'set'; path: string[]; value: unknown }
|
||||
| { op: 'unset'; path: string[] }
|
||||
|
||||
/** Settings-domain unary methods (the map keys settings.* of RpcMethodMap). */
|
||||
export interface SettingsApi {
|
||||
/**
|
||||
* Describe every registered namespace: redacted layered values plus the
|
||||
* serialized schema a client renders its form from. `writable: false`
|
||||
* (read-only provider) tells the client to disable every write control.
|
||||
*/
|
||||
describe(request: RpcRequest<{}>): Promise<RpcResponse<{ writable: boolean; namespaces: SettingsNamespaceView[] }>>
|
||||
|
||||
/**
|
||||
* Merge a patch into one namespace's user layer (validate → persist →
|
||||
* commit). Secret-role fields may be INCLUDED in the patch (write-only
|
||||
* direction); a form that leaves a secret untouched simply omits it and the
|
||||
* merge preserves the stored value. Responds with the namespace's new
|
||||
* redacted view; a schema or storage rejection is `settings-rejected`.
|
||||
*/
|
||||
update(request: RpcRequest<{ ns: string; patch: object; expectedRevision?: number }>): Promise<RpcResponse<SettingsNamespaceView>>
|
||||
|
||||
/**
|
||||
* Replace one namespace's user section wholesale — the removal/reset path a
|
||||
* merge cannot express (`section: {}` resets to composition defaults). Keys
|
||||
* absent from `section` are dropped, secrets included: a client must first
|
||||
* fold the descriptor's `user` layer (and re-supply any secret it wants to
|
||||
* keep) or accept the reset.
|
||||
*/
|
||||
replace(request: RpcRequest<{ ns: string; section: object; expectedRevision?: number }>): Promise<RpcResponse<SettingsNamespaceView>>
|
||||
|
||||
/**
|
||||
* Apply path-addressed edits to one namespace's user section, resolved
|
||||
* against the section as stored — NOT against whatever the caller last
|
||||
* read. This is the removal path for any client holding the redacted
|
||||
* descriptor: it names the field it means, so a secret the wire never
|
||||
* returned cannot be deleted as a side effect. `replace` remains the
|
||||
* deliberate wholesale reset.
|
||||
*/
|
||||
mutate(
|
||||
request: RpcRequest<{ ns: string; ops: SettingsPathOpView[]; expectedRevision?: number }>,
|
||||
): Promise<RpcResponse<SettingsNamespaceView>>
|
||||
}
|
||||
Reference in New Issue
Block a user