traderclaw-cli 1.0.52 → 1.0.54

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.
package/bin/cli.ts CHANGED
@@ -204,6 +204,8 @@ async function cmdSetup(args: string[]) {
204
204
  let apiKey = "";
205
205
  let orchestratorUrl = "";
206
206
 
207
+ let forwardTelegramRecipientArg = "";
208
+
207
209
  for (let i = 0; i < args.length; i++) {
208
210
  if ((args[i] === "--api-key" || args[i] === "-k") && args[i + 1]) {
209
211
  apiKey = args[++i];
@@ -211,6 +213,14 @@ async function cmdSetup(args: string[]) {
211
213
  if ((args[i] === "--url" || args[i] === "-u") && args[i + 1]) {
212
214
  orchestratorUrl = args[++i];
213
215
  }
216
+ if (
217
+ (args[i] === "--telegram-recipient" ||
218
+ args[i] === "--forward-telegram-chat-id" ||
219
+ args[i] === "--telegram-chat-id") &&
220
+ args[i + 1]
221
+ ) {
222
+ forwardTelegramRecipientArg = args[++i];
223
+ }
214
224
  }
215
225
 
216
226
  if (!apiKey) {
@@ -344,6 +354,17 @@ async function cmdSetup(args: string[]) {
344
354
  process.exit(1);
345
355
  }
346
356
 
357
+ print("\nTelegram delivery (optional)...\n");
358
+ printInfo(" Enter your Telegram @username or numeric chat id so agent replies can be routed to you.");
359
+ printInfo(" Usernames are resolved with Telegram getChat (set TELEGRAM_BOT_TOKEN on the gateway, or");
360
+ printInfo(" export it here for immediate resolution). Private chats: message your bot once first.\n");
361
+
362
+ let forwardTelegramRecipient = forwardTelegramRecipientArg.trim();
363
+ if (!forwardTelegramRecipient) {
364
+ forwardTelegramRecipient = await prompt("Telegram @username or chat id (optional, Enter to skip)", "");
365
+ }
366
+ forwardTelegramRecipient = forwardTelegramRecipient.trim();
367
+
347
368
  print("\nWriting configuration...\n");
348
369
 
349
370
  const existingConfig = readConfig();
@@ -353,6 +374,32 @@ async function cmdSetup(args: string[]) {
353
374
  apiKey,
354
375
  apiTimeout: 120000,
355
376
  };
377
+
378
+ if (forwardTelegramRecipient) {
379
+ try {
380
+ const { resolveTelegramRecipientToChatId, looksLikeTelegramChatId } = await import("../src/telegram-resolve.ts");
381
+ const botToken = String(
382
+ process.env.TELEGRAM_BOT_TOKEN || process.env.OPENCLAW_TELEGRAM_BOT_TOKEN || "",
383
+ ).trim();
384
+ if (looksLikeTelegramChatId(forwardTelegramRecipient)) {
385
+ pluginConfig.forwardTelegramRecipient = forwardTelegramRecipient;
386
+ printSuccess(` Saved Telegram chat id`);
387
+ } else if (botToken) {
388
+ const id = await resolveTelegramRecipientToChatId({ botToken, raw: forwardTelegramRecipient });
389
+ pluginConfig.forwardTelegramRecipient = id;
390
+ printSuccess(` Resolved @${forwardTelegramRecipient.replace(/^@/, "")} → chat id ${id}`);
391
+ } else {
392
+ pluginConfig.forwardTelegramRecipient = forwardTelegramRecipient;
393
+ printInfo(
394
+ " Saved as-is; start the gateway with TELEGRAM_BOT_TOKEN set to resolve @username on first run.",
395
+ );
396
+ }
397
+ } catch (err) {
398
+ printWarn(` Telegram resolve failed: ${err instanceof Error ? err.message : String(err)}`);
399
+ printInfo(" Saving your username anyway — fix token or use numeric chat id.");
400
+ pluginConfig.forwardTelegramRecipient = forwardTelegramRecipient;
401
+ }
402
+ }
356
403
  setPluginConfig(existingConfig, pluginConfig);
357
404
  writeConfig(existingConfig);
358
405
 
@@ -365,6 +412,7 @@ async function cmdSetup(args: string[]) {
365
412
  Orchestrator: ${orchestratorUrl}
366
413
  Wallet: ${walletLabel} (ID: ${walletId})
367
414
  API Key: ${maskKey(apiKey)}
415
+ Telegram fwd: ${(pluginConfig.forwardTelegramRecipient as string) || "(not set)"}
368
416
  Config: ${CONFIG_FILE}
369
417
  `);
370
418
  print("Next steps:");
@@ -575,8 +623,9 @@ Commands:
575
623
  config View and manage configuration
576
624
 
577
625
  Setup options:
578
- --api-key, -k API key (skip interactive prompt)
579
- --url, -u Orchestrator URL (skip interactive prompt)
626
+ --api-key, -k API key (skip interactive prompt)
627
+ --url, -u Orchestrator URL (skip interactive prompt)
628
+ --telegram-recipient Telegram @username or chat id (alias: --forward-telegram-chat-id)
580
629
 
581
630
  Config subcommands:
582
631
  config show Show current configuration
@@ -586,6 +635,7 @@ Config subcommands:
586
635
  Examples:
587
636
  openclaw-trader setup
588
637
  openclaw-trader setup --api-key sk_live_abc123 --url https://api.traderclaw.ai
638
+ openclaw-trader setup --telegram-recipient @MyChannelOrUser
589
639
  openclaw-trader status
590
640
  openclaw-trader config show
591
641
  openclaw-trader config set apiTimeout 60000
@@ -1856,6 +1856,51 @@ async function loadWizardLlmCatalogAsync() {
1856
1856
  }
1857
1857
  }
1858
1858
 
1859
+ function buildProvidersFromMap(providerMap) {
1860
+ return providerIds
1861
+ .map((id) => {
1862
+ const rawModels = providerMap.get(id) || [];
1863
+ const sortedIds = sortModelsByPreference(
1864
+ id,
1865
+ rawModels.map((m) => m.id),
1866
+ );
1867
+ const byId = new Map(rawModels.map((m) => [m.id, m]));
1868
+ const limitedIds = sortedIds.slice(0, MAX_MODELS_PER_PROVIDER_SORT);
1869
+ const models = limitedIds.map((mid) => byId.get(mid)).filter(Boolean);
1870
+ return { id, models };
1871
+ })
1872
+ .filter((entry) => supportedProviders.has(entry.id))
1873
+ .filter((entry) => entry.models.length > 0);
1874
+ }
1875
+
1876
+ /** When `openclaw models list --all --json` returns models; used if per-provider calls yield nothing. */
1877
+ function mergeFlatCatalogIntoMap(providerMap) {
1878
+ const raw = execSync("openclaw models list --all --json", {
1879
+ encoding: "utf-8",
1880
+ stdio: ["ignore", "pipe", "pipe"],
1881
+ maxBuffer: 50 * 1024 * 1024,
1882
+ timeout: 45_000,
1883
+ env: NO_COLOR_ENV,
1884
+ });
1885
+ const parsed = extractJson(raw);
1886
+ if (!parsed) return;
1887
+ const models = Array.isArray(parsed?.models) ? parsed.models : [];
1888
+ for (const entry of models) {
1889
+ if (!entry || typeof entry.key !== "string") continue;
1890
+ const modelId = String(entry.key);
1891
+ const slash = modelId.indexOf("/");
1892
+ if (slash <= 0 || slash === modelId.length - 1) continue;
1893
+ const provider = modelId.slice(0, slash);
1894
+ if (!supportedProviders.has(provider)) continue;
1895
+ const existing = providerMap.get(provider) || [];
1896
+ existing.push({
1897
+ id: modelId,
1898
+ name: typeof entry.name === "string" && entry.name.trim() ? entry.name : modelId,
1899
+ });
1900
+ providerMap.set(provider, existing);
1901
+ }
1902
+ }
1903
+
1859
1904
  try {
1860
1905
  const t0 = Date.now();
1861
1906
  const batches = await Promise.all(providerIds.map((p) => fetchModelsForProvider(p)));
@@ -1883,23 +1928,28 @@ async function loadWizardLlmCatalogAsync() {
1883
1928
  }
1884
1929
  }
1885
1930
 
1886
- const providers = providerIds
1887
- .map((id) => {
1888
- const rawModels = providerMap.get(id) || [];
1889
- const sortedIds = sortModelsByPreference(
1890
- id,
1891
- rawModels.map((m) => m.id),
1931
+ let providers = buildProvidersFromMap(providerMap);
1932
+ let catalogStrategy = "parallel";
1933
+
1934
+ if (providers.length === 0) {
1935
+ catalogStrategy = "legacy_fallback";
1936
+ try {
1937
+ mergeFlatCatalogIntoMap(providerMap);
1938
+ providers = buildProvidersFromMap(providerMap);
1939
+ } catch (legacyErr) {
1940
+ console.error(
1941
+ `[traderclaw] loadWizardLlmCatalog legacy fallback failed: ${legacyErr instanceof Error ? legacyErr.message : String(legacyErr)}`,
1892
1942
  );
1893
- const byId = new Map(rawModels.map((m) => [m.id, m]));
1894
- const limitedIds = sortedIds.slice(0, MAX_MODELS_PER_PROVIDER_SORT);
1895
- const models = limitedIds.map((mid) => byId.get(mid)).filter(Boolean);
1896
- return { id, models };
1897
- })
1898
- .filter((entry) => supportedProviders.has(entry.id))
1899
- .filter((entry) => entry.models.length > 0);
1943
+ }
1944
+ }
1900
1945
 
1901
1946
  if (providers.length === 0) {
1902
- return { ...fallback, warning: "openclaw_model_catalog_empty" };
1947
+ const failedParallel = batches.filter((b) => b.error).length;
1948
+ const hint =
1949
+ failedParallel > 0
1950
+ ? ` (${failedParallel}/${batches.length} per-provider openclaw calls failed — check OpenClaw version supports: openclaw models list --all --provider <id> --json)`
1951
+ : "";
1952
+ return { ...fallback, warning: `openclaw_model_catalog_empty${hint}` };
1903
1953
  }
1904
1954
 
1905
1955
  const elapsedMs = Date.now() - t0;
@@ -1908,6 +1958,7 @@ async function loadWizardLlmCatalogAsync() {
1908
1958
  providers,
1909
1959
  generatedAt: new Date().toISOString(),
1910
1960
  catalogFetchMs: elapsedMs,
1961
+ catalogStrategy,
1911
1962
  };
1912
1963
  } catch (err) {
1913
1964
  const detail = err?.message || String(err);
@@ -2211,7 +2262,7 @@ function wizardHtml(defaults) {
2211
2262
  const updateHint = () => {
2212
2263
  const elapsedSeconds = Math.max(1, Math.floor((Date.now() - llmLoadStartedAt) / 1000));
2213
2264
  if (elapsedSeconds >= 8) {
2214
- llmLoadingHintTextEl.textContent = "Still loading provider catalog (" + elapsedSeconds + "s). First run can take up to ~20s.";
2265
+ llmLoadingHintTextEl.textContent = "Still loading provider catalog (" + elapsedSeconds + "s). First run can take up to ~60s.";
2215
2266
  return;
2216
2267
  }
2217
2268
  llmLoadingHintTextEl.textContent = "Fetching provider list (" + elapsedSeconds + "s)...";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traderclaw-cli",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
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": {