@vocoder/cli 0.12.3 → 0.13.0
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/bin.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readAuthData,
|
|
14
14
|
verifyStoredAuth,
|
|
15
15
|
writeAuthData
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZHRKJ2KZ.mjs";
|
|
17
17
|
|
|
18
18
|
// src/bin.ts
|
|
19
19
|
import { Command } from "commander";
|
|
@@ -346,25 +346,35 @@ async function selectGitHubInstallation(installations, canInstallNew) {
|
|
|
346
346
|
|
|
347
347
|
// src/utils/project-create.ts
|
|
348
348
|
import * as p3 from "@clack/prompts";
|
|
349
|
-
import
|
|
349
|
+
import chalk3 from "chalk";
|
|
350
350
|
|
|
351
351
|
// src/utils/branch-select.ts
|
|
352
352
|
import { execSync } from "child_process";
|
|
353
353
|
import { isCancel as isCancel2, Prompt } from "@clack/core";
|
|
354
|
+
|
|
355
|
+
// src/utils/theme.ts
|
|
354
356
|
import chalk2 from "chalk";
|
|
357
|
+
var ORANGE = "#FC5206";
|
|
358
|
+
var PINK = "#D51977";
|
|
359
|
+
var BLUE = "#2450A9";
|
|
360
|
+
var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
|
|
361
|
+
var hex = (color) => (s) => noColor ? s : chalk2.hex(color)(s);
|
|
362
|
+
var dim = (s) => noColor ? s : chalk2.dim(s);
|
|
363
|
+
var bld = (s) => noColor ? s : chalk2.bold(s);
|
|
364
|
+
var grn = (s) => noColor ? s : chalk2.green(s);
|
|
365
|
+
var ylw = (s) => noColor ? s : chalk2.yellow(s);
|
|
366
|
+
var red = (s) => noColor ? s : chalk2.red(s);
|
|
367
|
+
var highlight = hex(PINK);
|
|
368
|
+
var info = hex(BLUE);
|
|
369
|
+
var active = hex(ORANGE);
|
|
370
|
+
|
|
371
|
+
// src/utils/branch-select.ts
|
|
355
372
|
var S_BAR = "\u2502";
|
|
356
373
|
var S_BAR_END = "\u2514";
|
|
357
374
|
var S_ACTIVE = "\u25C6";
|
|
358
375
|
var S_SUBMIT = "\u25C6";
|
|
359
376
|
var S_CANCEL = "\u25A0";
|
|
360
377
|
var S_ERROR = "\u25B2";
|
|
361
|
-
var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
|
|
362
|
-
var dim = (s) => noColor ? s : chalk2.gray(s);
|
|
363
|
-
var cyan = (s) => noColor ? s : chalk2.cyan(s);
|
|
364
|
-
var grn = (s) => noColor ? s : chalk2.green(s);
|
|
365
|
-
var ylw = (s) => noColor ? s : chalk2.yellow(s);
|
|
366
|
-
var red = (s) => noColor ? s : chalk2.red(s);
|
|
367
|
-
var bld = (s) => noColor ? s : chalk2.bold(s);
|
|
368
378
|
function symbol(state) {
|
|
369
379
|
switch (state) {
|
|
370
380
|
case "submit":
|
|
@@ -374,7 +384,7 @@ function symbol(state) {
|
|
|
374
384
|
case "error":
|
|
375
385
|
return ylw(S_ERROR);
|
|
376
386
|
default:
|
|
377
|
-
return
|
|
387
|
+
return active(S_ACTIVE);
|
|
378
388
|
}
|
|
379
389
|
}
|
|
380
390
|
function detectGitBranches(cwd) {
|
|
@@ -447,19 +457,19 @@ function buildList(filtered, cursor, scrollOffset, selected, filter, customPatte
|
|
|
447
457
|
const item = filtered[i];
|
|
448
458
|
const isCursor = i === cursor && !addCursor;
|
|
449
459
|
const isChecked = selected.has(item.value);
|
|
450
|
-
const icon = isChecked ? isCursor ?
|
|
460
|
+
const icon = isChecked ? isCursor ? info("\u25FC") : info("\u25FC") : isCursor ? active("\u25FB") : dim("\u25FB");
|
|
451
461
|
let label = item.isCustom ? `${item.label} ${dim("(custom)")}` : item.label;
|
|
452
462
|
if (isCursor) label = bld(label);
|
|
453
|
-
lines.push(`${
|
|
463
|
+
lines.push(`${info(S_BAR)} ${icon} ${label}`);
|
|
454
464
|
}
|
|
455
465
|
const trimmed = filter.trim();
|
|
456
466
|
const allItems = [...filtered];
|
|
457
467
|
const isNewPattern = trimmed.length > 0 && !allItems.some((i) => i.value === trimmed) && !customPatterns.includes(trimmed);
|
|
458
468
|
if (isNewPattern) {
|
|
459
469
|
const err = validateBranchPattern(trimmed) ?? (excludedPatterns.has(trimmed) ? "Already used for automatic translation" : null);
|
|
460
|
-
const icon = addCursor ?
|
|
470
|
+
const icon = addCursor ? active("\u25FB") : dim("\u25FB");
|
|
461
471
|
const label = err ? `${ylw("+")} ${dim(`"${trimmed}" \u2014 ${err}`)}` : `${grn("+")} Add "${trimmed}" as branch pattern`;
|
|
462
|
-
lines.push(`${
|
|
472
|
+
lines.push(`${info(S_BAR)} ${icon} ${label}`);
|
|
463
473
|
} else if (filtered.length === 0 && trimmed.length === 0) {
|
|
464
474
|
lines.push(dim(`${S_BAR} No branches detected`));
|
|
465
475
|
}
|
|
@@ -542,7 +552,7 @@ ${symbol(this.state)} ${message}
|
|
|
542
552
|
default:
|
|
543
553
|
return [
|
|
544
554
|
hdr.trimEnd(),
|
|
545
|
-
`${
|
|
555
|
+
`${info(S_BAR)} ${dim("/")} ${hint}`,
|
|
546
556
|
buildList(
|
|
547
557
|
filtered,
|
|
548
558
|
cursor,
|
|
@@ -554,7 +564,7 @@ ${symbol(this.state)} ${message}
|
|
|
554
564
|
optional,
|
|
555
565
|
excludedSet
|
|
556
566
|
),
|
|
557
|
-
`${
|
|
567
|
+
`${info(S_BAR_END)}`,
|
|
558
568
|
""
|
|
559
569
|
].join("\n");
|
|
560
570
|
}
|
|
@@ -627,30 +637,22 @@ ${symbol(this.state)} ${message}
|
|
|
627
637
|
// src/utils/locale-search.ts
|
|
628
638
|
import { isCancel as isCancel3, Prompt as Prompt2 } from "@clack/core";
|
|
629
639
|
import * as p2 from "@clack/prompts";
|
|
630
|
-
import chalk3 from "chalk";
|
|
631
640
|
var S_BAR2 = "\u2502";
|
|
632
641
|
var S_BAR_END2 = "\u2514";
|
|
633
642
|
var S_ACTIVE2 = "\u25C6";
|
|
634
643
|
var S_SUBMIT2 = "\u25C6";
|
|
635
644
|
var S_CANCEL2 = "\u25A0";
|
|
636
645
|
var S_ERROR2 = "\u25B2";
|
|
637
|
-
var noColor2 = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
|
|
638
|
-
var dim2 = (s) => noColor2 ? s : chalk3.gray(s);
|
|
639
|
-
var cyan2 = (s) => noColor2 ? s : chalk3.cyan(s);
|
|
640
|
-
var grn2 = (s) => noColor2 ? s : chalk3.green(s);
|
|
641
|
-
var ylw2 = (s) => noColor2 ? s : chalk3.yellow(s);
|
|
642
|
-
var red2 = (s) => noColor2 ? s : chalk3.red(s);
|
|
643
|
-
var bld2 = (s) => noColor2 ? s : chalk3.bold(s);
|
|
644
646
|
function symbol2(state) {
|
|
645
647
|
switch (state) {
|
|
646
648
|
case "submit":
|
|
647
|
-
return
|
|
649
|
+
return grn(S_SUBMIT2);
|
|
648
650
|
case "cancel":
|
|
649
|
-
return
|
|
651
|
+
return red(S_CANCEL2);
|
|
650
652
|
case "error":
|
|
651
|
-
return
|
|
653
|
+
return ylw(S_ERROR2);
|
|
652
654
|
default:
|
|
653
|
-
return
|
|
655
|
+
return active(S_ACTIVE2);
|
|
654
656
|
}
|
|
655
657
|
}
|
|
656
658
|
var MAX_VISIBLE2 = 12;
|
|
@@ -669,18 +671,18 @@ function buildList2(filtered, cursor, scrollOffset, selected) {
|
|
|
669
671
|
const opt = filtered[i];
|
|
670
672
|
const isCursor = i === cursor;
|
|
671
673
|
const isChecked = isMulti && selected.has(opt.bcp47);
|
|
672
|
-
const icon = isMulti ? isChecked ? isCursor ?
|
|
674
|
+
const icon = isMulti ? isChecked ? isCursor ? info("\u25FC") : info("\u25FC") : isCursor ? active("\u25FB") : dim("\u25FB") : isCursor ? active("\u25CF") : dim("\u25CB");
|
|
673
675
|
visibleLines.push(
|
|
674
|
-
`${
|
|
676
|
+
`${info(S_BAR2)} ${icon} ${isCursor ? bld(opt.label) : opt.label}`
|
|
675
677
|
);
|
|
676
678
|
}
|
|
677
679
|
const hidden = filtered.length - (end - scrollOffset);
|
|
678
680
|
if (hidden > 0)
|
|
679
|
-
visibleLines.push(
|
|
680
|
-
if (filtered.length === 0) visibleLines.push(
|
|
681
|
+
visibleLines.push(dim(`${S_BAR2} ${hidden} more \u2014 keep typing to narrow`));
|
|
682
|
+
if (filtered.length === 0) visibleLines.push(dim(`${S_BAR2} No matches`));
|
|
681
683
|
if (isMulti && selected.size > 0) {
|
|
682
684
|
visibleLines.push(
|
|
683
|
-
|
|
685
|
+
dim(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
|
|
684
686
|
);
|
|
685
687
|
}
|
|
686
688
|
return visibleLines.join("\n");
|
|
@@ -716,43 +718,43 @@ async function runFilterablePrompt(opts) {
|
|
|
716
718
|
render() {
|
|
717
719
|
const filtered = getFiltered();
|
|
718
720
|
clampCursor(filtered);
|
|
719
|
-
const hdr = `${
|
|
721
|
+
const hdr = `${dim(S_BAR2)}
|
|
720
722
|
${symbol2(this.state)} ${message}
|
|
721
723
|
`;
|
|
722
|
-
const hint = filter.length > 0 ? filter :
|
|
724
|
+
const hint = filter.length > 0 ? filter : dim(
|
|
723
725
|
`type to filter, \u2191\u2193 navigate${multi ? ", space select" : ""}`
|
|
724
726
|
);
|
|
725
727
|
switch (this.state) {
|
|
726
728
|
case "submit": {
|
|
727
729
|
const val = multi ? Array.from(selected).map((id) => options.find((o) => o.bcp47 === id)?.label ?? id).join(", ") : options.find((o) => o.bcp47 === this.value)?.label ?? "";
|
|
728
|
-
return `${hdr}${
|
|
730
|
+
return `${hdr}${dim(S_BAR2)} ${bld(val || dim("none"))}`;
|
|
729
731
|
}
|
|
730
732
|
case "cancel":
|
|
731
|
-
return `${hdr}${
|
|
733
|
+
return `${hdr}${dim(S_BAR2)}`;
|
|
732
734
|
case "error":
|
|
733
735
|
return [
|
|
734
736
|
hdr.trimEnd(),
|
|
735
|
-
`${
|
|
737
|
+
`${ylw(S_BAR2)} ${dim("/")} ${hint}`,
|
|
736
738
|
buildList2(
|
|
737
739
|
filtered,
|
|
738
740
|
cursor,
|
|
739
741
|
scrollOffset,
|
|
740
742
|
multi ? selected : null
|
|
741
743
|
),
|
|
742
|
-
`${
|
|
744
|
+
`${ylw(S_BAR_END2)} ${ylw(this.error)}`,
|
|
743
745
|
""
|
|
744
746
|
].join("\n");
|
|
745
747
|
default:
|
|
746
748
|
return [
|
|
747
749
|
hdr.trimEnd(),
|
|
748
|
-
`${
|
|
750
|
+
`${info(S_BAR2)} ${dim("/")} ${hint}`,
|
|
749
751
|
buildList2(
|
|
750
752
|
filtered,
|
|
751
753
|
cursor,
|
|
752
754
|
scrollOffset,
|
|
753
755
|
multi ? selected : null
|
|
754
756
|
),
|
|
755
|
-
`${
|
|
757
|
+
`${info(S_BAR_END2)}`,
|
|
756
758
|
""
|
|
757
759
|
].join("\n");
|
|
758
760
|
}
|
|
@@ -861,7 +863,7 @@ function buildLanguageOptions(locales) {
|
|
|
861
863
|
async function runProjectCreate(params) {
|
|
862
864
|
const { api, userToken, organizationId, repoCanonical } = params;
|
|
863
865
|
const projectName = (params.defaultName ?? "my-project").trim();
|
|
864
|
-
p3.log.success(`Project: ${
|
|
866
|
+
p3.log.success(`Project: ${chalk3.bold(projectName)}`);
|
|
865
867
|
let sourceLocales;
|
|
866
868
|
try {
|
|
867
869
|
({ sourceLocales } = await api.listLocales(userToken));
|
|
@@ -874,7 +876,7 @@ async function runProjectCreate(params) {
|
|
|
874
876
|
const languageOptions = buildLanguageOptions(sourceLocales);
|
|
875
877
|
const appDir = params.defaultAppDir ?? "";
|
|
876
878
|
if (appDir) {
|
|
877
|
-
p3.log.success(`App directory: ${
|
|
879
|
+
p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
|
|
878
880
|
}
|
|
879
881
|
const sourceLocale = await searchSelectLocale(
|
|
880
882
|
languageOptions,
|
|
@@ -939,7 +941,7 @@ async function runProjectCreate(params) {
|
|
|
939
941
|
appDirs: appDir ? [appDir] : [],
|
|
940
942
|
repoCanonical
|
|
941
943
|
});
|
|
942
|
-
p3.log.success(`Project ${
|
|
944
|
+
p3.log.success(`Project ${chalk3.bold(result.projectName)} created!`);
|
|
943
945
|
return result;
|
|
944
946
|
} catch (error) {
|
|
945
947
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -966,7 +968,7 @@ async function runAppCreate(params) {
|
|
|
966
968
|
return null;
|
|
967
969
|
}
|
|
968
970
|
if (appDir) {
|
|
969
|
-
p3.log.success(`App directory: ${
|
|
971
|
+
p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
|
|
970
972
|
}
|
|
971
973
|
const sourceLocale = await searchSelectLocale(
|
|
972
974
|
languageOptions,
|
|
@@ -1018,7 +1020,7 @@ async function runAppCreate(params) {
|
|
|
1018
1020
|
}
|
|
1019
1021
|
const targetBranches = appPushBranches;
|
|
1020
1022
|
try {
|
|
1021
|
-
const result = await api.
|
|
1023
|
+
const result = await api.createProject(userToken, {
|
|
1022
1024
|
projectId,
|
|
1023
1025
|
appDir,
|
|
1024
1026
|
sourceLocale,
|
|
@@ -1027,7 +1029,7 @@ async function runAppCreate(params) {
|
|
|
1027
1029
|
repoCanonical: repoCanonical ?? ""
|
|
1028
1030
|
});
|
|
1029
1031
|
p3.log.success(
|
|
1030
|
-
`App ${
|
|
1032
|
+
`App ${chalk3.bold(appDir)} added to ${chalk3.bold(projectName)}!`
|
|
1031
1033
|
);
|
|
1032
1034
|
return {
|
|
1033
1035
|
projectId: result.projectId,
|
|
@@ -1046,7 +1048,7 @@ async function runAppCreate(params) {
|
|
|
1046
1048
|
}
|
|
1047
1049
|
|
|
1048
1050
|
// src/commands/init.ts
|
|
1049
|
-
import
|
|
1051
|
+
import chalk5 from "chalk";
|
|
1050
1052
|
import { join as join2 } from "path";
|
|
1051
1053
|
import { config as loadEnv } from "dotenv";
|
|
1052
1054
|
|
|
@@ -1160,7 +1162,7 @@ function resolveGitContext() {
|
|
|
1160
1162
|
|
|
1161
1163
|
// src/utils/workspace.ts
|
|
1162
1164
|
import * as p4 from "@clack/prompts";
|
|
1163
|
-
import
|
|
1165
|
+
import chalk4 from "chalk";
|
|
1164
1166
|
async function selectWorkspace(result) {
|
|
1165
1167
|
const { workspaces, canCreateWorkspace } = result;
|
|
1166
1168
|
if (workspaces.length === 0) {
|
|
@@ -1264,7 +1266,7 @@ function runScaffold(params) {
|
|
|
1264
1266
|
if (detection.ecosystem) {
|
|
1265
1267
|
const frameworkLabel = detection.framework ?? detection.ecosystem;
|
|
1266
1268
|
const pmLabel = detection.packageManager;
|
|
1267
|
-
p5.log.info(`Detected: ${
|
|
1269
|
+
p5.log.info(`Detected: ${chalk5.bold(frameworkLabel)} (${pmLabel})`);
|
|
1268
1270
|
}
|
|
1269
1271
|
const { devPackages, runtimePackages } = getPackagesToInstall(detection);
|
|
1270
1272
|
const allPackages = [...devPackages, ...runtimePackages];
|
|
@@ -1292,10 +1294,10 @@ function runScaffold(params) {
|
|
|
1292
1294
|
devPackages.length > 0 ? buildInstallCommand(detection.packageManager, devPackages, true) : null,
|
|
1293
1295
|
runtimePackages.length > 0 ? buildInstallCommand(detection.packageManager, runtimePackages, false) : null
|
|
1294
1296
|
].filter(Boolean).join(" && ");
|
|
1295
|
-
p5.log.warn(`Run manually: ${
|
|
1297
|
+
p5.log.warn(`Run manually: ${highlight(cmds)}`);
|
|
1296
1298
|
}
|
|
1297
1299
|
} else if (detection.ecosystem) {
|
|
1298
|
-
p5.log.info(`Packages: ${
|
|
1300
|
+
p5.log.info(`Packages: ${chalk5.green("already installed")}`);
|
|
1299
1301
|
}
|
|
1300
1302
|
const snippets = getSetupSnippets({
|
|
1301
1303
|
framework: detection.framework,
|
|
@@ -1325,19 +1327,19 @@ function runScaffold(params) {
|
|
|
1325
1327
|
code: snippets.wrapStep.code
|
|
1326
1328
|
});
|
|
1327
1329
|
p5.log.message("");
|
|
1328
|
-
p5.log.message(
|
|
1330
|
+
p5.log.message(chalk5.bold("Finish setup in your code"));
|
|
1329
1331
|
p5.log.message("");
|
|
1330
1332
|
for (let i = 0; i < steps.length; i++) {
|
|
1331
1333
|
const step = steps[i];
|
|
1332
1334
|
p5.log.step(
|
|
1333
|
-
`${
|
|
1335
|
+
`${chalk5.bold(step.label)} ${chalk5.dim(`\u2014 ${step.hint}`)}`
|
|
1334
1336
|
);
|
|
1335
1337
|
printCodeBlock(step.code);
|
|
1336
1338
|
if (i < steps.length - 1) p5.log.message("");
|
|
1337
1339
|
}
|
|
1338
1340
|
const written = writeVocoderConfig({ targetBranches, useTypeScript });
|
|
1339
1341
|
if (written) {
|
|
1340
|
-
p5.log.success(`Created ${
|
|
1342
|
+
p5.log.success(`Created ${highlight(written)}`);
|
|
1341
1343
|
} else if (!findExistingConfig(process.cwd())) {
|
|
1342
1344
|
const ext = useTypeScript ? "ts" : "js";
|
|
1343
1345
|
p5.log.warn(
|
|
@@ -1345,11 +1347,11 @@ function runScaffold(params) {
|
|
|
1345
1347
|
);
|
|
1346
1348
|
}
|
|
1347
1349
|
p5.log.message("");
|
|
1348
|
-
const branchList = targetBranches.length > 0 ? targetBranches.map((b) =>
|
|
1350
|
+
const branchList = targetBranches.length > 0 ? targetBranches.map((b) => highlight(b)).join(" or ") : highlight("your target branch");
|
|
1349
1351
|
p5.log.success(
|
|
1350
1352
|
`Push to ${branchList} to trigger your first translation run.`
|
|
1351
1353
|
);
|
|
1352
|
-
p5.log.message(
|
|
1354
|
+
p5.log.message(info(" Docs: https://vocoder.app/docs/getting-started"));
|
|
1353
1355
|
}
|
|
1354
1356
|
function writeApiKeyToEnv(apiKey) {
|
|
1355
1357
|
const envPath = join2(process.cwd(), ".env");
|
|
@@ -1374,12 +1376,12 @@ function writeApiKeyToEnv(apiKey) {
|
|
|
1374
1376
|
function printApiKey(apiKey) {
|
|
1375
1377
|
const saved = writeApiKeyToEnv(apiKey);
|
|
1376
1378
|
p5.log.message("");
|
|
1377
|
-
p5.log.message(
|
|
1379
|
+
p5.log.message(chalk5.bold("Your API Key"));
|
|
1378
1380
|
printCodeBlock(`VOCODER_API_KEY=${apiKey}`);
|
|
1379
1381
|
if (saved) {
|
|
1380
|
-
p5.log.success(
|
|
1382
|
+
p5.log.success(chalk5.dim("Saved to .env"));
|
|
1381
1383
|
} else {
|
|
1382
|
-
p5.log.message(
|
|
1384
|
+
p5.log.message(chalk5.dim(" Add the above to your .env file"));
|
|
1383
1385
|
}
|
|
1384
1386
|
}
|
|
1385
1387
|
function printCodeBlock(code) {
|
|
@@ -1388,20 +1390,20 @@ function printCodeBlock(code) {
|
|
|
1388
1390
|
(max, line) => Math.max(max, line.length),
|
|
1389
1391
|
0
|
|
1390
1392
|
);
|
|
1391
|
-
const bar =
|
|
1393
|
+
const bar = chalk5.gray("\u2502");
|
|
1392
1394
|
const pad = (s) => s + " ".repeat(maxLen - s.length);
|
|
1393
|
-
process.stdout.write(`${
|
|
1395
|
+
process.stdout.write(`${chalk5.gray("\u2502")}
|
|
1394
1396
|
`);
|
|
1395
1397
|
process.stdout.write(
|
|
1396
|
-
`${
|
|
1398
|
+
`${chalk5.gray("\u2502")} ${chalk5.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
|
|
1397
1399
|
`
|
|
1398
1400
|
);
|
|
1399
1401
|
for (const line of lines) {
|
|
1400
|
-
process.stdout.write(`${
|
|
1402
|
+
process.stdout.write(`${chalk5.gray("\u2502")} ${bar} ${pad(line)} ${bar}
|
|
1401
1403
|
`);
|
|
1402
1404
|
}
|
|
1403
1405
|
process.stdout.write(
|
|
1404
|
-
`${
|
|
1406
|
+
`${chalk5.gray("\u2502")} ${chalk5.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
|
|
1405
1407
|
`
|
|
1406
1408
|
);
|
|
1407
1409
|
}
|
|
@@ -1540,7 +1542,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
|
|
|
1540
1542
|
return null;
|
|
1541
1543
|
}
|
|
1542
1544
|
const userInfo = await api.getCliUserInfo(rawToken);
|
|
1543
|
-
authSpinner.stop(`Authenticated as ${
|
|
1545
|
+
authSpinner.stop(`Authenticated as ${chalk5.bold(userInfo.email)}`);
|
|
1544
1546
|
return {
|
|
1545
1547
|
token: rawToken,
|
|
1546
1548
|
...userInfo,
|
|
@@ -1550,7 +1552,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
|
|
|
1550
1552
|
}
|
|
1551
1553
|
async function init(options = {}) {
|
|
1552
1554
|
const apiUrl = options.apiUrl || process.env.VOCODER_API_URL || "https://vocoder.app";
|
|
1553
|
-
p5.intro(
|
|
1555
|
+
p5.intro(active(chalk5.bold("Vocoder Setup")));
|
|
1554
1556
|
try {
|
|
1555
1557
|
const gitContext = resolveGitContext();
|
|
1556
1558
|
const identity = gitContext.identity;
|
|
@@ -1564,15 +1566,15 @@ async function init(options = {}) {
|
|
|
1564
1566
|
let repoProjectName = null;
|
|
1565
1567
|
if (identity) {
|
|
1566
1568
|
const anonApi = new VocoderAPI({ apiUrl, apiKey: "" });
|
|
1567
|
-
const lookup = await anonApi.
|
|
1569
|
+
const lookup = await anonApi.lookupAppByRepo({
|
|
1568
1570
|
repoCanonical: identity.repoCanonical,
|
|
1569
1571
|
appDir: identity.repoAppDir
|
|
1570
1572
|
});
|
|
1571
1573
|
if (lookup.exactMatch) {
|
|
1572
1574
|
const { exactMatch } = lookup;
|
|
1573
|
-
p5.log.success(`Project: ${
|
|
1575
|
+
p5.log.success(`Project: ${chalk5.bold(exactMatch.projectName)}`);
|
|
1574
1576
|
p5.log.info(
|
|
1575
|
-
`Branches: ${
|
|
1577
|
+
`Branches: ${highlight((exactMatch.targetBranches ?? ["main"]).join(", "))}`
|
|
1576
1578
|
);
|
|
1577
1579
|
const needsKey = await p5.confirm({
|
|
1578
1580
|
message: "Need to regenerate your API key?"
|
|
@@ -1605,18 +1607,18 @@ async function init(options = {}) {
|
|
|
1605
1607
|
}
|
|
1606
1608
|
const isTs = detectLocalEcosystem().isTypeScript;
|
|
1607
1609
|
const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"], useTypeScript: isTs });
|
|
1608
|
-
if (written) p5.log.success(`Created ${
|
|
1609
|
-
p5.outro("Vocoder is already set up for this repository.");
|
|
1610
|
+
if (written) p5.log.success(`Created ${highlight(written)}`);
|
|
1611
|
+
p5.outro(active("Vocoder is already set up for this repository."));
|
|
1610
1612
|
return 0;
|
|
1611
1613
|
}
|
|
1612
1614
|
if (lookup.hasWholeRepoApp) {
|
|
1613
1615
|
const wholeRepo = lookup.existingApps.find((a) => a.appDir === "");
|
|
1614
1616
|
if (wholeRepo) {
|
|
1615
|
-
p5.log.success(`Project: ${
|
|
1617
|
+
p5.log.success(`Project: ${chalk5.bold(wholeRepo.projectName)}`);
|
|
1616
1618
|
const isTs = detectLocalEcosystem().isTypeScript;
|
|
1617
1619
|
const written = writeVocoderConfig({ targetBranches: ["main"], useTypeScript: isTs });
|
|
1618
|
-
if (written) p5.log.success(`Created ${
|
|
1619
|
-
p5.outro("Vocoder is already set up for this repository.");
|
|
1620
|
+
if (written) p5.log.success(`Created ${highlight(written)}`);
|
|
1621
|
+
p5.outro(active("Vocoder is already set up for this repository."));
|
|
1620
1622
|
return 0;
|
|
1621
1623
|
}
|
|
1622
1624
|
}
|
|
@@ -1633,7 +1635,7 @@ async function init(options = {}) {
|
|
|
1633
1635
|
let authOrganizationId;
|
|
1634
1636
|
const storedAuth = await verifyStoredAuth(api);
|
|
1635
1637
|
if (storedAuth.status === "valid") {
|
|
1636
|
-
p5.log.success(`Authenticated as ${
|
|
1638
|
+
p5.log.success(`Authenticated as ${chalk5.bold(storedAuth.email)}`);
|
|
1637
1639
|
userToken = storedAuth.token;
|
|
1638
1640
|
userEmail = storedAuth.email;
|
|
1639
1641
|
userName = storedAuth.name;
|
|
@@ -1673,7 +1675,7 @@ async function init(options = {}) {
|
|
|
1673
1675
|
selectedWorkspaceId = authOrganizationId;
|
|
1674
1676
|
selectedWorkspaceName = ws?.name ?? userEmail;
|
|
1675
1677
|
p5.log.success(
|
|
1676
|
-
`Connected as ${
|
|
1678
|
+
`Connected as ${chalk5.bold(userEmail)} \u2014 workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1677
1679
|
);
|
|
1678
1680
|
} else {
|
|
1679
1681
|
const discoveryResult = await api.getCliGitHubDiscovery(userToken).catch(() => null);
|
|
@@ -1724,7 +1726,7 @@ async function init(options = {}) {
|
|
|
1724
1726
|
});
|
|
1725
1727
|
selectedWorkspaceId = claimResult.organizationId;
|
|
1726
1728
|
selectedWorkspaceName = claimResult.organizationName;
|
|
1727
|
-
p5.log.success(`Workspace: ${
|
|
1729
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1728
1730
|
} else {
|
|
1729
1731
|
const workspaceData = await api.listWorkspaces(userToken, {
|
|
1730
1732
|
repo: identity?.repoCanonical
|
|
@@ -1738,13 +1740,13 @@ async function init(options = {}) {
|
|
|
1738
1740
|
const ws = covering[0];
|
|
1739
1741
|
selectedWorkspaceId = ws.id;
|
|
1740
1742
|
selectedWorkspaceName = ws.name;
|
|
1741
|
-
p5.log.success(`Workspace: ${
|
|
1743
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1742
1744
|
} else if (repoCanonical && covering.length > 1) {
|
|
1743
1745
|
const choice = await p5.select({
|
|
1744
1746
|
message: "Select workspace for this repo",
|
|
1745
1747
|
options: covering.map((w) => ({
|
|
1746
1748
|
value: w.id,
|
|
1747
|
-
label: `${w.name} ${
|
|
1749
|
+
label: `${w.name} ${chalk5.dim(`(${w.projectCount} project${w.projectCount !== 1 ? "s" : ""})`)}`
|
|
1748
1750
|
}))
|
|
1749
1751
|
});
|
|
1750
1752
|
if (p5.isCancel(choice)) {
|
|
@@ -1754,11 +1756,11 @@ async function init(options = {}) {
|
|
|
1754
1756
|
const ws = covering.find((w) => w.id === choice);
|
|
1755
1757
|
selectedWorkspaceId = ws.id;
|
|
1756
1758
|
selectedWorkspaceName = ws.name;
|
|
1757
|
-
p5.log.success(`Workspace: ${
|
|
1759
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1758
1760
|
} else if (repoCanonical && covering.length === 0 && connected.length > 0) {
|
|
1759
1761
|
const shortRepo = repoCanonical.split(":")[1] ?? repoCanonical;
|
|
1760
1762
|
p5.log.warn(
|
|
1761
|
-
`${
|
|
1763
|
+
`${chalk5.bold(shortRepo)} isn't accessible from your Vocoder installation.
|
|
1762
1764
|
Grant access to this repository or install on the account that owns it.`
|
|
1763
1765
|
);
|
|
1764
1766
|
const fixOptions = [];
|
|
@@ -1766,13 +1768,13 @@ async function init(options = {}) {
|
|
|
1766
1768
|
if (ws.installationConfigureUrl) {
|
|
1767
1769
|
fixOptions.push({
|
|
1768
1770
|
value: `grant:${ws.id}`,
|
|
1769
|
-
label: `Configure ${
|
|
1771
|
+
label: `Configure ${chalk5.bold(ws.connectionLabel ?? ws.name)}'s GitHub App installation`
|
|
1770
1772
|
});
|
|
1771
1773
|
}
|
|
1772
1774
|
}
|
|
1773
1775
|
fixOptions.push({
|
|
1774
1776
|
value: "install_new",
|
|
1775
|
-
label: `Install on a different GitHub account ${
|
|
1777
|
+
label: `Install on a different GitHub account ${chalk5.dim("(creates a new personal workspace)")}`
|
|
1776
1778
|
});
|
|
1777
1779
|
fixOptions.push({ value: "cancel", label: "Cancel" });
|
|
1778
1780
|
const fix = await p5.select({
|
|
@@ -1787,8 +1789,8 @@ async function init(options = {}) {
|
|
|
1787
1789
|
const ws = connected.find((w) => `grant:${w.id}` === fix);
|
|
1788
1790
|
await tryOpenBrowser2(ws.installationConfigureUrl);
|
|
1789
1791
|
p5.cancel(
|
|
1790
|
-
`Grant access to ${
|
|
1791
|
-
then re-run ${
|
|
1792
|
+
`Grant access to ${chalk5.bold(shortRepo)} in your browser,
|
|
1793
|
+
then re-run ${chalk5.bold("vocoder init")}.`
|
|
1792
1794
|
);
|
|
1793
1795
|
return 1;
|
|
1794
1796
|
}
|
|
@@ -1805,13 +1807,13 @@ async function init(options = {}) {
|
|
|
1805
1807
|
}
|
|
1806
1808
|
selectedWorkspaceId = connectResult.organizationId;
|
|
1807
1809
|
selectedWorkspaceName = connectResult.organizationName;
|
|
1808
|
-
p5.log.success(`Workspace: ${
|
|
1810
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1809
1811
|
} else {
|
|
1810
1812
|
if (workspaceData.workspaces.length === 1 && !workspaceData.canCreateWorkspace) {
|
|
1811
1813
|
const ws = workspaceData.workspaces[0];
|
|
1812
1814
|
selectedWorkspaceId = ws.id;
|
|
1813
1815
|
selectedWorkspaceName = ws.name;
|
|
1814
|
-
p5.log.success(`Workspace: ${
|
|
1816
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1815
1817
|
} else {
|
|
1816
1818
|
const workspaceResult = await selectWorkspace(workspaceData);
|
|
1817
1819
|
if (workspaceResult.action === "cancelled") {
|
|
@@ -1821,7 +1823,7 @@ async function init(options = {}) {
|
|
|
1821
1823
|
if (workspaceResult.action === "use") {
|
|
1822
1824
|
selectedWorkspaceId = workspaceResult.workspace.id;
|
|
1823
1825
|
selectedWorkspaceName = workspaceResult.workspace.name;
|
|
1824
|
-
p5.log.success(`Workspace: ${
|
|
1826
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1825
1827
|
} else {
|
|
1826
1828
|
const connectChoice = await p5.select({
|
|
1827
1829
|
message: "Connect your new workspace to GitHub",
|
|
@@ -1849,7 +1851,7 @@ async function init(options = {}) {
|
|
|
1849
1851
|
selectedWorkspaceId = connectResult.organizationId;
|
|
1850
1852
|
selectedWorkspaceName = connectResult.organizationName;
|
|
1851
1853
|
p5.log.success(
|
|
1852
|
-
`Workspace: ${
|
|
1854
|
+
`Workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1853
1855
|
);
|
|
1854
1856
|
} else {
|
|
1855
1857
|
const installations = await runGitHubDiscoveryFlow({
|
|
@@ -1911,7 +1913,7 @@ async function init(options = {}) {
|
|
|
1911
1913
|
}
|
|
1912
1914
|
}
|
|
1913
1915
|
p5.log.success(
|
|
1914
|
-
`Workspace: ${
|
|
1916
|
+
`Workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1915
1917
|
);
|
|
1916
1918
|
}
|
|
1917
1919
|
}
|
|
@@ -1921,8 +1923,8 @@ async function init(options = {}) {
|
|
|
1921
1923
|
}
|
|
1922
1924
|
if (repoProjectId && repoProjectName && existingAppsForRepo.length > 0) {
|
|
1923
1925
|
p5.log.info(
|
|
1924
|
-
`${
|
|
1925
|
-
Configured apps: ${existingAppsForRepo.map((a) =>
|
|
1926
|
+
`${chalk5.bold(repoProjectName)} is already set up for this repo.
|
|
1927
|
+
Configured apps: ${existingAppsForRepo.map((a) => highlight(a.appDir || "(entire repo)")).join(", ")}`
|
|
1926
1928
|
);
|
|
1927
1929
|
const appResult = await runAppCreate({
|
|
1928
1930
|
api,
|
|
@@ -1943,7 +1945,7 @@ async function init(options = {}) {
|
|
|
1943
1945
|
targetBranches: appResult.targetBranches,
|
|
1944
1946
|
appDir: identity?.repoAppDir
|
|
1945
1947
|
});
|
|
1946
|
-
p5.outro("You're all set.");
|
|
1948
|
+
p5.outro(active("You're all set."));
|
|
1947
1949
|
return 0;
|
|
1948
1950
|
}
|
|
1949
1951
|
try {
|
|
@@ -1951,13 +1953,13 @@ async function init(options = {}) {
|
|
|
1951
1953
|
const ws = wsCheck.workspaces.find((w) => w.id === selectedWorkspaceId);
|
|
1952
1954
|
if (ws && ws.maxProjects !== -1 && ws.projectCount >= ws.maxProjects) {
|
|
1953
1955
|
p5.log.warn(
|
|
1954
|
-
`Project limit reached \u2014 ${ws.projectCount}/${ws.maxProjects} on your ${
|
|
1956
|
+
`Project limit reached \u2014 ${ws.projectCount}/${ws.maxProjects} on your ${chalk5.bold(ws.planId)} plan.`
|
|
1955
1957
|
);
|
|
1956
1958
|
const options2 = [];
|
|
1957
1959
|
if (repoProjectId) {
|
|
1958
1960
|
options2.push({
|
|
1959
1961
|
value: "connect",
|
|
1960
|
-
label: `Reconnect this repo to ${
|
|
1962
|
+
label: `Reconnect this repo to ${chalk5.bold(repoProjectName ?? "existing project")}`
|
|
1961
1963
|
});
|
|
1962
1964
|
}
|
|
1963
1965
|
options2.push({ value: "upgrade", label: "Upgrade plan" });
|
|
@@ -1977,7 +1979,7 @@ async function init(options = {}) {
|
|
|
1977
1979
|
);
|
|
1978
1980
|
return 1;
|
|
1979
1981
|
}
|
|
1980
|
-
const existingProjects = await api.
|
|
1982
|
+
const existingProjects = await api.listApps(
|
|
1981
1983
|
userToken,
|
|
1982
1984
|
selectedWorkspaceId
|
|
1983
1985
|
);
|
|
@@ -1997,7 +1999,7 @@ async function init(options = {}) {
|
|
|
1997
1999
|
targetBranches: chosenProject.targetBranches,
|
|
1998
2000
|
repoCanonical: identity?.repoCanonical ?? ""
|
|
1999
2001
|
});
|
|
2000
|
-
p5.log.success(`Connected to project: ${
|
|
2002
|
+
p5.log.success(`Connected to project: ${chalk5.bold(chosenProject.name)}`);
|
|
2001
2003
|
printApiKey(appResult.apiKey);
|
|
2002
2004
|
runScaffold({
|
|
2003
2005
|
sourceLocale: chosenProject.sourceLocale,
|
|
@@ -2009,7 +2011,7 @@ async function init(options = {}) {
|
|
|
2009
2011
|
p5.log.error(`Failed to create app binding: ${msg}`);
|
|
2010
2012
|
return 1;
|
|
2011
2013
|
}
|
|
2012
|
-
p5.outro("You're all set.");
|
|
2014
|
+
p5.outro(active("You're all set."));
|
|
2013
2015
|
return 0;
|
|
2014
2016
|
}
|
|
2015
2017
|
} catch {
|
|
@@ -2036,7 +2038,7 @@ Translations won't run automatically until you grant access.
|
|
|
2036
2038
|
To fix: go to your GitHub App installation settings and add this
|
|
2037
2039
|
repository to the allowed list, or switch to "All repositories".
|
|
2038
2040
|
` + (projectResult.configureUrl ? `
|
|
2039
|
-
${
|
|
2041
|
+
${chalk5.dim(projectResult.configureUrl)}
|
|
2040
2042
|
` : "")
|
|
2041
2043
|
);
|
|
2042
2044
|
}
|
|
@@ -2046,7 +2048,7 @@ Translations won't run automatically until you grant access.
|
|
|
2046
2048
|
appDir: identity?.repoAppDir
|
|
2047
2049
|
});
|
|
2048
2050
|
printApiKey(projectResult.apiKey);
|
|
2049
|
-
p5.outro("You're all set.");
|
|
2051
|
+
p5.outro(active("You're all set."));
|
|
2050
2052
|
return 0;
|
|
2051
2053
|
} catch (error) {
|
|
2052
2054
|
if (error instanceof Error) {
|
|
@@ -2064,7 +2066,7 @@ Translations won't run automatically until you grant access.
|
|
|
2064
2066
|
|
|
2065
2067
|
// src/commands/locales.ts
|
|
2066
2068
|
import * as p8 from "@clack/prompts";
|
|
2067
|
-
import
|
|
2069
|
+
import chalk7 from "chalk";
|
|
2068
2070
|
import { config as loadEnv3 } from "dotenv";
|
|
2069
2071
|
|
|
2070
2072
|
// src/commands/sync.ts
|
|
@@ -2072,7 +2074,7 @@ import { createHash, randomUUID } from "crypto";
|
|
|
2072
2074
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2073
2075
|
import { join as join3 } from "path";
|
|
2074
2076
|
import * as p7 from "@clack/prompts";
|
|
2075
|
-
import
|
|
2077
|
+
import chalk6 from "chalk";
|
|
2076
2078
|
|
|
2077
2079
|
// src/utils/branch.ts
|
|
2078
2080
|
import { execSync as execSync4 } from "child_process";
|
|
@@ -2142,7 +2144,6 @@ function matchBranchPattern(branch, pattern) {
|
|
|
2142
2144
|
|
|
2143
2145
|
// src/utils/config.ts
|
|
2144
2146
|
import * as p6 from "@clack/prompts";
|
|
2145
|
-
import chalk7 from "chalk";
|
|
2146
2147
|
import { config as loadEnv2 } from "dotenv";
|
|
2147
2148
|
loadEnv2();
|
|
2148
2149
|
function validateLocalConfig(config) {
|
|
@@ -2201,7 +2202,7 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
|
|
|
2201
2202
|
const fileConfig = loadVocoderConfig(process.cwd());
|
|
2202
2203
|
if (!fileConfig) {
|
|
2203
2204
|
p6.log.warn(
|
|
2204
|
-
`No ${
|
|
2205
|
+
`No ${highlight("vocoder.config.ts")} found \u2014 run ${highlight("npx @vocoder/cli init")} to generate one.`
|
|
2205
2206
|
);
|
|
2206
2207
|
}
|
|
2207
2208
|
const envExtractionPattern = process.env.VOCODER_INCLUDE_PATTERN;
|
|
@@ -2280,13 +2281,13 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
|
|
|
2280
2281
|
}
|
|
2281
2282
|
if (verbose) {
|
|
2282
2283
|
const lines = [
|
|
2283
|
-
`Include patterns: ${
|
|
2284
|
-
...excludePattern.length > 0 ? [`Exclude patterns: ${
|
|
2285
|
-
`API key: ${
|
|
2286
|
-
`API URL: ${
|
|
2287
|
-
`Sync mode: ${
|
|
2288
|
-
...maxWaitMs ? [`Max wait: ${
|
|
2289
|
-
`No fallback: ${
|
|
2284
|
+
`Include patterns: ${highlight(configSources.includePattern)}`,
|
|
2285
|
+
...excludePattern.length > 0 ? [`Exclude patterns: ${highlight(configSources.excludePattern)}`] : [],
|
|
2286
|
+
`API key: ${highlight(configSources.apiKey)}`,
|
|
2287
|
+
`API URL: ${highlight(configSources.apiUrl)}`,
|
|
2288
|
+
`Sync mode: ${highlight(configSources.mode)}`,
|
|
2289
|
+
...maxWaitMs ? [`Max wait: ${highlight(String(configSources.maxWaitMs))}`] : [],
|
|
2290
|
+
`No fallback: ${highlight(String(configSources.noFallback))}`
|
|
2290
2291
|
];
|
|
2291
2292
|
p6.note(lines.join("\n"), "Configuration sources");
|
|
2292
2293
|
}
|
|
@@ -2557,7 +2558,7 @@ async function fetchApiSnapshot(api, params) {
|
|
|
2557
2558
|
async function sync(options = {}) {
|
|
2558
2559
|
const startTime = Date.now();
|
|
2559
2560
|
const projectRoot = process.cwd();
|
|
2560
|
-
p7.intro("Vocoder Sync");
|
|
2561
|
+
p7.intro(active("Vocoder Sync"));
|
|
2561
2562
|
const mergedConfig = await getMergedConfig(options, options.verbose);
|
|
2562
2563
|
if (!mergedConfig.apiKey) {
|
|
2563
2564
|
p7.log.warn("No API key found. Run init to get started:");
|
|
@@ -2566,7 +2567,7 @@ async function sync(options = {}) {
|
|
|
2566
2567
|
p7.log.info(
|
|
2567
2568
|
" Or add your key to .env: VOCODER_API_KEY=vcp_..."
|
|
2568
2569
|
);
|
|
2569
|
-
p7.outro("Run `npx @vocoder/cli init` to set up your project.");
|
|
2570
|
+
p7.outro(active("Run `npx @vocoder/cli init` to set up your project."));
|
|
2570
2571
|
return 1;
|
|
2571
2572
|
}
|
|
2572
2573
|
const spinner7 = p7.spinner();
|
|
@@ -2579,7 +2580,7 @@ async function sync(options = {}) {
|
|
|
2579
2580
|
};
|
|
2580
2581
|
validateLocalConfig(localConfig);
|
|
2581
2582
|
const api = new VocoderAPI(localConfig);
|
|
2582
|
-
const apiConfig = await api.
|
|
2583
|
+
const apiConfig = await api.getAppConfig();
|
|
2583
2584
|
const requestedMode = mergedConfig.mode;
|
|
2584
2585
|
const waitTimeoutMs = resolveWaitTimeoutMs({
|
|
2585
2586
|
requestedMaxWaitMs: mergedConfig.maxWaitMs,
|
|
@@ -2596,10 +2597,10 @@ async function sync(options = {}) {
|
|
|
2596
2597
|
...fileConfig?.appIndustry ? { appIndustry: fileConfig.appIndustry } : {},
|
|
2597
2598
|
...fileConfig?.formality ? { formality: fileConfig.formality } : {}
|
|
2598
2599
|
};
|
|
2599
|
-
spinner7.stop(`Branch: ${
|
|
2600
|
+
spinner7.stop(`Branch: ${highlight(branch)}`);
|
|
2600
2601
|
if (!options.force && !isTargetBranch(branch, config.targetBranches)) {
|
|
2601
2602
|
p7.log.warn(
|
|
2602
|
-
`Skipping translations (${
|
|
2603
|
+
`Skipping translations (${highlight(branch)} is not a target branch)`
|
|
2603
2604
|
);
|
|
2604
2605
|
p7.log.info(`Target branches: ${config.targetBranches.join(", ")}`);
|
|
2605
2606
|
p7.log.info("Use --force to translate anyway");
|
|
@@ -2623,7 +2624,7 @@ async function sync(options = {}) {
|
|
|
2623
2624
|
return 0;
|
|
2624
2625
|
}
|
|
2625
2626
|
spinner7.stop(
|
|
2626
|
-
`Extracted ${
|
|
2627
|
+
`Extracted ${highlight(extractedStrings.length)} strings from ${highlight(patternsDisplay)}`
|
|
2627
2628
|
);
|
|
2628
2629
|
if (options.verbose) {
|
|
2629
2630
|
const sampleLines = extractedStrings.slice(0, 5).map((s) => ` "${s.text}" (${s.file}:${s.line})`);
|
|
@@ -2644,7 +2645,7 @@ async function sync(options = {}) {
|
|
|
2644
2645
|
].join("\n"),
|
|
2645
2646
|
"Dry run - would translate"
|
|
2646
2647
|
);
|
|
2647
|
-
p7.outro("No API calls made.");
|
|
2648
|
+
p7.outro(active("No API calls made."));
|
|
2648
2649
|
return 0;
|
|
2649
2650
|
}
|
|
2650
2651
|
const repoIdentity = resolveGitRepositoryIdentity();
|
|
@@ -2666,14 +2667,14 @@ async function sync(options = {}) {
|
|
|
2666
2667
|
const cacheFile = getCacheFilePath(projectRoot, fingerprint);
|
|
2667
2668
|
if (existsSync3(cacheFile)) {
|
|
2668
2669
|
if (options.verbose) {
|
|
2669
|
-
p7.log.info(`Cache hit: ${
|
|
2670
|
+
p7.log.info(`Cache hit: ${chalk6.dim(cacheFile)} (fingerprint ${highlight(fingerprint)})`);
|
|
2670
2671
|
}
|
|
2671
2672
|
const duration2 = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
2672
|
-
p7.outro(`Up to date (${duration2}s)`);
|
|
2673
|
+
p7.outro(active(`Up to date (${duration2}s)`));
|
|
2673
2674
|
return 0;
|
|
2674
2675
|
}
|
|
2675
2676
|
if (options.verbose) {
|
|
2676
|
-
p7.log.info(`No cache for fingerprint ${
|
|
2677
|
+
p7.log.info(`No cache for fingerprint ${highlight(fingerprint)} \u2014 will submit to API`);
|
|
2677
2678
|
}
|
|
2678
2679
|
}
|
|
2679
2680
|
spinner7.start("Submitting strings to Vocoder API");
|
|
@@ -2698,7 +2699,7 @@ async function sync(options = {}) {
|
|
|
2698
2699
|
policy: config.syncPolicy
|
|
2699
2700
|
});
|
|
2700
2701
|
if (options.verbose) {
|
|
2701
|
-
p7.log.info(`Batch: ${
|
|
2702
|
+
p7.log.info(`Batch: ${chalk6.dim(batchResponse.batchId)}`);
|
|
2702
2703
|
p7.log.info(`Requested mode: ${requestedMode}`);
|
|
2703
2704
|
p7.log.info(`Effective mode: ${effectiveMode}`);
|
|
2704
2705
|
p7.log.info(`Wait timeout: ${waitTimeoutMs}ms`);
|
|
@@ -2707,14 +2708,14 @@ async function sync(options = {}) {
|
|
|
2707
2708
|
}
|
|
2708
2709
|
}
|
|
2709
2710
|
if (batchResponse.status === "UP_TO_DATE" && batchResponse.noChanges) {
|
|
2710
|
-
p7.log.success(`Up to date \u2014 ${
|
|
2711
|
+
p7.log.success(`Up to date \u2014 ${highlight(batchResponse.totalStrings)} strings, no changes`);
|
|
2711
2712
|
} else if (batchResponse.newStrings === 0) {
|
|
2712
|
-
const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${
|
|
2713
|
-
p7.log.success(`No new strings \u2014 ${
|
|
2713
|
+
const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${chalk6.yellow(batchResponse.deletedStrings)} archived` : "";
|
|
2714
|
+
p7.log.success(`No new strings \u2014 ${highlight(batchResponse.totalStrings)} total${archivedNote}, using existing translations`);
|
|
2714
2715
|
} else {
|
|
2715
|
-
const statParts = [`${
|
|
2716
|
+
const statParts = [`${highlight(batchResponse.newStrings)} new, ${highlight(batchResponse.totalStrings)} total`];
|
|
2716
2717
|
if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
|
|
2717
|
-
statParts.push(`${
|
|
2718
|
+
statParts.push(`${chalk6.yellow(batchResponse.deletedStrings)} archived`);
|
|
2718
2719
|
}
|
|
2719
2720
|
const estTime = batchResponse.estimatedTime ? ` (~${batchResponse.estimatedTime}s)` : "";
|
|
2720
2721
|
p7.log.info(`${statParts.join(", ")} \u2192 syncing to ${config.targetLocales.join(", ")}${estTime}`);
|
|
@@ -2835,7 +2836,7 @@ async function sync(options = {}) {
|
|
|
2835
2836
|
);
|
|
2836
2837
|
}
|
|
2837
2838
|
const duration = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
2838
|
-
p7.outro(`Sync complete! (${duration}s)`);
|
|
2839
|
+
p7.outro(active(`Sync complete! (${duration}s)`));
|
|
2839
2840
|
return 0;
|
|
2840
2841
|
} catch (error) {
|
|
2841
2842
|
spinner7.stop();
|
|
@@ -2898,15 +2899,15 @@ async function listProjectLocales(options = {}) {
|
|
|
2898
2899
|
if (!config) return 1;
|
|
2899
2900
|
const api = new VocoderAPI(config);
|
|
2900
2901
|
try {
|
|
2901
|
-
const
|
|
2902
|
+
const projectConfig = await api.getAppConfig();
|
|
2902
2903
|
p8.log.info(
|
|
2903
|
-
`Source locale: ${
|
|
2904
|
+
`Source locale: ${highlight(projectConfig.sourceLocale)}`
|
|
2904
2905
|
);
|
|
2905
|
-
if (
|
|
2906
|
+
if (projectConfig.targetLocales.length === 0) {
|
|
2906
2907
|
p8.log.info("Target locales: (none configured)");
|
|
2907
2908
|
} else {
|
|
2908
2909
|
p8.log.info(
|
|
2909
|
-
`Target locales: ${
|
|
2910
|
+
`Target locales: ${projectConfig.targetLocales.map((l) => highlight(l)).join(", ")}`
|
|
2910
2911
|
);
|
|
2911
2912
|
}
|
|
2912
2913
|
return 0;
|
|
@@ -2933,9 +2934,9 @@ async function addLocales(locales, options = {}) {
|
|
|
2933
2934
|
try {
|
|
2934
2935
|
const result = await api.addLocale(locale);
|
|
2935
2936
|
lastTargetLocales = result.targetLocales;
|
|
2936
|
-
spinner7.stop(`Added ${
|
|
2937
|
+
spinner7.stop(`Added ${highlight(locale)}`);
|
|
2937
2938
|
} catch (error) {
|
|
2938
|
-
spinner7.stop(`Failed to add ${
|
|
2939
|
+
spinner7.stop(`Failed to add ${chalk7.red(locale)}`);
|
|
2939
2940
|
hadError = true;
|
|
2940
2941
|
if (error instanceof VocoderAPIError && error.limitError) {
|
|
2941
2942
|
const { limitError } = error;
|
|
@@ -2952,7 +2953,7 @@ async function addLocales(locales, options = {}) {
|
|
|
2952
2953
|
}
|
|
2953
2954
|
if (lastTargetLocales.length > 0) {
|
|
2954
2955
|
p8.log.info(
|
|
2955
|
-
`Target locales now: ${lastTargetLocales.map((l) =>
|
|
2956
|
+
`Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
|
|
2956
2957
|
);
|
|
2957
2958
|
}
|
|
2958
2959
|
return hadError ? 1 : 0;
|
|
@@ -2973,9 +2974,9 @@ async function removeLocales(locales, options = {}) {
|
|
|
2973
2974
|
try {
|
|
2974
2975
|
const result = await api.removeLocale(locale);
|
|
2975
2976
|
lastTargetLocales = result.targetLocales;
|
|
2976
|
-
spinner7.stop(`Removed ${
|
|
2977
|
+
spinner7.stop(`Removed ${highlight(locale)}`);
|
|
2977
2978
|
} catch (error) {
|
|
2978
|
-
spinner7.stop(`Failed to remove ${
|
|
2979
|
+
spinner7.stop(`Failed to remove ${chalk7.red(locale)}`);
|
|
2979
2980
|
hadError = true;
|
|
2980
2981
|
p8.log.error(
|
|
2981
2982
|
error instanceof Error ? error.message : "Unknown error"
|
|
@@ -2984,7 +2985,7 @@ async function removeLocales(locales, options = {}) {
|
|
|
2984
2985
|
}
|
|
2985
2986
|
if (lastTargetLocales.length > 0) {
|
|
2986
2987
|
p8.log.info(
|
|
2987
|
-
`Target locales now: ${lastTargetLocales.map((l) =>
|
|
2988
|
+
`Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
|
|
2988
2989
|
);
|
|
2989
2990
|
} else if (!hadError) {
|
|
2990
2991
|
p8.log.info("Target locales now: (none configured)");
|
|
@@ -2997,10 +2998,10 @@ async function listSupportedLocales(options = {}) {
|
|
|
2997
2998
|
const api = new VocoderAPI(config);
|
|
2998
2999
|
try {
|
|
2999
3000
|
const result = await api.listLocales(config.apiKey);
|
|
3000
|
-
p8.log.info(
|
|
3001
|
+
p8.log.info(chalk7.bold("Source locales:"));
|
|
3001
3002
|
printLocaleTable(result.sourceLocales);
|
|
3002
3003
|
p8.log.info("");
|
|
3003
|
-
p8.log.info(
|
|
3004
|
+
p8.log.info(chalk7.bold("Target locales:"));
|
|
3004
3005
|
printLocaleTable(result.targetLocales);
|
|
3005
3006
|
return 0;
|
|
3006
3007
|
} catch (error) {
|
|
@@ -3013,7 +3014,7 @@ async function listSupportedLocales(options = {}) {
|
|
|
3013
3014
|
function printLocaleTable(locales) {
|
|
3014
3015
|
for (const locale of locales) {
|
|
3015
3016
|
const native = locale.nativeName && locale.nativeName !== locale.name ? ` (${locale.nativeName})` : "";
|
|
3016
|
-
p8.log.info(` ${
|
|
3017
|
+
p8.log.info(` ${highlight(locale.code.padEnd(10))} ${locale.name}${native}`);
|
|
3017
3018
|
}
|
|
3018
3019
|
}
|
|
3019
3020
|
|
|
@@ -3036,12 +3037,12 @@ async function logout(options = {}) {
|
|
|
3036
3037
|
return 0;
|
|
3037
3038
|
}
|
|
3038
3039
|
|
|
3039
|
-
// src/commands/
|
|
3040
|
+
// src/commands/app-config.ts
|
|
3040
3041
|
import * as p10 from "@clack/prompts";
|
|
3041
|
-
import
|
|
3042
|
+
import chalk8 from "chalk";
|
|
3042
3043
|
import { config as loadEnv4 } from "dotenv";
|
|
3043
3044
|
loadEnv4();
|
|
3044
|
-
async function
|
|
3045
|
+
async function appConfig(options = {}) {
|
|
3045
3046
|
const apiKey = process.env.VOCODER_API_KEY;
|
|
3046
3047
|
if (!apiKey) {
|
|
3047
3048
|
p10.log.error(
|
|
@@ -3052,21 +3053,21 @@ async function projectConfig(options = {}) {
|
|
|
3052
3053
|
const apiUrl = options.apiUrl ?? process.env.VOCODER_API_URL ?? "https://vocoder.app";
|
|
3053
3054
|
const api = new VocoderAPI({ apiKey, apiUrl });
|
|
3054
3055
|
try {
|
|
3055
|
-
const config = await api.
|
|
3056
|
+
const config = await api.getAppConfig();
|
|
3056
3057
|
const lines = [
|
|
3057
|
-
`
|
|
3058
|
+
`App: ${chalk8.bold(config.projectName)}`,
|
|
3058
3059
|
`Organization: ${config.organizationName}`,
|
|
3059
|
-
`Source locale: ${
|
|
3060
|
-
`Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) =>
|
|
3061
|
-
`Target branches: ${config.targetBranches.map((b) =>
|
|
3062
|
-
...config.primaryBranch ? [`Primary branch: ${
|
|
3060
|
+
`Source locale: ${highlight(config.sourceLocale)}`,
|
|
3061
|
+
`Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) => highlight(l)).join(", ") : chalk8.dim("(none)")}`,
|
|
3062
|
+
`Target branches: ${config.targetBranches.map((b) => highlight(b)).join(", ")}`,
|
|
3063
|
+
...config.primaryBranch ? [`Primary branch: ${highlight(config.primaryBranch)}`] : [],
|
|
3063
3064
|
`Sync policy:`,
|
|
3064
|
-
` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) =>
|
|
3065
|
-
` Blocking mode: ${
|
|
3066
|
-
` Non-blocking mode: ${
|
|
3067
|
-
` Max wait: ${
|
|
3065
|
+
` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) => highlight(b)).join(", ")}`,
|
|
3066
|
+
` Blocking mode: ${highlight(config.syncPolicy.blockingMode)}`,
|
|
3067
|
+
` Non-blocking mode: ${highlight(config.syncPolicy.nonBlockingMode)}`,
|
|
3068
|
+
` Max wait: ${highlight(String(config.syncPolicy.defaultMaxWaitMs))} ms`
|
|
3068
3069
|
];
|
|
3069
|
-
p10.note(lines.join("\n"), `${config.projectName} \u2014
|
|
3070
|
+
p10.note(lines.join("\n"), `${config.projectName} \u2014 app config`);
|
|
3070
3071
|
return 0;
|
|
3071
3072
|
} catch (error) {
|
|
3072
3073
|
p10.log.error(
|
|
@@ -3080,7 +3081,6 @@ async function projectConfig(options = {}) {
|
|
|
3080
3081
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
3081
3082
|
import { join as join4 } from "path";
|
|
3082
3083
|
import * as p11 from "@clack/prompts";
|
|
3083
|
-
import chalk11 from "chalk";
|
|
3084
3084
|
import { config as loadEnv5 } from "dotenv";
|
|
3085
3085
|
loadEnv5();
|
|
3086
3086
|
async function getTranslations(options = {}) {
|
|
@@ -3103,17 +3103,17 @@ async function getTranslations(options = {}) {
|
|
|
3103
3103
|
return 1;
|
|
3104
3104
|
}
|
|
3105
3105
|
const spinner7 = p11.spinner();
|
|
3106
|
-
spinner7.start(`Fetching translations for ${
|
|
3106
|
+
spinner7.start(`Fetching translations for ${highlight(branch)}\u2026`);
|
|
3107
3107
|
try {
|
|
3108
|
-
const
|
|
3109
|
-
const targetLocales = options.locale ? [options.locale] :
|
|
3108
|
+
const projectConfig = await api.getAppConfig();
|
|
3109
|
+
const targetLocales = options.locale ? [options.locale] : projectConfig.targetLocales;
|
|
3110
3110
|
if (targetLocales.length === 0) {
|
|
3111
3111
|
spinner7.stop("No target locales configured.");
|
|
3112
3112
|
p11.log.info("Add target locales with `vocoder locales add <code>`.");
|
|
3113
3113
|
return 1;
|
|
3114
3114
|
}
|
|
3115
3115
|
const snapshot = await api.getTranslationSnapshot({ branch, targetLocales });
|
|
3116
|
-
spinner7.stop(`Fetched translations for ${
|
|
3116
|
+
spinner7.stop(`Fetched translations for ${highlight(branch)}`);
|
|
3117
3117
|
if (snapshot.status === "NOT_FOUND") {
|
|
3118
3118
|
p11.log.warn(
|
|
3119
3119
|
`No translation snapshot found for branch "${branch}". Run \`vocoder sync\` to generate one.`
|
|
@@ -3141,16 +3141,16 @@ function writeLocaleFiles(translations, outputDir) {
|
|
|
3141
3141
|
for (const [locale, strings] of Object.entries(translations)) {
|
|
3142
3142
|
const filePath = join4(outputDir, `${locale}.json`);
|
|
3143
3143
|
writeFileSync4(filePath, JSON.stringify(strings, null, 2) + "\n", "utf-8");
|
|
3144
|
-
p11.log.success(`Wrote ${
|
|
3144
|
+
p11.log.success(`Wrote ${highlight(filePath)}`);
|
|
3145
3145
|
}
|
|
3146
3146
|
}
|
|
3147
3147
|
|
|
3148
|
-
// src/commands/create-
|
|
3148
|
+
// src/commands/create-app.ts
|
|
3149
3149
|
import * as p12 from "@clack/prompts";
|
|
3150
|
-
import
|
|
3150
|
+
import chalk9 from "chalk";
|
|
3151
3151
|
import { config as loadEnv6 } from "dotenv";
|
|
3152
3152
|
loadEnv6();
|
|
3153
|
-
async function
|
|
3153
|
+
async function createApp(options) {
|
|
3154
3154
|
const authData = readAuthData();
|
|
3155
3155
|
if (!authData) {
|
|
3156
3156
|
p12.log.error(
|
|
@@ -3180,7 +3180,7 @@ async function createProject(options) {
|
|
|
3180
3180
|
const targetLocales = options.targetLocales ? options.targetLocales.split(",").map((l) => l.trim()).filter(Boolean) : [];
|
|
3181
3181
|
const targetBranches = options.targetBranches ? options.targetBranches.split(",").map((b) => b.trim()).filter(Boolean) : ["main"];
|
|
3182
3182
|
const spinner7 = p12.spinner();
|
|
3183
|
-
spinner7.start(`Creating
|
|
3183
|
+
spinner7.start(`Creating app "${options.name}"\u2026`);
|
|
3184
3184
|
try {
|
|
3185
3185
|
const result = await api.createProject(authData.token, {
|
|
3186
3186
|
organizationId: options.workspace,
|
|
@@ -3191,16 +3191,16 @@ async function createProject(options) {
|
|
|
3191
3191
|
appDirs: [appDir],
|
|
3192
3192
|
...repoCanonical ? { repoCanonical } : {}
|
|
3193
3193
|
});
|
|
3194
|
-
spinner7.stop(`Created
|
|
3194
|
+
spinner7.stop(`Created app ${chalk9.bold(result.projectName)}`);
|
|
3195
3195
|
const lines = [
|
|
3196
3196
|
`Project ID: ${result.projectId}`,
|
|
3197
|
-
`Source locale: ${
|
|
3198
|
-
`Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) =>
|
|
3199
|
-
`Branches: ${result.targetBranches.map((b) =>
|
|
3200
|
-
...repoCanonical ? [`Repository: ${
|
|
3197
|
+
`Source locale: ${highlight(result.sourceLocale)}`,
|
|
3198
|
+
`Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) => highlight(l)).join(", ") : chalk9.dim("(none)")}`,
|
|
3199
|
+
`Branches: ${result.targetBranches.map((b) => highlight(b)).join(", ")}`,
|
|
3200
|
+
...repoCanonical ? [`Repository: ${highlight(repoCanonical)}${appDir !== "." ? ` (${appDir})` : ""}`] : [],
|
|
3201
3201
|
"",
|
|
3202
3202
|
`Add this to your .env file:`,
|
|
3203
|
-
` ${
|
|
3203
|
+
` ${chalk9.bold("VOCODER_API_KEY")}=${highlight(result.apiKey)}`
|
|
3204
3204
|
];
|
|
3205
3205
|
p12.note(lines.join("\n"), "Project created");
|
|
3206
3206
|
if (!result.repositoryBound && repoCanonical) {
|
|
@@ -3228,7 +3228,7 @@ async function createProject(options) {
|
|
|
3228
3228
|
|
|
3229
3229
|
// src/commands/whoami.ts
|
|
3230
3230
|
import * as p13 from "@clack/prompts";
|
|
3231
|
-
import
|
|
3231
|
+
import chalk10 from "chalk";
|
|
3232
3232
|
async function whoami(options = {}) {
|
|
3233
3233
|
const stored = readAuthData();
|
|
3234
3234
|
if (!stored) {
|
|
@@ -3238,10 +3238,10 @@ async function whoami(options = {}) {
|
|
|
3238
3238
|
const apiUrl = options.apiUrl ?? stored.apiUrl ?? "https://vocoder.app";
|
|
3239
3239
|
const api = new VocoderAPI({ apiUrl, apiKey: "" });
|
|
3240
3240
|
try {
|
|
3241
|
-
const
|
|
3242
|
-
p13.log.info(`Logged in as ${
|
|
3243
|
-
if (
|
|
3244
|
-
p13.log.info(`Name: ${
|
|
3241
|
+
const info2 = await api.getCliUserInfo(stored.token);
|
|
3242
|
+
p13.log.info(`Logged in as ${chalk10.bold(info2.email)}`);
|
|
3243
|
+
if (info2.name) {
|
|
3244
|
+
p13.log.info(`Name: ${info2.name}`);
|
|
3245
3245
|
}
|
|
3246
3246
|
p13.log.info(`API: ${apiUrl}`);
|
|
3247
3247
|
return 0;
|
|
@@ -3266,7 +3266,7 @@ program.name("vocoder").description("Vocoder CLI - Project setup and string extr
|
|
|
3266
3266
|
program.command("init").description("Authenticate and provision Vocoder for this project").option("--api-url <url>", "Override Vocoder API URL").option("--yes", "Allow overwriting existing local config values").option(
|
|
3267
3267
|
"--ci",
|
|
3268
3268
|
"Non-interactive mode: print auth URL to stdout, skip browser open"
|
|
3269
|
-
).option("--
|
|
3269
|
+
).option("--app-name <name>", "Starter app name to create").option("--source-locale <locale>", "Source locale for the starter project").option(
|
|
3270
3270
|
"--target-locales <list>",
|
|
3271
3271
|
"Comma-separated target locales (e.g. es,fr,de)"
|
|
3272
3272
|
).action((options) => runCommand(init, options));
|
|
@@ -3286,9 +3286,9 @@ localesCmd.command("remove <codes...>").description("Remove one or more target l
|
|
|
3286
3286
|
(codes, options) => runCommand((opts) => removeLocales(codes, opts), options)
|
|
3287
3287
|
);
|
|
3288
3288
|
localesCmd.command("supported").description("List all locales supported by Vocoder").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(listSupportedLocales, options));
|
|
3289
|
-
program.command("project").description("Show current
|
|
3289
|
+
program.command("project").description("Show current app configuration").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(appConfig, options));
|
|
3290
3290
|
program.command("translations").description("Download the current translation snapshot").option("--branch <branch>", "Git branch (auto-detected if omitted)").option("--locale <locale>", "Fetch a specific locale only").option("--output <dir>", "Write locale JSON files to this directory").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(getTranslations, options));
|
|
3291
|
-
program.command("create-
|
|
3291
|
+
program.command("create-app").description("Create a new Vocoder app (requires prior `vocoder init`)").requiredOption("--name <name>", "App display name").requiredOption("--source-locale <code>", "Source language BCP 47 code (e.g. en)").requiredOption("--workspace <org-id>", "Workspace organization ID").option(
|
|
3292
3292
|
"--target-locales <codes>",
|
|
3293
3293
|
"Comma-separated target locale codes (e.g. fr,de,pt-BR)"
|
|
3294
3294
|
).option(
|
|
@@ -3309,7 +3309,7 @@ program.command("create-project").description("Create a new Vocoder project (req
|
|
|
3309
3309
|
targetBranches: options.targetBranches,
|
|
3310
3310
|
workspace: options.workspace
|
|
3311
3311
|
};
|
|
3312
|
-
return runCommand(
|
|
3312
|
+
return runCommand(createApp, translated);
|
|
3313
3313
|
});
|
|
3314
3314
|
program.parse(process.argv);
|
|
3315
3315
|
//# sourceMappingURL=bin.mjs.map
|