cleanup(config): remove literal credential compatibility residue
Adapter schemas now carry only credential references, but the Models join, onboarding readiness, shipped overlays, SDK scaffolding, fixtures, and active decision prose still treated a redacted literal apiKey as a supported compatibility state. That residue made an unsupported field look contractual and pinned Schemastery silent-dropping as behavior. Delete those branches and examples, and let compositions and scaffolds use adapter-owned reference and environment resolution. Do not add a tombstone validator or change generic unknown-key behavior: literal adapter credentials have no migration contract to preserve.
This commit is contained in:
@@ -80,8 +80,8 @@ function renderProviderEditor({ target, ...props }: ProviderEditorRenderProps):
|
||||
* Remove one user-added provider and its page-managed credential. Credential
|
||||
* removal comes first so a second-step failure leaves the provider row visible
|
||||
* and the whole operation safely retryable; both unsets are idempotent.
|
||||
* The settings removal names the profile rather than rebuilding its redacted
|
||||
* namespace, which would drop literal secrets stored elsewhere.
|
||||
* The settings removal names the profile rather than rebuilding its whole
|
||||
* namespace from a partial view.
|
||||
* @param api - settings and credential wire faces.
|
||||
* @param controller - the page store to refresh.
|
||||
* @param target - the provider's settings address and optional managed credential.
|
||||
@@ -112,16 +112,14 @@ export async function removeProviderProfile(
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a whole-section provider still needs its first key: nothing marks
|
||||
* the credential configured and no literal `apiKey` is stored, so the page
|
||||
* opens the setup card instead of showing a row.
|
||||
* Whether a whole-section provider still needs its first key: an unconfigured
|
||||
* credential opens the setup card instead of showing a row.
|
||||
* @param row - the joined provider row.
|
||||
* @returns whether to render the setup card.
|
||||
*/
|
||||
export function needsSetup(row: ProviderRow): boolean {
|
||||
if (row.entry.settingsPath.length > 0) return false
|
||||
if (row.credential?.configured === true) return false
|
||||
return !row.literalApiKeyConfigured
|
||||
return row.credential?.configured !== true
|
||||
}
|
||||
|
||||
function targetOf(row: ProviderRow): EditorTarget {
|
||||
@@ -264,7 +262,7 @@ function Loaded({ injected }: { injected: ModelsSectionInjected }): ReactNode {
|
||||
)
|
||||
}
|
||||
const open = !adding && editing?.provider === row.entry.provider
|
||||
const credentialConfigured = row.literalApiKeyConfigured || row.credential?.configured === true
|
||||
const credentialConfigured = row.credential?.configured === true
|
||||
const credentialMissing = !credentialConfigured
|
||||
&& row.apiKeyEnv !== undefined
|
||||
&& row.credential?.configured === false
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
* both families, `reasoningEffort` for deepseek / `reasoning` for pi-ai, and
|
||||
* DeepSeek's id/name/context-window model catalog). Everything else stays
|
||||
* owned by `settings.yaml`. Profile edits land as minimal `settings.mutate`
|
||||
* path ops against the stored section — the card reads the redacted
|
||||
* descriptor, so it names only the fields it can see and a stored literal
|
||||
* secret is never collaterally removed.
|
||||
* path ops against the stored section — the card names only the fields it can
|
||||
* see instead of rebuilding the whole subtree from a partial descriptor.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
@@ -80,10 +79,9 @@ function draftAt(namespace: SettingsNamespaceView, path: readonly string[]): Rec
|
||||
|
||||
/**
|
||||
* The minimal path ops carrying `after` over `before`, both as the card sees
|
||||
* them (that is, redacted). Only keys the card observed are named: a stored
|
||||
* `role('secret')` field appears in neither side, so it produces no op and
|
||||
* survives the write — the whole reason edits are path-addressed rather than
|
||||
* a rebuilt section.
|
||||
* them. Only keys the card observed are named; fields absent from both sides
|
||||
* produce no op, which is why edits are path-addressed rather than a rebuilt
|
||||
* section.
|
||||
* @param base - path of the edited subtree inside the user section.
|
||||
* @param before - the subtree as loaded, or undefined when it is new.
|
||||
* @param after - the subtree as edited.
|
||||
@@ -205,9 +203,8 @@ export function ProviderEditor(props: ProviderEditorProps): ReactNode {
|
||||
/**
|
||||
* The write for this card, or a failure message. Every edit travels as
|
||||
* path ops against the STORED section: the draft comes from the redacted
|
||||
* descriptor, so a wholesale replace rebuilt from it would delete the
|
||||
* literal secrets the wire never returned. Ops name only the fields this
|
||||
* card can see, so a stored secret is untouched by construction.
|
||||
* descriptor, so a wholesale replace rebuilt from it could delete fields
|
||||
* outside the card. Ops name only the fields this card can see.
|
||||
*/
|
||||
const applyOnce = async (): Promise<string | undefined> => {
|
||||
const ns = namespace.ns
|
||||
|
||||
@@ -31,8 +31,6 @@ export interface ProviderRow {
|
||||
apiKeyEnv: string | undefined
|
||||
/** Credential state for {@link apiKeyEnv}, once described. */
|
||||
credential: CredentialView | undefined
|
||||
/** Whether the redacted secret sidecar reports an effective literal `apiKey`. */
|
||||
literalApiKeyConfigured: boolean
|
||||
}
|
||||
|
||||
/** Page snapshot. */
|
||||
@@ -97,19 +95,6 @@ function apiKeyEnvOf(namespace: SettingsNamespaceView | undefined, path: readonl
|
||||
return typeof ref === 'string' && ref.length > 0 ? ref : undefined
|
||||
}
|
||||
|
||||
/** Whether one namespace's redacted sidecar reports a set literal API key. */
|
||||
function literalApiKeyConfigured(
|
||||
namespace: SettingsNamespaceView | undefined,
|
||||
path: readonly string[],
|
||||
): boolean {
|
||||
if (namespace === undefined) return false
|
||||
const secretPath = [...path, 'apiKey']
|
||||
return namespace.secrets.some(secret =>
|
||||
secret.set
|
||||
&& secret.path.length === secretPath.length
|
||||
&& secret.path.every((key, index) => key === secretPath[index]))
|
||||
}
|
||||
|
||||
/** The models settings page controller (one per settings surface). */
|
||||
export class ModelsSettingsStore {
|
||||
/** The snapshot the section renders from (uSES-safe store). */
|
||||
@@ -170,7 +155,6 @@ export class ModelsSettingsStore {
|
||||
removable,
|
||||
apiKeyEnv: apiKeyEnvOf(namespace, entry.settingsPath),
|
||||
credential: undefined,
|
||||
literalApiKeyConfigured: literalApiKeyConfigured(namespace, entry.settingsPath),
|
||||
}
|
||||
})
|
||||
const refs = [...new Set(rows.flatMap(row => row.apiKeyEnv === undefined ? [] : [row.apiKeyEnv]))]
|
||||
@@ -257,7 +241,6 @@ export function deepSeekReadiness(state: ModelsSettingsState): DeepSeekReadiness
|
||||
reason: 'settings-unavailable',
|
||||
}
|
||||
}
|
||||
if (row.literalApiKeyConfigured) return { kind: 'configured' }
|
||||
if (row.apiKeyEnv === undefined) {
|
||||
return {
|
||||
kind: 'unavailable',
|
||||
|
||||
Reference in New Issue
Block a user