traderclaw-cli 1.0.60 → 1.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,8 +14,42 @@ import { resolvePluginPackageRoot } from "./resolve-plugin-root.mjs";
14
14
 
15
15
  const execFileAsync = promisify(execFile);
16
16
 
17
- /** Parallel per-provider `openclaw models list` wall time ~max(single), not sum of all providers. */
18
- const OPENCLAW_MODELS_PER_PROVIDER_TIMEOUT_MS = 16_000;
17
+ /** Fast wizard catalog lookup: prefer one full list, then only probe key providers. */
18
+ const OPENCLAW_MODELS_FLAT_TIMEOUT_MS = 7_500;
19
+ const OPENCLAW_MODELS_PER_PROVIDER_TIMEOUT_MS = 4_500;
20
+ const WIZARD_PRIORITY_PROVIDERS = [
21
+ "anthropic",
22
+ "openai",
23
+ "openrouter",
24
+ "google",
25
+ "xai",
26
+ "deepseek",
27
+ "groq",
28
+ "mistral",
29
+ ];
30
+ const WIZARD_PROVIDER_PRIORITY = [
31
+ ...WIZARD_PRIORITY_PROVIDERS,
32
+ "perplexity",
33
+ "together",
34
+ "openai-codex",
35
+ "google-vertex",
36
+ "amazon-bedrock",
37
+ "vercel-ai-gateway",
38
+ "nvidia",
39
+ "moonshot",
40
+ "qwen",
41
+ "cerebras",
42
+ "minimax",
43
+ ];
44
+ let wizardLlmCatalogPromise = null;
45
+
46
+ function compareWizardProviderPriority(a, b) {
47
+ const ai = WIZARD_PROVIDER_PRIORITY.indexOf(a);
48
+ const bi = WIZARD_PROVIDER_PRIORITY.indexOf(b);
49
+ const aRank = ai >= 0 ? ai : Number.MAX_SAFE_INTEGER;
50
+ const bRank = bi >= 0 ? bi : Number.MAX_SAFE_INTEGER;
51
+ return aRank - bRank || a.localeCompare(b);
52
+ }
19
53
 
20
54
  const PLUGIN_ROOT = resolvePluginPackageRoot(import.meta.url);
21
55
  const PLUGIN_PACKAGE_JSON = JSON.parse(readFileSync(join(PLUGIN_ROOT, "package.json"), "utf-8"));
