@elmohq/cli 0.2.17 → 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.
- package/dist/index.js +138 -39
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import crypto from "node:crypto";
|
|
3
|
+
import crypto, { randomBytes } from "node:crypto";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
@@ -92,7 +92,18 @@ async function runMigrations(plan, ctx) {
|
|
|
92
92
|
}
|
|
93
93
|
//#endregion
|
|
94
94
|
//#region src/migrations/index.ts
|
|
95
|
-
const MIGRATIONS = [
|
|
95
|
+
const MIGRATIONS = [{
|
|
96
|
+
from: "0.2.17",
|
|
97
|
+
to: "0.2.18",
|
|
98
|
+
description: "add ELMO_ENCRYPTION_KEY for encrypted in-app provider credentials",
|
|
99
|
+
async run(ctx) {
|
|
100
|
+
const env = await ctx.readEnv();
|
|
101
|
+
if (env.ELMO_ENCRYPTION_KEY !== void 0) return;
|
|
102
|
+
env.ELMO_ENCRYPTION_KEY = randomBytes(32).toString("base64");
|
|
103
|
+
await ctx.writeEnv(env);
|
|
104
|
+
ctx.log.info("Added ELMO_ENCRYPTION_KEY — lets you store provider credentials encrypted in the app.");
|
|
105
|
+
}
|
|
106
|
+
}];
|
|
96
107
|
//#endregion
|
|
97
108
|
//#region src/telemetry.ts
|
|
98
109
|
const POSTHOG_PUBLIC_KEY = "phc_Jhx9LnI9cTDFHpQmpOzJSDTW127qD9pFU65KRnYym6z";
|
|
@@ -102,7 +113,8 @@ function envDisabled() {
|
|
|
102
113
|
}
|
|
103
114
|
async function readEnvKey(configDir, key) {
|
|
104
115
|
try {
|
|
105
|
-
|
|
116
|
+
const contents = await fs.readFile(path.join(configDir, ".env"), "utf8");
|
|
117
|
+
return parse(contents)[key];
|
|
106
118
|
} catch {
|
|
107
119
|
return;
|
|
108
120
|
}
|
|
@@ -179,8 +191,8 @@ function assertNotCancelled(value) {
|
|
|
179
191
|
process.exit(0);
|
|
180
192
|
}
|
|
181
193
|
}
|
|
182
|
-
function generateSecret(bytes = 32) {
|
|
183
|
-
return crypto.randomBytes(bytes).toString(
|
|
194
|
+
function generateSecret(bytes = 32, encoding = "base64url") {
|
|
195
|
+
return crypto.randomBytes(bytes).toString(encoding);
|
|
184
196
|
}
|
|
185
197
|
function link(text, url) {
|
|
186
198
|
return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
|
|
@@ -274,6 +286,7 @@ async function runInit(options, version) {
|
|
|
274
286
|
env.VITE_DEPLOYMENT_MODE = "local";
|
|
275
287
|
env.DEPLOYMENT_ID = preservedDeploymentId ?? crypto.randomUUID();
|
|
276
288
|
env.BETTER_AUTH_SECRET = generateSecret();
|
|
289
|
+
env.ELMO_ENCRYPTION_KEY = generateSecret(32, "base64");
|
|
277
290
|
env.APP_NAME = DEFAULT_APP_NAME;
|
|
278
291
|
env.APP_ICON = DEFAULT_APP_ICON;
|
|
279
292
|
env.VITE_APP_NAME = DEFAULT_APP_NAME;
|
|
@@ -368,16 +381,18 @@ async function runInit(options, version) {
|
|
|
368
381
|
postgres_mode: postgresMode,
|
|
369
382
|
dev_mode: Boolean(options.dev),
|
|
370
383
|
setup_mode: setupMode,
|
|
371
|
-
has_scraper: Boolean(env.BRIGHTDATA_API_TOKEN || env.OLOSTEP_API_KEY || env.OXYLABS_USERNAME),
|
|
384
|
+
has_scraper: Boolean(env.BRIGHTDATA_API_TOKEN || env.OLOSTEP_API_KEY || env.OXYLABS_USERNAME || env.CLORO_API_KEY),
|
|
372
385
|
has_direct_api: hasDirectApiConfigured(env)
|
|
373
386
|
});
|
|
374
387
|
if (email) await submitNewsletterSignup(configDir, email);
|
|
375
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")}`);
|
|
376
389
|
p.outro(pc.green("Setup complete!"));
|
|
377
390
|
}
|
|
391
|
+
const CLORO_AFFILIATE = "https://cloro.dev?fpr=elmo";
|
|
378
392
|
const BRIGHTDATA_AFFILIATE = "https://get.brightdata.com/67h1b7h0shcn";
|
|
379
|
-
const OLOSTEP_AFFILIATE = "https://olostep.com/?ref=elmo";
|
|
380
393
|
const OXYLABS_AFFILIATE = "https://oxylabs.go2cloud.org/aff_c?offer_id=7&aff_id=2263&url_id=32";
|
|
394
|
+
const OLOSTEP_AFFILIATE = "https://olostep.com/?ref=elmo";
|
|
395
|
+
const DATAFORSEO_AFFILIATE = "https://dataforseo.com/?aff=184966";
|
|
381
396
|
const PROVIDERS_DOC_URL = "https://docs.elmohq.com/docs/user-guide/providers";
|
|
382
397
|
const BRIGHTDATA_MODELS = [
|
|
383
398
|
"chatgpt",
|
|
@@ -401,14 +416,37 @@ const OXYLABS_MODELS = [
|
|
|
401
416
|
"google-ai-overview",
|
|
402
417
|
"perplexity"
|
|
403
418
|
];
|
|
404
|
-
const
|
|
405
|
-
|
|
419
|
+
const CLORO_MODELS = [
|
|
420
|
+
"chatgpt",
|
|
406
421
|
"google-ai-mode",
|
|
407
422
|
"google-ai-overview",
|
|
408
|
-
"chatgpt",
|
|
409
423
|
"perplexity",
|
|
424
|
+
"copilot",
|
|
410
425
|
"gemini"
|
|
411
426
|
];
|
|
427
|
+
const DEFAULT_SCRAPER_MODELS = ["chatgpt", "google-ai-mode"];
|
|
428
|
+
const DATAFORSEO_TARGETS = [
|
|
429
|
+
{
|
|
430
|
+
model: "google-ai-mode",
|
|
431
|
+
kind: "scraper"
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
model: "google-ai-overview",
|
|
435
|
+
kind: "scraper"
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
model: "chatgpt",
|
|
439
|
+
kind: "scraper"
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
model: "gemini",
|
|
443
|
+
kind: "scraper"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
model: "perplexity",
|
|
447
|
+
kind: "api"
|
|
448
|
+
}
|
|
449
|
+
];
|
|
412
450
|
const DEFAULT_OPENAI_MODEL = "gpt-5-mini";
|
|
413
451
|
const DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-6";
|
|
414
452
|
const DEFAULT_OPENROUTER_MODEL = "anthropic/claude-sonnet-4.6";
|
|
@@ -418,9 +456,11 @@ async function configureProvidersInteractive(env) {
|
|
|
418
456
|
"Elmo needs two kinds of providers:",
|
|
419
457
|
"",
|
|
420
458
|
pc.bold("1. A scraper") + " — to track ChatGPT and Google AI Mode (no public APIs):",
|
|
421
|
-
` • ${pc.cyan("
|
|
422
|
-
` • ${pc.cyan("
|
|
423
|
-
` • ${pc.cyan("
|
|
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`,
|
|
424
464
|
"",
|
|
425
465
|
pc.bold("2. A direct LLM API") + " — for low-latency tasks (onboarding analysis, sentiment scoring,",
|
|
426
466
|
" ad-hoc LLM calls). Required:",
|
|
@@ -450,20 +490,28 @@ async function configureProvidersRecommended(env) {
|
|
|
450
490
|
const scraper = await p.select({
|
|
451
491
|
message: "Scraper (tracks ChatGPT + Google AI Mode)",
|
|
452
492
|
options: [
|
|
493
|
+
{
|
|
494
|
+
value: "cloro",
|
|
495
|
+
label: "Cloro — most reliable, every surface (~$0.65/mo per prompt, $30/mo min)"
|
|
496
|
+
},
|
|
453
497
|
{
|
|
454
498
|
value: "brightdata",
|
|
455
|
-
label: "BrightData — ~$0.45/mo per prompt
|
|
499
|
+
label: "BrightData — pay-as-you-go, cheaper but slower (~$0.45/mo per prompt)"
|
|
456
500
|
},
|
|
457
501
|
{
|
|
458
502
|
value: "oxylabs",
|
|
459
|
-
label: "Oxylabs —
|
|
503
|
+
label: "Oxylabs — cheapest per run, no Gemini/Copilot ($49/mo min)"
|
|
460
504
|
},
|
|
461
505
|
{
|
|
462
506
|
value: "olostep",
|
|
463
|
-
label: "Olostep — ~$2.25/mo per prompt
|
|
507
|
+
label: "Olostep — premium, built for high volume (~$2.25/mo per prompt)"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
value: "dataforseo",
|
|
511
|
+
label: "DataForSEO — pay-as-you-go, scrapers + direct APIs (~$1.20/mo per prompt)"
|
|
464
512
|
}
|
|
465
513
|
],
|
|
466
|
-
initialValue: "
|
|
514
|
+
initialValue: "cloro"
|
|
467
515
|
});
|
|
468
516
|
assertNotCancelled(scraper);
|
|
469
517
|
await collectScraperKey(scraper, env);
|
|
@@ -509,6 +557,7 @@ async function configureProvidersCustom(env) {
|
|
|
509
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.");
|
|
510
558
|
}
|
|
511
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);
|
|
512
561
|
await collectBrightData(env, targets);
|
|
513
562
|
await collectOxylabs(env, targets);
|
|
514
563
|
await collectOlostep(env, targets);
|
|
@@ -519,7 +568,15 @@ function hasDirectApiConfigured(env) {
|
|
|
519
568
|
return Boolean(env.ANTHROPIC_API_KEY || env.OPENAI_API_KEY || env.MISTRAL_API_KEY || env.OPENROUTER_API_KEY);
|
|
520
569
|
}
|
|
521
570
|
async function collectScraperKey(scraper, env) {
|
|
522
|
-
if (scraper === "
|
|
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") {
|
|
523
580
|
p.log.info(`Sign up: ${link(pc.cyan(BRIGHTDATA_AFFILIATE), BRIGHTDATA_AFFILIATE)}`);
|
|
524
581
|
const key = await p.password({
|
|
525
582
|
message: "BrightData API token",
|
|
@@ -541,7 +598,7 @@ async function collectScraperKey(scraper, env) {
|
|
|
541
598
|
});
|
|
542
599
|
assertNotCancelled(password);
|
|
543
600
|
env.OXYLABS_PASSWORD = password;
|
|
544
|
-
} else {
|
|
601
|
+
} else if (scraper === "olostep") {
|
|
545
602
|
p.log.info(`Sign up: ${link(pc.cyan(OLOSTEP_AFFILIATE), OLOSTEP_AFFILIATE)}`);
|
|
546
603
|
const key = await p.password({
|
|
547
604
|
message: "Olostep API key",
|
|
@@ -549,6 +606,20 @@ async function collectScraperKey(scraper, env) {
|
|
|
549
606
|
});
|
|
550
607
|
assertNotCancelled(key);
|
|
551
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;
|
|
552
623
|
}
|
|
553
624
|
}
|
|
554
625
|
async function collectDirectApiQuick(kind, env) {
|
|
@@ -584,8 +655,8 @@ async function collectDirectApiQuick(kind, env) {
|
|
|
584
655
|
}
|
|
585
656
|
async function collectBrightData(env, targets) {
|
|
586
657
|
const enable = await p.confirm({
|
|
587
|
-
message: `Configure ${pc.bold("BrightData")}? (~$0.45/mo per prompt)`,
|
|
588
|
-
initialValue:
|
|
658
|
+
message: `Configure ${pc.bold("BrightData")}? (pay-as-you-go, cheaper but slower — ~$0.45/mo per prompt)`,
|
|
659
|
+
initialValue: false
|
|
589
660
|
});
|
|
590
661
|
assertNotCancelled(enable);
|
|
591
662
|
if (!enable) return;
|
|
@@ -605,7 +676,7 @@ async function collectBrightData(env, targets) {
|
|
|
605
676
|
}
|
|
606
677
|
async function collectOlostep(env, targets) {
|
|
607
678
|
const enable = await p.confirm({
|
|
608
|
-
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)`,
|
|
609
680
|
initialValue: false
|
|
610
681
|
});
|
|
611
682
|
assertNotCancelled(enable);
|
|
@@ -626,7 +697,7 @@ async function collectOlostep(env, targets) {
|
|
|
626
697
|
}
|
|
627
698
|
async function collectOxylabs(env, targets) {
|
|
628
699
|
const enable = await p.confirm({
|
|
629
|
-
message: `Configure ${pc.bold("Oxylabs")}? (
|
|
700
|
+
message: `Configure ${pc.bold("Oxylabs")}? (cheapest per run, no Gemini/Copilot — $49/mo min)`,
|
|
630
701
|
initialValue: false
|
|
631
702
|
});
|
|
632
703
|
assertNotCancelled(enable);
|
|
@@ -651,6 +722,27 @@ async function collectOxylabs(env, targets) {
|
|
|
651
722
|
targets
|
|
652
723
|
});
|
|
653
724
|
}
|
|
725
|
+
async function collectCloro(env, targets) {
|
|
726
|
+
const enable = await p.confirm({
|
|
727
|
+
message: `Configure ${pc.bold("Cloro")}? (most reliable, every surface — ~$0.65/mo per prompt, $30/mo min)`,
|
|
728
|
+
initialValue: true
|
|
729
|
+
});
|
|
730
|
+
assertNotCancelled(enable);
|
|
731
|
+
if (!enable) return;
|
|
732
|
+
p.log.info(`Sign up and create an API key: ${link(pc.cyan(CLORO_AFFILIATE), CLORO_AFFILIATE)}`);
|
|
733
|
+
const key = await p.password({
|
|
734
|
+
message: "Cloro API key",
|
|
735
|
+
validate: (v) => !v ? "Required" : void 0
|
|
736
|
+
});
|
|
737
|
+
assertNotCancelled(key);
|
|
738
|
+
env.CLORO_API_KEY = key;
|
|
739
|
+
await pickScraperTargets({
|
|
740
|
+
providerLabel: "Cloro",
|
|
741
|
+
providerId: "cloro",
|
|
742
|
+
allModels: CLORO_MODELS,
|
|
743
|
+
targets
|
|
744
|
+
});
|
|
745
|
+
}
|
|
654
746
|
async function pickScraperTargets(args) {
|
|
655
747
|
const selected = await p.multiselect({
|
|
656
748
|
message: `LLM Providers to track via ${args.providerLabel}`,
|
|
@@ -798,11 +890,12 @@ async function collectOpenRouter(env, targets) {
|
|
|
798
890
|
}
|
|
799
891
|
async function collectDataForSEO(env, targets) {
|
|
800
892
|
const enable = await p.confirm({
|
|
801
|
-
message: `Configure ${pc.bold("DataForSEO")}? (
|
|
893
|
+
message: `Configure ${pc.bold("DataForSEO")}? (pay-as-you-go, scrapers + direct APIs — ~$1.20/mo per prompt)`,
|
|
802
894
|
initialValue: false
|
|
803
895
|
});
|
|
804
896
|
assertNotCancelled(enable);
|
|
805
897
|
if (!enable) return;
|
|
898
|
+
p.log.info(`Sign up: ${link(pc.cyan(DATAFORSEO_AFFILIATE), DATAFORSEO_AFFILIATE)}`);
|
|
806
899
|
const login = await p.text({
|
|
807
900
|
message: "DataForSEO login",
|
|
808
901
|
validate: (v) => !v ? "Required" : void 0
|
|
@@ -817,19 +910,23 @@ async function collectDataForSEO(env, targets) {
|
|
|
817
910
|
env.DATAFORSEO_PASSWORD = pwd;
|
|
818
911
|
const selected = await p.multiselect({
|
|
819
912
|
message: "LLM Providers to track via DataForSEO",
|
|
820
|
-
options:
|
|
821
|
-
value:
|
|
822
|
-
|
|
913
|
+
options: DATAFORSEO_TARGETS.map((t) => ({
|
|
914
|
+
value: formatScrapeTarget({
|
|
915
|
+
model: t.model,
|
|
916
|
+
provider: "dataforseo",
|
|
917
|
+
webSearch: true
|
|
918
|
+
}),
|
|
919
|
+
label: `${t.model} (${t.kind})`
|
|
823
920
|
})),
|
|
824
921
|
required: false,
|
|
825
|
-
initialValues: [
|
|
922
|
+
initialValues: [formatScrapeTarget({
|
|
923
|
+
model: "google-ai-mode",
|
|
924
|
+
provider: "dataforseo",
|
|
925
|
+
webSearch: true
|
|
926
|
+
})]
|
|
826
927
|
});
|
|
827
928
|
assertNotCancelled(selected);
|
|
828
|
-
|
|
829
|
-
model,
|
|
830
|
-
provider: "dataforseo",
|
|
831
|
-
webSearch: true
|
|
832
|
-
}));
|
|
929
|
+
targets.push(...selected);
|
|
833
930
|
}
|
|
834
931
|
async function finalizeScrapeTargets(env, targets, options = {}) {
|
|
835
932
|
const deduped = dedupeTargets(targets);
|
|
@@ -1153,13 +1250,13 @@ function buildComposeYaml(options) {
|
|
|
1153
1250
|
function buildPostgresService() {
|
|
1154
1251
|
return [
|
|
1155
1252
|
"postgres:",
|
|
1156
|
-
" image: postgres:
|
|
1253
|
+
" image: postgres:18-alpine",
|
|
1157
1254
|
" environment:",
|
|
1158
1255
|
" POSTGRES_USER: postgres",
|
|
1159
1256
|
" POSTGRES_PASSWORD: postgres",
|
|
1160
1257
|
" POSTGRES_DB: elmo",
|
|
1161
1258
|
" volumes:",
|
|
1162
|
-
" - postgres_data:/var/lib/postgresql
|
|
1259
|
+
" - postgres_data:/var/lib/postgresql",
|
|
1163
1260
|
" ports:",
|
|
1164
1261
|
" - \"5432:5432\"",
|
|
1165
1262
|
" healthcheck:",
|
|
@@ -1250,12 +1347,13 @@ function sleep(ms) {
|
|
|
1250
1347
|
}
|
|
1251
1348
|
function runDockerCompose(configDir, args) {
|
|
1252
1349
|
return new Promise((resolve, reject) => {
|
|
1253
|
-
|
|
1350
|
+
const commandArgs = [
|
|
1254
1351
|
"compose",
|
|
1255
1352
|
"-f",
|
|
1256
1353
|
path.join(configDir, "elmo.yaml"),
|
|
1257
1354
|
...args
|
|
1258
|
-
]
|
|
1355
|
+
];
|
|
1356
|
+
spawn("docker", commandArgs, { stdio: "inherit" }).on("close", (code) => {
|
|
1259
1357
|
if (code === 0) resolve();
|
|
1260
1358
|
else reject(/* @__PURE__ */ new Error(`docker compose exited with code ${code}`));
|
|
1261
1359
|
});
|
|
@@ -1263,12 +1361,13 @@ function runDockerCompose(configDir, args) {
|
|
|
1263
1361
|
}
|
|
1264
1362
|
function runDockerComposeCapture(configDir, args) {
|
|
1265
1363
|
return new Promise((resolve, reject) => {
|
|
1266
|
-
const
|
|
1364
|
+
const commandArgs = [
|
|
1267
1365
|
"compose",
|
|
1268
1366
|
"-f",
|
|
1269
1367
|
path.join(configDir, "elmo.yaml"),
|
|
1270
1368
|
...args
|
|
1271
|
-
]
|
|
1369
|
+
];
|
|
1370
|
+
const child = spawn("docker", commandArgs);
|
|
1272
1371
|
let stdout = "";
|
|
1273
1372
|
let stderr = "";
|
|
1274
1373
|
child.stdout.on("data", (data) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elmohq/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"marketing",
|
|
@@ -32,16 +32,16 @@
|
|
|
32
32
|
"commander": "^15.0.0",
|
|
33
33
|
"dotenv": "17.4.2",
|
|
34
34
|
"picocolors": "^1.1.1",
|
|
35
|
-
"posthog-node": "^5.
|
|
35
|
+
"posthog-node": "^5.47.3",
|
|
36
36
|
"semver": "^7.8.5"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/node": "^26.1.
|
|
39
|
+
"@types/node": "^26.1.2",
|
|
40
40
|
"@types/semver": "^7.7.1",
|
|
41
|
-
"rolldown": "^1.1
|
|
41
|
+
"rolldown": "^1.2.1",
|
|
42
42
|
"typescript": "^7.0.2",
|
|
43
43
|
"vitest": "^4.1.10",
|
|
44
|
-
"@workspace/config": "0.2.
|
|
44
|
+
"@workspace/config": "0.2.19"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "node scripts/build.mjs",
|