@elmohq/cli 0.2.18 → 0.2.19

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.
Files changed (2) hide show
  1. package/dist/index.js +54 -33
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -388,10 +388,11 @@ async function runInit(options, version) {
388
388
  p.log.message(`If you find Elmo useful, star us on GitHub!\n ${link(pc.cyan("https://github.com/elmohq/elmo"), "https://github.com/elmohq/elmo")}`);
389
389
  p.outro(pc.green("Setup complete!"));
390
390
  }
391
+ const CLORO_AFFILIATE = "https://cloro.dev?fpr=elmo";
391
392
  const BRIGHTDATA_AFFILIATE = "https://get.brightdata.com/67h1b7h0shcn";
392
- const OLOSTEP_AFFILIATE = "https://olostep.com/?ref=elmo";
393
393
  const OXYLABS_AFFILIATE = "https://oxylabs.go2cloud.org/aff_c?offer_id=7&aff_id=2263&url_id=32";
394
- const CLORO_AFFILIATE = "https://cloro.dev?fpr=elmo";
394
+ const OLOSTEP_AFFILIATE = "https://olostep.com/?ref=elmo";
395
+ const DATAFORSEO_AFFILIATE = "https://dataforseo.com/?aff=184966";
395
396
  const PROVIDERS_DOC_URL = "https://docs.elmohq.com/docs/user-guide/providers";