@@ -1871,11 +1905,23 @@ async function loadWizardLlmCatalogAsync() {
1871
1905
  providers: [
1872
1906
  {
1873
1907
  id: "anthropic",
1874
- models: [{ id: "anthropic/claude-sonnet-4-6", name: "Claude Sonnet 4.6 (recommended default)" }],
1908
+ models: [
1909
+ { id: "anthropic/claude-sonnet-4-6", name: "Claude Sonnet 4.6 (recommended default)" },
1910
+ { id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6" },
1911
+ { id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5" },
1912
+ ],
1875
1913
  },
1876
1914
  {
1877
1915
  id: "openai",
1878
- models: [{ id: "openai/gpt-5.4", name: "GPT-5.4" }],
1916
+ models: [
1917
+ { id: "openai/gpt-5.4", name: "GPT-5.4 (recommended default)" },
1918
+ { id: "openai/gpt-5.4-mini", name: "GPT-5.4 mini" },
1919
+ { id: "openai/gpt-5.4-nano", name: "GPT-5.4 nano" },
1920
+ ],
1921
+ },
1922
+ {
1923
+ id: "openai-codex",
1924
+ models: [{ id: "openai-codex/gpt-5-codex", name: "GPT-5 Codex" }],
1879
1925
  },
1880
1926
  {
1881
1927
  id: "xai",
@@ -1901,6 +1947,22 @@ async function loadWizardLlmCatalogAsync() {
1901
1947
  id: "mistral",
1902
1948
  models: [{ id: "mistral/mistral-large-latest", name: "Mistral Large" }],
1903
1949
  },
1950
+ {
1951
+ id: "perplexity",
1952
+ models: [{ id: "perplexity/sonar-pro", name: "Sonar Pro" }],
1953
+ },
1954
+ {
1955
+ id: "together",
1956
+ models: [{ id: "together/moonshotai/Kimi-K2.5", name: "Kimi K2.5" }],
1957
+ },
1958
+ {
1959
+ id: "nvidia",
1960
+ models: [{ id: "nvidia/llama-3.3-70b-instruct", name: "Llama 3.3 70B Instruct" }],
1961
+ },
1962
+ {
1963
+ id: "qwen",
1964
+ models: [{ id: "qwen/qwen3-235b-a22b", name: "Qwen3 235B A22B" }],
1965
+ },
1904
1966
  ],
1905
1967
  };
1906
1968
 
@@ -1908,7 +1970,8 @@ async function loadWizardLlmCatalogAsync() {
1908
1970
  return { ...fallback, warning: "openclaw_not_found" };
1909
1971
  }
1910
1972
 
1911
- const providerIds = [...supportedProviders].sort((a, b) => a.localeCompare(b));
1973
+ const providerIds = [...supportedProviders].sort(compareWizardProviderPriority);
1974
+ const priorityProviderIds = providerIds.filter((id) => WIZARD_PRIORITY_PROVIDERS.includes(id));
1912
1975
 
1913
1976
  async function fetchModelsForProvider(provider) {
1914
1977
  try {
@@ -1928,6 +1991,36 @@ async function loadWizardLlmCatalogAsync() {
1928
1991
  }
1929
1992
  }
1930
1993
 
1994
+ function seedFallbackProviderMap() {
1995
+ return new Map(
1996
+ fallback.providers.map((entry) => [
1997
+ entry.id,
1998
+ entry.models.map((model) => ({ ...model })),
1999
+ ]),
2000
+ );
2001
+ }
2002
+
2003
+ function mergeCatalogModelsIntoMap(providerMap, models, expectedProvider = "") {
2004
+ let added = 0;
2005
+ for (const entry of models) {
2006
+ if (!entry || typeof entry.key !== "string") continue;
2007
+ const modelId = String(entry.key);
2008
+ const slash = modelId.indexOf("/");
2009
+ if (slash <= 0 || slash === modelId.length - 1) continue;
2010
+ const provider = modelId.slice(0, slash);
2011
+ if (!supportedProviders.has(provider)) continue;
2012
+ if (expectedProvider && provider !== expectedProvider) continue;
2013
+ const existing = providerMap.get(provider) || [];
2014
+ existing.push({
2015
+ id: modelId,
2016
+ name: typeof entry.name === "string" && entry.name.trim() ? entry.name : modelId,
2017
+ });
2018
+ providerMap.set(provider, existing);
2019
+ added += 1;
2020
+ }
2021
+ return added;
2022
+ }
2023
+
1931
2024
  function buildProvidersFromMap(providerMap) {
1932
2025
  return providerIds
1933
2026
  .map((id) => {
@@ -1951,86 +2044,80 @@ async function loadWizardLlmCatalogAsync() {
1951
2044
  encoding: "utf-8",
1952
2045
  stdio: ["ignore", "pipe", "pipe"],
1953
2046
  maxBuffer: 50 * 1024 * 1024,
1954
- timeout: 45_000,
2047
+ timeout: OPENCLAW_MODELS_FLAT_TIMEOUT_MS,
1955
2048
  env: NO_COLOR_ENV,
1956
2049
  });
1957
2050
  const parsed = extractJson(raw);
1958
- if (!parsed) return;
2051
+ if (!parsed) return 0;
1959
2052
  const models = Array.isArray(parsed?.models) ? parsed.models : [];
1960
- for (const entry of models) {
1961
- if (!entry || typeof entry.key !== "string") continue;
1962
- const modelId = String(entry.key);
1963
- const slash = modelId.indexOf("/");
1964
- if (slash <= 0 || slash === modelId.length - 1) continue;
1965
- const provider = modelId.slice(0, slash);
1966
- if (!supportedProviders.has(provider)) continue;
1967
- const existing = providerMap.get(provider) || [];
1968
- existing.push({
1969
- id: modelId,
1970
- name: typeof entry.name === "string" && entry.name.trim() ? entry.name : modelId,
1971
- });
1972
- providerMap.set(provider, existing);
1973
- }
2053
+ return mergeCatalogModelsIntoMap(providerMap, models);
1974
2054
  }
1975
2055
 
1976
2056
  try {
1977
2057
  const t0 = Date.now();
1978
- const batches = await Promise.all(providerIds.map((p) => fetchModelsForProvider(p)));
1979
- const providerMap = new Map();
1980
-
1981
- for (const batch of batches) {
1982
- if (batch.error || !batch.stdout) continue;
1983
- const parsed = extractJson(batch.stdout);
1984
- if (!parsed) continue;
1985
- const models = Array.isArray(parsed?.models) ? parsed.models : [];
1986
- const want = batch.provider;
1987
- for (const entry of models) {
1988
- if (!entry || typeof entry.key !== "string") continue;
1989
- const modelId = String(entry.key);
1990
- const slash = modelId.indexOf("/");
1991
- if (slash <= 0 || slash === modelId.length - 1) continue;
1992
- const provider = modelId.slice(0, slash);
1993
- if (provider !== want) continue;
1994
- const existing = providerMap.get(provider) || [];
1995
- existing.push({
1996
- id: modelId,
1997
- name: typeof entry.name === "string" && entry.name.trim() ? entry.name : modelId,
1998
- });
1999
- providerMap.set(provider, existing);
2058
+ const liveProviderMap = new Map();
2059
+ let catalogStrategy = "flat_all";
2060
+ let flatError = "";
2061
+ let liveAdded = 0;
2062
+ try {
2063
+ liveAdded = mergeFlatCatalogIntoMap(liveProviderMap);
2064
+ } catch (err) {
2065
+ flatError = err instanceof Error ? err.message : String(err);
2066
+ }
2067
+
2068
+ let batches = [];
2069
+ if (liveAdded === 0) {
2070
+ catalogStrategy = "priority_parallel";
2071
+ batches = await Promise.all(priorityProviderIds.map((p) => fetchModelsForProvider(p)));
2072
+ for (const batch of batches) {
2073
+ if (batch.error || !batch.stdout) continue;
2074
+ const parsed = extractJson(batch.stdout);
2075
+ if (!parsed) continue;
2076
+ const models = Array.isArray(parsed?.models) ? parsed.models : [];
2077
+ liveAdded += mergeCatalogModelsIntoMap(liveProviderMap, models, batch.provider);
2000
2078
  }
2001
2079
  }
2002
2080
 
2003
- let providers = buildProvidersFromMap(providerMap);
2004
- let catalogStrategy = "parallel";
2005
-
2081
+ let providers = buildProvidersFromMap(liveProviderMap);
2006
2082
  if (providers.length === 0) {
2007
- catalogStrategy = "legacy_fallback";
2008
- try {
2009
- mergeFlatCatalogIntoMap(providerMap);
2010
- providers = buildProvidersFromMap(providerMap);
2011
- } catch (legacyErr) {
2012
- console.error(
2013
- `[traderclaw] loadWizardLlmCatalog legacy fallback failed: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}`,
2083
+ const failedParallel = batches.filter((b) => b.error).length;
2084
+ const details = [];
2085
+ if (flatError) details.push(`flat list failed: ${flatError.slice(0, 160)}`);
2086
+ if (failedParallel > 0) {
2087
+ details.push(
2088
+ `${failedParallel}/${priorityProviderIds.length} priority provider lookups failed`,
2014
2089
  );
2015
2090
  }
2091
+ return {
2092
+ ...fallback,
2093
+ warning: `openclaw_model_catalog_unavailable${details.length ? ` (${details.join("; ")})` : ""}`,
2094
+ };
2016
2095
  }
2017
2096
 
2018
- if (providers.length === 0) {
2019
- const failedParallel = batches.filter((b) => b.error).length;
2020
- const hint =
2021
- failedParallel > 0
2022
- ? ` (${failedParallel}/${batches.length} per-provider openclaw calls failed — check OpenClaw version supports: openclaw models list --all --provider <id> --json)`
2023
- : "";
2024
- return { ...fallback, warning: `openclaw_model_catalog_empty${hint}` };
2097
+ const mergedProviderMap = new Map(liveProviderMap);
2098
+ for (const [provider, models] of seedFallbackProviderMap()) {
2099
+ if (!mergedProviderMap.has(provider) || (mergedProviderMap.get(provider) || []).length === 0) {
2100
+ mergedProviderMap.set(provider, models);
2101
+ }
2025
2102
  }
2103
+ providers = buildProvidersFromMap(mergedProviderMap);
2026
2104
 
2027
2105
  const elapsedMs = Date.now() - t0;
2106
+ const source =
2107
+ providers.length > buildProvidersFromMap(liveProviderMap).length ? "hybrid" : "openclaw";
2108
+ const warning =
2109
+ source === "hybrid" && flatError
2110
+ ? `loaded priority providers; kept curated defaults for the rest (${flatError.slice(0, 160)})`
2111
+ : source === "hybrid"
2112
+ ? "loaded priority providers; kept curated defaults for the rest"
2113
+ : "";
2028
2114
  return {
2029
- source: "openclaw",
2115
+ source,
2030
2116
  providers,
2031
2117
  generatedAt: new Date().toISOString(),
2032
2118
  catalogFetchMs: elapsedMs,
2033
2119
  catalogStrategy,
2120
+ ...(warning ? { warning } : {}),
2034
2121
  };
2035
2122
  } catch (err) {
2036
2123
  const detail = err?.message || String(err);
@@ -2334,7 +2421,7 @@ function wizardHtml(defaults) {
2334
2421
  const updateHint = () => {
2335
2422
  const elapsedSeconds = Math.max(1, Math.floor((Date.now() - llmLoadStartedAt) / 1000));
2336
2423
  if (elapsedSeconds >= 8) {
2337
- llmLoadingHintTextEl.textContent = "Still loading provider catalog (" + elapsedSeconds + "s). First run can take up to ~60s.";
2424
+ llmLoadingHintTextEl.textContent = "Still loading provider catalog (" + elapsedSeconds + "s). This should usually finish in under ~10s.";
2338
2425
  return;
2339
2426
  }
2340
2427
  llmLoadingHintTextEl.textContent = "Fetching provider list (" + elapsedSeconds + "s)...";
@@ -2403,10 +2490,13 @@ function wizardHtml(defaults) {
2403
2490
  setSelectOptions(llmProviderEl, providers, "${defaults.llmProvider}");
2404
2491
  refreshModelOptions("${defaults.llmModel}");
2405
2492
  const isFallback = llmCatalog.source === "fallback";
2493
+ const isHybrid = llmCatalog.source === "hybrid";
2406
2494
  const catalogMsg = isFallback
2407
- ? "Showing safe defaults only (could not load full OpenClaw catalog" + (llmCatalog.warning ? ": " + llmCatalog.warning : "") + "). These providers still work pick one and paste your credential."
2408
- : "LLM providers loaded. Select provider and paste credential to continue. Model selection is optional.";
2409
- setLlmCatalogReady(true, catalogMsg, isFallback);
2495
+ ? "Showing curated safe defaults only (could not load live OpenClaw catalog" + (llmCatalog.warning ? ": " + llmCatalog.warning : "") + "). Anthropic and OpenAI stay ready first, with several other providers still available."
2496
+ : isHybrid
2497
+ ? "Loaded priority providers first and filled the rest with curated defaults" + (llmCatalog.warning ? " (" + llmCatalog.warning + ")" : "") + "."
2498
+ : "LLM providers loaded. Select provider and paste credential to continue. Model selection is optional.";
2499
+ setLlmCatalogReady(true, catalogMsg, isFallback || isHybrid);
2410
2500
  } catch (err) {
2411
2501
  setLlmCatalogReady(false, "Failed to load LLM providers. Check OpenClaw and reload this page.", true);
2412
2502
  manualEl.textContent = "Failed to load LLM provider catalog: " + (err && err.message ? err.message : String(err));
@@ -2782,7 +2872,13 @@ async function cmdInstall(args) {
2782
2872
 
2783
2873
  if (req.method === "GET" && req.url === "/api/llm/options") {
2784
2874
  try {
2785
- const payload = await loadWizardLlmCatalogAsync();
2875
+ if (!wizardLlmCatalogPromise) {
2876
+ wizardLlmCatalogPromise = loadWizardLlmCatalogAsync().catch((err) => {
2877
+ wizardLlmCatalogPromise = null;
2878
+ throw err;
2879
+ });
2880
+ }
2881
+ const payload = await wizardLlmCatalogPromise;
2786
2882
  respondJson(200, payload);
2787
2883
  } catch (err) {
2788
2884
  respondJson(500, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traderclaw-cli",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "description": "Global TraderClaw CLI (install --wizard, setup, precheck). Installs solana-traderclaw as a dependency for OpenClaw plugin files.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "node": ">=22"
18
18
  },
19
19
  "dependencies": {
20
- "solana-traderclaw": "^1.0.60"
20
+ "solana-traderclaw": "^1.0.61"
21
21
  },
22
22
  "keywords": [
23
23
  "traderclaw",