fix: refresh Pi credential readiness

This commit is contained in:
2026-08-05 06:30:25 +02:00
parent 7df21b5f21
commit fd1fd2f802
5 changed files with 117 additions and 15 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ export function createPiManagement(config: AppConfig, deps: PiManagementDeps): P
return piProviderCredentialStatus({
provider,
authProviders: loadPiAuthProviders(),
credentialValue: secretValue(config, "THT_MODEL_API_KEY"),
resolveCredentialValue: () => secretValue(config, "THT_MODEL_API_KEY"),
credentialFile: config.modelApiKeyFile,
});
} catch {
+9 -2
View File
@@ -179,7 +179,7 @@ export function buildPiChildEnv(opts: {
export function piProviderCredentialStatus(opts: {
provider?: string;
credentialFile?: string;
credentialValue?: string;
resolveCredentialValue?: () => string | undefined;
authProviders?: ReadonlySet<string>;
fsOps?: CredentialFsOps;
}): PiCredentialStatus {
@@ -187,7 +187,14 @@ export function piProviderCredentialStatus(opts: {
if (!provider || LOCAL_PROVIDERS.has(provider)) return "missing";
if (opts.authProviders?.has(provider)) return "present";
try {
buildPiChildEnv({ ...opts, ambient: {} });
buildPiChildEnv({
ambient: {},
provider,
credentialFile: opts.credentialFile,
credentialValue: opts.resolveCredentialValue?.(),
authProviders: opts.authProviders,
fsOps: opts.fsOps,
});
return "present";
} catch {
return "missing";
+57 -2
View File
@@ -135,15 +135,70 @@ test("credential status reports only present or missing without treating local p
expect(piProviderCredentialStatus({
provider: "deepseek",
authProviders: new Set(["deepseek"]),
credentialValue: "must-not-be-returned",
resolveCredentialValue: () => "must-not-be-returned",
})).toBe("present");
expect(piProviderCredentialStatus({ provider: "deepseek" })).toBe("missing");
expect(piProviderCredentialStatus({
provider: "local-qwen",
credentialValue: "must-not-be-returned",
resolveCredentialValue: () => "must-not-be-returned",
})).toBe("missing");
});
// Catches the generic managed secret being read before providers that self-authenticate or need
// no credential have been classified.
test("credential status never resolves the generic secret for auth-store or local providers", () => {
let secretReads = 0;
const unreadableSecret = () => {
secretReads += 1;
throw new Error("unrelated generic secret is unreadable");
};
expect(piProviderCredentialStatus({
provider: "deepseek",
authProviders: new Set(["deepseek"]),
resolveCredentialValue: unreadableSecret,
})).toBe("present");
expect(piProviderCredentialStatus({
provider: "local-qwen",
resolveCredentialValue: unreadableSecret,
})).toBe("missing");
expect(secretReads).toBe(0);
});
// Catches generic hosted providers skipping their managed-secret source or treating an absent or
// unreadable source as credentialed.
test("credential status resolves the generic secret only for providers that require it", () => {
let presentReads = 0;
expect(piProviderCredentialStatus({
provider: "openai",
resolveCredentialValue: () => {
presentReads += 1;
return "managed-openai-key";
},
})).toBe("present");
expect(presentReads).toBe(1);
let missingReads = 0;
expect(piProviderCredentialStatus({
provider: "openai",
resolveCredentialValue: () => {
missingReads += 1;
return undefined;
},
})).toBe("missing");
expect(missingReads).toBe(1);
let unreadableReads = 0;
expect(piProviderCredentialStatus({
provider: "openai",
resolveCredentialValue: () => {
unreadableReads += 1;
throw new Error("generic secret is unreadable");
},
})).toBe("missing");
expect(unreadableReads).toBe(1);
});
test("bundle value is injected without exposing bundle metadata to Pi", () => {
const env = buildPiChildEnv({
ambient: {