

























@@ -77,6 +77,38 @@ function isRecord(value: unknown): value is Record<string, unknown> {
7777return !!value && typeof value === "object" && !Array.isArray(value);
7878}
797980+function normalizeOptionalCredentialString(value: unknown): string | undefined {
81+if (typeof value !== "string") {
82+return undefined;
83+}
84+const trimmed = value.trim();
85+return trimmed ? value : undefined;
86+}
87+88+function normalizeOptionalCredentialBoolean(value: unknown): boolean | undefined {
89+return typeof value === "boolean" ? value : undefined;
90+}
91+92+function normalizeExpiryField(value: unknown): number | undefined {
93+if (value === undefined) {
94+return undefined;
95+}
96+return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
97+}
98+99+function normalizeCredentialMetadata(value: unknown): Record<string, string> | undefined {
100+if (!isRecord(value)) {
101+return undefined;
102+}
103+const metadata: Record<string, string> = {};
104+for (const [key, entry] of Object.entries(value)) {
105+if (typeof entry === "string") {
106+metadata[key] = entry;
107+}
108+}
109+return Object.keys(metadata).length > 0 ? metadata : undefined;
110+}
111+80112function normalizeSecretBackedField(params: {
81113entry: Record<string, unknown>;
82114valueField: "key" | "token";
@@ -93,6 +125,25 @@ function normalizeSecretBackedField(params: {
93125delete params.entry[params.valueField];
94126}
95127128+function normalizeCommonCredentialFields(entry: Record<string, unknown>): Record<string, unknown> {
129+const normalized: Record<string, unknown> = {
130+provider: typeof entry.provider === "string" ? normalizeProviderId(entry.provider) : "",
131+};
132+const copyToAgents = normalizeOptionalCredentialBoolean(entry.copyToAgents);
133+if (copyToAgents !== undefined) {
134+normalized.copyToAgents = copyToAgents;
135+}
136+const email = normalizeOptionalCredentialString(entry.email);
137+if (email !== undefined) {
138+normalized.email = email;
139+}
140+const displayName = normalizeOptionalCredentialString(entry.displayName);
141+if (displayName !== undefined) {
142+normalized.displayName = displayName;
143+}
144+return normalized;
145+}
146+96147function normalizeRawCredentialEntry(raw: Record<string, unknown>): Partial<AuthProfileCredential> {
97148const entry = { ...raw } as Record<string, unknown>;
98149if (!("type" in entry) && typeof entry["mode"] === "string") {
@@ -103,6 +154,73 @@ function normalizeRawCredentialEntry(raw: Record<string, unknown>): Partial<Auth
103154}
104155normalizeSecretBackedField({ entry, valueField: "key", refField: "keyRef" });
105156normalizeSecretBackedField({ entry, valueField: "token", refField: "tokenRef" });
157+if (entry.type === "api_key") {
158+const normalized: Record<string, unknown> = {
159+type: "api_key",
160+ ...normalizeCommonCredentialFields(entry),
161+};
162+const key = normalizeOptionalCredentialString(entry.key);
163+const keyRef = coerceSecretRef(entry.keyRef);
164+const metadata = normalizeCredentialMetadata(entry.metadata);
165+if (key !== undefined) {
166+normalized.key = key;
167+}
168+if (keyRef) {
169+normalized.keyRef = keyRef;
170+}
171+if (metadata) {
172+normalized.metadata = metadata;
173+}
174+return normalized as Partial<AuthProfileCredential>;
175+}
176+if (entry.type === "token") {
177+const normalized: Record<string, unknown> = {
178+type: "token",
179+ ...normalizeCommonCredentialFields(entry),
180+};
181+const token = normalizeOptionalCredentialString(entry.token);
182+const tokenRef = coerceSecretRef(entry.tokenRef);
183+const expires = normalizeExpiryField(entry.expires);
184+if (token !== undefined) {
185+normalized.token = token;
186+}
187+if (tokenRef) {
188+normalized.tokenRef = tokenRef;
189+}
190+if (expires !== undefined) {
191+normalized.expires = expires;
192+}
193+return normalized as Partial<AuthProfileCredential>;
194+}
195+if (entry.type === "oauth") {
196+const normalized: Record<string, unknown> = {
197+type: "oauth",
198+ ...normalizeCommonCredentialFields(entry),
199+};
200+for (const field of [
201+"access",
202+"refresh",
203+"idToken",
204+"clientId",
205+"enterpriseUrl",
206+"projectId",
207+"accountId",
208+"chatgptPlanType",
209+] as const) {
210+const value = normalizeOptionalCredentialString(entry[field]);
211+if (value !== undefined) {
212+normalized[field] = value;
213+}
214+}
215+const expires = normalizeExpiryField(entry.expires);
216+if (expires !== undefined) {
217+normalized.expires = expires;
218+}
219+if (isOAuthProfileSecretRef(entry.oauthRef)) {
220+normalized.oauthRef = entry.oauthRef;
221+}
222+return normalized;
223+}
106224return entry as Partial<AuthProfileCredential>;
107225}
108226@@ -528,22 +646,23 @@ function parseCredentialEntry(
528646raw: unknown,
529647fallbackProvider?: string,
530648): { ok: true; credential: AuthProfileCredential } | { ok: false; reason: CredentialRejectReason } {
531-if (!raw || typeof raw !== "object") {
649+if (!isRecord(raw)) {
532650return { ok: false, reason: "non_object" };
533651}
534-const typed = normalizeRawCredentialEntry(raw as Record<string, unknown>);
652+const typed = normalizeRawCredentialEntry(raw);
535653if (!AUTH_PROFILE_TYPES.has(typed.type as AuthProfileCredential["type"])) {
536654return { ok: false, reason: "invalid_type" };
537655}
538656const provider = typed.provider ?? fallbackProvider;
539-if (typeof provider !== "string" || provider.trim().length === 0) {
657+const normalizedProvider = typeof provider === "string" ? normalizeProviderId(provider) : "";
658+if (!normalizedProvider) {
540659return { ok: false, reason: "missing_provider" };
541660}
542661return {
543662ok: true,
544663credential: {
545664 ...typed,
546- provider,
665+provider: normalizedProvider,
547666} as AuthProfileCredential,
548667};
549668}
@@ -609,8 +728,9 @@ export function coercePersistedAuthProfileStore(raw: unknown): AuthProfileStore
609728normalized[key] = parsed.credential;
610729}
611730warnRejectedCredentialEntries("auth-profiles.json", rejected);
731+const version = Number(record.version ?? AUTH_STORE_VERSION);
612732return {
613-version: Number(record.version ?? AUTH_STORE_VERSION),
733+version: Number.isFinite(version) && version > 0 ? version : AUTH_STORE_VERSION,
614734profiles: normalized,
615735 ...coerceAuthProfileState(record),
616736};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。