396
397
  const BRIGHTDATA_MODELS = [
397
398
  "chatgpt",
@@ -455,10 +456,11 @@ async function configureProvidersInteractive(env) {
455
456
  "Elmo needs two kinds of providers:",
456
457
  "",
457
458
  pc.bold("1. A scraper") + " — to track ChatGPT and Google AI Mode (no public APIs):",
458
- ` • ${pc.cyan("BrightData")} — cheap solid option, ~$0.45/mo per prompt`,
459
- ` • ${pc.cyan("Oxylabs")} — async job API, pay-as-you-go`,
460
- ` • ${pc.cyan("Cloro")} — every surface, credit plans from $30/mo, ~$0.65/mo per prompt`,
461
- ` • ${pc.cyan("Olostep")} — premium option, powers Peec/AirOps, ~$2.25/mo per prompt`,
459
+ ` • ${pc.cyan("Cloro")} — most reliable, every surface, ~$0.65/mo per prompt ($30/mo min)`,
460
+ ` • ${pc.cyan("BrightData")} — pay-as-you-go and cheaper, but slower, ~$0.45/mo per prompt`,
461
+ ` • ${pc.cyan("Oxylabs")} — cheapest per run, no Gemini/Copilot, $49/mo min`,
462
+ ` • ${pc.cyan("Olostep")} — premium, built for high volume, ~$2.25/mo per prompt`,
463
+ ` • ${pc.cyan("DataForSEO")} — pay-as-you-go, scrapers + direct APIs, ~$1.20/mo per prompt`,
462
464
  "",
463
465
  pc.bold("2. A direct LLM API") + " — for low-latency tasks (onboarding analysis, sentiment scoring,",
464
466
  " ad-hoc LLM calls). Required:",
@@ -488,24 +490,28 @@ async function configureProvidersRecommended(env) {
488
490
  const scraper = await p.select({
489
491
  message: "Scraper (tracks ChatGPT + Google AI Mode)",
490
492
  options: [
493
+ {
494
+ value: "cloro",
495
+ label: "Cloro — most reliable, every surface (~$0.65/mo per prompt, $30/mo min)"
496
+ },
491
497
  {
492
498
  value: "brightdata",
493
- label: "BrightData — ~$0.45/mo per prompt (cheaper)"
499
+ label: "BrightData — pay-as-you-go, cheaper but slower (~$0.45/mo per prompt)"
494
500
  },
495
501
  {
496
502
  value: "oxylabs",
497
- label: "Oxylabs — async job API, pay-as-you-go"
503
+ label: "Oxylabs — cheapest per run, no Gemini/Copilot ($49/mo min)"
498
504
  },
499
505
  {
500
- value: "cloro",
501
- label: "Cloro — ~$0.65/mo per prompt (credit plans from $30/mo)"
506
+ value: "olostep",
507
+ label: "Olostep — premium, built for high volume (~$2.25/mo per prompt)"
502
508
  },
503
509
  {
504
- value: "olostep",
505
- label: "Olostep — ~$2.25/mo per prompt (premium)"
510
+ value: "dataforseo",
511
+ label: "DataForSEO — pay-as-you-go, scrapers + direct APIs (~$1.20/mo per prompt)"
506
512
  }
507
513
  ],
508
- initialValue: "brightdata"
514
+ initialValue: "cloro"
509
515
  });
510
516
  assertNotCancelled(scraper);
511
517
  await collectScraperKey(scraper, env);
@@ -551,9 +557,9 @@ async function configureProvidersCustom(env) {
551
557
  if (!hasDirectApiConfigured(env)) p.log.warn("Onboarding analysis and other low-latency LLM tasks require a direct API. Configure at least one before continuing.");
552
558
  }
553
559
  p.log.step(pc.bold("Step 2 of 2 — Scrapers (optional, but needed to track ChatGPT / Google AI Mode)"));
560
+ await collectCloro(env, targets);
554
561
  await collectBrightData(env, targets);
555
562
  await collectOxylabs(env, targets);
556
- await collectCloro(env, targets);
557
563
  await collectOlostep(env, targets);
558
564
  await collectDataForSEO(env, targets);
559
565
  await finalizeScrapeTargets(env, targets);
@@ -562,7 +568,15 @@ function hasDirectApiConfigured(env) {
562
568
  return Boolean(env.ANTHROPIC_API_KEY || env.OPENAI_API_KEY || env.MISTRAL_API_KEY || env.OPENROUTER_API_KEY);
563
569
  }
564
570
  async function collectScraperKey(scraper, env) {
565
- if (scraper === "brightdata") {
571
+ if (scraper === "cloro") {
572
+ p.log.info(`Sign up: ${link(pc.cyan(CLORO_AFFILIATE), CLORO_AFFILIATE)}`);
573
+ const key = await p.password({
574
+ message: "Cloro API key",
575
+ validate: (v) => !v ? "Required" : void 0
576
+ });
577
+ assertNotCancelled(key);
578
+ env.CLORO_API_KEY = key;
579
+ } else if (scraper === "brightdata") {
566
580
  p.log.info(`Sign up: ${link(pc.cyan(BRIGHTDATA_AFFILIATE), BRIGHTDATA_AFFILIATE)}`);
567
581
  const key = await p.password({
568
582
  message: "BrightData API token",
@@ -584,15 +598,7 @@ async function collectScraperKey(scraper, env) {
584
598
  });
585
599
  assertNotCancelled(password);
586
600
  env.OXYLABS_PASSWORD = password;
587
- } else if (scraper === "cloro") {
588
- p.log.info(`Sign up: ${link(pc.cyan(CLORO_AFFILIATE), CLORO_AFFILIATE)}`);
589
- const key = await p.password({
590
- message: "Cloro API key",
591
- validate: (v) => !v ? "Required" : void 0
592
- });
593
- assertNotCancelled(key);
594
- env.CLORO_API_KEY = key;
595
- } else {
601
+ } else if (scraper === "olostep") {
596
602
  p.log.info(`Sign up: ${link(pc.cyan(OLOSTEP_AFFILIATE), OLOSTEP_AFFILIATE)}`);
597
603
  const key = await p.password({
598
604
  message: "Olostep API key",
@@ -600,6 +606,20 @@ async function collectScraperKey(scraper, env) {
600
606
  });
601
607
  assertNotCancelled(key);
602
608
  env.OLOSTEP_API_KEY = key;
609
+ } else {
610
+ p.log.info(`Sign up: ${link(pc.cyan(DATAFORSEO_AFFILIATE), DATAFORSEO_AFFILIATE)}`);
611
+ const login = await p.text({
612
+ message: "DataForSEO login",
613
+ validate: (v) => !v ? "Required" : void 0
614
+ });
615
+ assertNotCancelled(login);
616
+ env.DATAFORSEO_LOGIN = login;
617
+ const password = await p.password({
618
+ message: "DataForSEO password",
619
+ validate: (v) => !v ? "Required" : void 0
620
+ });
621
+ assertNotCancelled(password);
622
+ env.DATAFORSEO_PASSWORD = password;
603
623
  }
604
624
  }
605
625
  async function collectDirectApiQuick(kind, env) {
@@ -635,8 +655,8 @@ async function collectDirectApiQuick(kind, env) {
635
655
  }
636
656
  async function collectBrightData(env, targets) {
637
657
  const enable = await p.confirm({
638
- message: `Configure ${pc.bold("BrightData")}? (~$0.45/mo per prompt)`,
639
- initialValue: true
658
+ message: `Configure ${pc.bold("BrightData")}? (pay-as-you-go, cheaper but slower — ~$0.45/mo per prompt)`,
659
+ initialValue: false
640
660
  });
641
661
  assertNotCancelled(enable);
642
662
  if (!enable) return;
@@ -656,7 +676,7 @@ async function collectBrightData(env, targets) {
656
676
  }
657
677
  async function collectOlostep(env, targets) {
658
678
  const enable = await p.confirm({
659
- message: `Configure ${pc.bold("Olostep")}? (~$2.25/mo per prompt)`,
679
+ message: `Configure ${pc.bold("Olostep")}? (premium, built for high volume — ~$2.25/mo per prompt)`,
660
680
  initialValue: false
661
681
  });
662
682
  assertNotCancelled(enable);
@@ -677,7 +697,7 @@ async function collectOlostep(env, targets) {
677
697
  }
678
698
  async function collectOxylabs(env, targets) {
679
699
  const enable = await p.confirm({
680
- message: `Configure ${pc.bold("Oxylabs")}? (async job API, pay-as-you-go)`,
700
+ message: `Configure ${pc.bold("Oxylabs")}? (cheapest per run, no Gemini/Copilot — $49/mo min)`,
681
701
  initialValue: false
682
702
  });
683
703
  assertNotCancelled(enable);
@@ -704,8 +724,8 @@ async function collectOxylabs(env, targets) {
704
724
  }
705
725
  async function collectCloro(env, targets) {
706
726
  const enable = await p.confirm({
707
- message: `Configure ${pc.bold("Cloro")}? (async task API, credit plans from $30/mo)`,
708
- initialValue: false
727
+ message: `Configure ${pc.bold("Cloro")}? (most reliable, every surface — ~$0.65/mo per prompt, $30/mo min)`,
728
+ initialValue: true
709
729
  });
710
730
  assertNotCancelled(enable);
711
731
  if (!enable) return;
@@ -870,11 +890,12 @@ async function collectOpenRouter(env, targets) {
870
890
  }
871
891
  async function collectDataForSEO(env, targets) {
872
892
  const enable = await p.confirm({
873
- message: `Configure ${pc.bold("DataForSEO")}? (Google AI Mode + ChatGPT/Gemini scrapers)`,
893
+ message: `Configure ${pc.bold("DataForSEO")}? (pay-as-you-go, scrapers + direct APIs — ~$1.20/mo per prompt)`,
874
894
  initialValue: false
875
895
  });
876
896
  assertNotCancelled(enable);
877
897
  if (!enable) return;
898
+ p.log.info(`Sign up: ${link(pc.cyan(DATAFORSEO_AFFILIATE), DATAFORSEO_AFFILIATE)}`);
878
899
  const login = await p.text({
879
900
  message: "DataForSEO login",
880
901
  validate: (v) => !v ? "Required" : void 0
@@ -1229,13 +1250,13 @@ function buildComposeYaml(options) {
1229
1250
  function buildPostgresService() {
1230
1251
  return [
1231
1252
  "postgres:",
1232
- " image: postgres:16-alpine",
1253
+ " image: postgres:18-alpine",
1233
1254
  " environment:",
1234
1255
  " POSTGRES_USER: postgres",
1235
1256
  " POSTGRES_PASSWORD: postgres",
1236
1257
  " POSTGRES_DB: elmo",
1237
1258
  " volumes:",
1238
- " - postgres_data:/var/lib/postgresql/data",
1259
+ " - postgres_data:/var/lib/postgresql",
1239
1260
  " ports:",
1240
1261
  " - \"5432:5432\"",
1241
1262
  " healthcheck:",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elmohq/cli",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "license": "MIT",
5
5
  "keywords": [
6
6
  "marketing",
@@ -41,7 +41,7 @@
41
41
  "rolldown": "^1.2.1",
42
42
  "typescript": "^7.0.2",
43
43
  "vitest": "^4.1.10",
44
- "@workspace/config": "0.2.18"
44
+ "@workspace/config": "0.2.19"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "node scripts/build.mjs",