fix: usage script provider credential resolution (#1479)

The JS-script usage path resolved {{apiKey}}/{{baseUrl}} with env-only
field guessing, so apps that store credentials elsewhere (Codex:
auth.OPENAI_API_KEY + config.toml base_url) always got empty values and
custom-template queries failed despite a fully configured provider.

- query_usage / test_usage_script now delegate to
  Provider::resolve_usage_credentials, the same per-app resolver used by
  the native balance/coding-plan path and mirrored by the frontend
  getProviderCredentials; explicit non-empty script values still win
- test_usage_script loads the provider and applies the same fallback,
  so testing matches what a saved script does
- the custom-template variable preview shows the effective values
  (script overrides first, then provider config) instead of always
  showing provider credentials
- extract_codex_base_url documents and test-locks the frontend-mirror
  invariant: non-active [model_providers.*] sections are never read

Reworked from the original patch to reuse the existing resolver instead
of duplicating per-app extraction.

Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
pa001024
2026-06-10 22:57:27 +08:00
committed by GitHub
parent 4f911727d2
commit 9ea303b224
4 changed files with 189 additions and 60 deletions
+15 -8
View File
@@ -331,6 +331,14 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
const [testing, setTesting] = useState(false);
// {{apiKey}}/{{baseUrl}} 实际注入值,镜像后端 resolve_script_credentials 的
// 优先级:脚本配置中的显式非空值优先(旧配置可能携带),否则回退供应商凭据。
const effectiveScriptCredentials = {
apiKey: script.apiKey?.trim() || providerCredentials.apiKey,
baseUrl:
script.baseUrl?.trim().replace(/\/+$/, "") || providerCredentials.baseUrl,
};
// 🔧 失焦时的验证(严格)- 仅确保有效整数
const validateTimeout = (value: string): number => {
const num = Number(value);
@@ -678,13 +686,12 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
const preset = PRESET_TEMPLATES[presetName];
if (preset !== undefined) {
if (presetName === TEMPLATE_TYPES.CUSTOM) {
// 🔧 自定义模式:用户应该在脚本中直接写完整 URL 和凭证,而不是依赖变量替换
// 这样可以避免同源检查导致的问题
// 如果用户想使用变量,需要手动在配置中设置 baseUrl/apiKey
// 自定义模板没有凭证输入框:清空显式覆盖值后,测试与真实查询
// 都会在后端回退到供应商配置(Provider::resolve_usage_credentials),
// 与下方“支持的变量”区域展示的 {{apiKey}}/{{baseUrl}} 取值一致。
setScript({
...script,
code: preset,
// 清除凭证,用户可选择手动输入或保持空
apiKey: undefined,
baseUrl: undefined,
accessToken: undefined,
@@ -886,9 +893,9 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
{"{{baseUrl}}"}
</code>
<span className="text-muted-foreground/50">=</span>
{providerCredentials.baseUrl ? (
{effectiveScriptCredentials.baseUrl ? (
<code className="text-foreground/70 break-all font-mono">
{providerCredentials.baseUrl}
{effectiveScriptCredentials.baseUrl}
</code>
) : (
<span className="text-muted-foreground/50 italic">
@@ -903,11 +910,11 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
{"{{apiKey}}"}
</code>
<span className="text-muted-foreground/50">=</span>
{providerCredentials.apiKey ? (
{effectiveScriptCredentials.apiKey ? (
<>
{showApiKey ? (
<code className="text-foreground/70 break-all font-mono">
{providerCredentials.apiKey}
{effectiveScriptCredentials.apiKey}
</code>
) : (
<code className="text-foreground/70 font-mono">