





























@@ -11,6 +11,7 @@ import {
1111} from "@mariozechner/pi-ai";
1212import { afterEach, describe, expect, it } from "vitest";
1313import { resolveAgentWorkspaceDir, resolveDefaultAgentDir } from "../agents/agent-scope.js";
14+import { externalCliDiscoveryForProviders } from "../agents/auth-profiles/external-cli-discovery.js";
1415import { ensureAuthProfileStore, saveAuthProfileStore } from "../agents/auth-profiles/store.js";
1516import type { AuthProfileStore } from "../agents/auth-profiles/types.js";
1617import {
@@ -75,6 +76,10 @@ const GATEWAY_LIVE_PROBE_TIMEOUT_MS = Math.max(
757630_000,
7677toInt(process.env.OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS, 90_000),
7778);
79+const GATEWAY_LIVE_SETUP_TIMEOUT_MS = Math.max(
80+1_000,
81+toInt(process.env.OPENCLAW_LIVE_GATEWAY_SETUP_TIMEOUT_MS, 60_000),
82+);
7883const GATEWAY_LIVE_MODEL_TIMEOUT_MS = resolveGatewayLiveModelTimeoutMs();
7984const GATEWAY_LIVE_HEARTBEAT_MS = Math.max(
80851_000,
@@ -200,7 +205,7 @@ function isGatewayLiveModelTimeout(error: string): boolean {
200205async function withGatewayLiveTimeout<T>(params: {
201206operation: Promise<T>;
202207timeoutMs: number;
203-timeoutLabel: "probe" | "model";
208+timeoutLabel: "setup" | "probe" | "model";
204209context: string;
205210}): Promise<T> {
206211let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
@@ -239,6 +244,19 @@ async function withGatewayLiveTimeout<T>(params: {
239244}
240245}
241246247+async function withGatewayLiveSetupTimeout<T>(
248+operation: Promise<T>,
249+context: string,
250+timeoutMs = GATEWAY_LIVE_SETUP_TIMEOUT_MS,
251+): Promise<T> {
252+return await withGatewayLiveTimeout({
253+ operation,
254+ timeoutMs,
255+timeoutLabel: "setup",
256+ context,
257+});
258+}
259+242260async function withGatewayLiveProbeTimeout<T>(operation: Promise<T>, context: string): Promise<T> {
243261return await withGatewayLiveTimeout({
244262 operation,
@@ -2368,16 +2386,62 @@ describeLive("gateway live (dev agent, profile keys)", () => {
23682386"runs meaningful prompts across models with available keys",
23692387async () =>
23702388await withSuppressedGatewayLiveWarnings(async () => {
2371-logProgress("[all-models] discover candidates");
2389+const providerList = providerFilterList();
2390+const providerLog = providerList?.join(",") ?? "all";
2391+logProgress(`[all-models] discover candidates providers=${providerLog}`);
2392+logProgress("[all-models] loading config");
23722393clearRuntimeConfigSnapshot();
2373-const cfg = getRuntimeConfig();
2374-await ensureOpenClawModelsJson(cfg, undefined, {
2375-providerDiscoveryProviderIds: providerFilterList(),
2376-});
2394+const cfg = await withGatewayLiveSetupTimeout(
2395+Promise.resolve().then(() => getRuntimeConfig()),
2396+"[all-models] load config",
2397+);
2398+const workspaceDir = resolveAgentWorkspaceDir(cfg, DEFAULT_AGENT_ID);
2399+logProgress("[all-models] preparing models.json");
2400+await withGatewayLiveSetupTimeout(
2401+ensureOpenClawModelsJson(cfg, undefined, {
2402+ workspaceDir,
2403+ ...(providerList ? { providerDiscoveryProviderIds: providerList } : {}),
2404+providerDiscoveryEntriesOnly: true,
2405+}),
2406+"[all-models] prepare models.json",
2407+);
2377240823782409const agentDir = resolveDefaultAgentDir(cfg);
2379-const authStorage = discoverAuthStorage(agentDir);
2410+const externalCli = providerList
2411+ ? externalCliDiscoveryForProviders({ cfg, providers: providerList })
2412+ : undefined;
2413+logProgress("[all-models] loading auth profiles");
2414+const authProfileStore = await withGatewayLiveSetupTimeout(
2415+Promise.resolve().then(() =>
2416+ensureAuthProfileStore(agentDir, {
2417+allowKeychainPrompt: false,
2418+ ...(externalCli ? { externalCli } : {}),
2419+}),
2420+),
2421+"[all-models] load auth profiles",
2422+);
2423+const authStorage = await withGatewayLiveSetupTimeout(
2424+Promise.resolve().then(() =>
2425+discoverAuthStorage(agentDir, {
2426+config: cfg,
2427+env: process.env,
2428+ ...(externalCli ? { externalCli } : {}),
2429+ ...(providerList
2430+ ? {
2431+skipExternalAuthProfiles: true,
2432+syntheticAuthProviderRefs: [],
2433+}
2434+ : {}),
2435+}),
2436+),
2437+"[all-models] load auth storage",
2438+);
2439+logProgress("[all-models] loading model registry");
23802440const modelRegistry = discoverModels(authStorage, agentDir);
2441+const all = await withGatewayLiveSetupTimeout(
2442+Promise.resolve().then(() => modelRegistry.getAll()),
2443+"[all-models] load model registry",
2444+);
2381244523822446const rawModels = process.env.OPENCLAW_LIVE_GATEWAY_MODELS?.trim();
23832447const useModern = !rawModels || rawModels === "modern" || rawModels === "all";
@@ -2399,7 +2463,6 @@ describeLive("gateway live (dev agent, profile keys)", () => {
23992463})
24002464 : null;
24012465if (!wanted) {
2402-const all = modelRegistry.getAll();
24032466wanted = filter
24042467 ? all.filter((m) => targetMatcher.matchesModel(m.provider, m.id))
24052468 : all.filter(
@@ -2413,6 +2476,7 @@ describeLive("gateway live (dev agent, profile keys)", () => {
24132476}) && isHighSignalLiveModelRef({ provider: m.provider, id: m.id }),
24142477);
24152478}
2479+logProgress(`[all-models] wanted=${wanted.length} total=${all.length}`);
2416248024172481const candidates: Array<Model<Api>> = [];
24182482const skipped: Array<{ model: string; error: string }> = [];
@@ -2425,11 +2489,18 @@ describeLive("gateway live (dev agent, profile keys)", () => {
24252489}
24262490const modelRef = `${model.provider}/${model.id}`;
24272491try {
2428-const apiKeyInfo = await getApiKeyForModel({
2429- model,
2430- cfg,
2431-credentialPrecedence: LIVE_CREDENTIAL_PRECEDENCE,
2432-});
2492+const apiKeyInfo = await withGatewayLiveSetupTimeout(
2493+getApiKeyForModel({
2494+ model,
2495+ cfg,
2496+store: authProfileStore,
2497+ agentDir,
2498+ workspaceDir,
2499+credentialPrecedence: LIVE_CREDENTIAL_PRECEDENCE,
2500+}),
2501+`[all-models] auth ${modelRef}`,
2502+GATEWAY_LIVE_PROBE_TIMEOUT_MS,
2503+);
24332504if (REQUIRE_PROFILE_KEYS && !apiKeyInfo.source.startsWith("profile:")) {
24342505skipped.push({
24352506model: modelRef,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。