@vocoder/cli 0.12.2 → 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";
|
|
@@ -54,14 +54,6 @@ function writeVocoderConfig(options) {
|
|
|
54
54
|
export default defineConfig({
|
|
55
55
|
targetBranches: [${branchesStr}],
|
|
56
56
|
include: [${includesStr}],
|
|
57
|
-
exclude: [
|
|
58
|
-
'**/node_modules/**',
|
|
59
|
-
'**/.next/**',
|
|
60
|
-
'**/dist/**',
|
|
61
|
-
'**/build/**',
|
|
62
|
-
'**/*.test.*',
|
|
63
|
-
'**/*.spec.*',
|
|
64
|
-
],
|
|
65
57
|
})
|
|
66
58
|
`;
|
|
67
59
|
try {
|
|
@@ -354,25 +346,35 @@ async function selectGitHubInstallation(installations, canInstallNew) {
|
|
|
354
346
|
|
|
355
347
|
// src/utils/project-create.ts
|
|
356
348
|
import * as p3 from "@clack/prompts";
|
|
357
|
-
import
|
|
349
|
+
import chalk3 from "chalk";
|
|
358
350
|
|
|
359
351
|
// src/utils/branch-select.ts
|
|
360
352
|
import { execSync } from "child_process";
|
|
361
353
|
import { isCancel as isCancel2, Prompt } from "@clack/core";
|
|
354
|
+
|
|
355
|
+
// src/utils/theme.ts
|
|
362
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
|
|
363
372
|
var S_BAR = "\u2502";
|
|
364
373
|
var S_BAR_END = "\u2514";
|
|
365
374
|
var S_ACTIVE = "\u25C6";
|
|
366
375
|
var S_SUBMIT = "\u25C6";
|
|
367
376
|
var S_CANCEL = "\u25A0";
|
|
368
377
|
var S_ERROR = "\u25B2";
|
|
369
|
-
var noColor = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
|
|
370
|
-
var dim = (s) => noColor ? s : chalk2.gray(s);
|
|
371
|
-
var cyan = (s) => noColor ? s : chalk2.cyan(s);
|
|
372
|
-
var grn = (s) => noColor ? s : chalk2.green(s);
|
|
373
|
-
var ylw = (s) => noColor ? s : chalk2.yellow(s);
|
|
374
|
-
var red = (s) => noColor ? s : chalk2.red(s);
|
|
375
|
-
var bld = (s) => noColor ? s : chalk2.bold(s);
|
|
376
378
|
function symbol(state) {
|
|
377
379
|
switch (state) {
|
|
378
380
|
case "submit":
|
|
@@ -382,7 +384,7 @@ function symbol(state) {
|
|
|
382
384
|
case "error":
|
|
383
385
|
return ylw(S_ERROR);
|
|
384
386
|
default:
|
|
385
|
-
return
|
|
387
|
+
return active(S_ACTIVE);
|
|
386
388
|
}
|
|
387
389
|
}
|
|
388
390
|
function detectGitBranches(cwd) {
|
|
@@ -455,19 +457,19 @@ function buildList(filtered, cursor, scrollOffset, selected, filter, customPatte
|
|
|
455
457
|
const item = filtered[i];
|
|
456
458
|
const isCursor = i === cursor && !addCursor;
|
|
457
459
|
const isChecked = selected.has(item.value);
|
|
458
|
-
const icon = isChecked ? isCursor ?
|
|
460
|
+
const icon = isChecked ? isCursor ? info("\u25FC") : info("\u25FC") : isCursor ? active("\u25FB") : dim("\u25FB");
|
|
459
461
|
let label = item.isCustom ? `${item.label} ${dim("(custom)")}` : item.label;
|
|
460
462
|
if (isCursor) label = bld(label);
|
|
461
|
-
lines.push(`${
|
|
463
|
+
lines.push(`${info(S_BAR)} ${icon} ${label}`);
|
|
462
464
|
}
|
|
463
465
|
const trimmed = filter.trim();
|
|
464
466
|
const allItems = [...filtered];
|
|
465
467
|
const isNewPattern = trimmed.length > 0 && !allItems.some((i) => i.value === trimmed) && !customPatterns.includes(trimmed);
|
|
466
468
|
if (isNewPattern) {
|
|
467
469
|
const err = validateBranchPattern(trimmed) ?? (excludedPatterns.has(trimmed) ? "Already used for automatic translation" : null);
|
|
468
|
-
const icon = addCursor ?
|
|
470
|
+
const icon = addCursor ? active("\u25FB") : dim("\u25FB");
|
|
469
471
|
const label = err ? `${ylw("+")} ${dim(`"${trimmed}" \u2014 ${err}`)}` : `${grn("+")} Add "${trimmed}" as branch pattern`;
|
|
470
|
-
lines.push(`${
|
|
472
|
+
lines.push(`${info(S_BAR)} ${icon} ${label}`);
|
|
471
473
|
} else if (filtered.length === 0 && trimmed.length === 0) {
|
|
472
474
|
lines.push(dim(`${S_BAR} No branches detected`));
|
|
473
475
|
}
|
|
@@ -550,7 +552,7 @@ ${symbol(this.state)} ${message}
|
|
|
550
552
|
default:
|
|
551
553
|
return [
|
|
552
554
|
hdr.trimEnd(),
|
|
553
|
-
`${
|
|
555
|
+
`${info(S_BAR)} ${dim("/")} ${hint}`,
|
|
554
556
|
buildList(
|
|
555
557
|
filtered,
|
|
556
558
|
cursor,
|
|
@@ -562,7 +564,7 @@ ${symbol(this.state)} ${message}
|
|
|
562
564
|
optional,
|
|
563
565
|
excludedSet
|
|
564
566
|
),
|
|
565
|
-
`${
|
|
567
|
+
`${info(S_BAR_END)}`,
|
|
566
568
|
""
|
|
567
569
|
].join("\n");
|
|
568
570
|
}
|
|
@@ -635,30 +637,22 @@ ${symbol(this.state)} ${message}
|
|
|
635
637
|
// src/utils/locale-search.ts
|
|
636
638
|
import { isCancel as isCancel3, Prompt as Prompt2 } from "@clack/core";
|
|
637
639
|
import * as p2 from "@clack/prompts";
|
|
638
|
-
import chalk3 from "chalk";
|
|
639
640
|
var S_BAR2 = "\u2502";
|
|
640
641
|
var S_BAR_END2 = "\u2514";
|
|
641
642
|
var S_ACTIVE2 = "\u25C6";
|
|
642
643
|
var S_SUBMIT2 = "\u25C6";
|
|
643
644
|
var S_CANCEL2 = "\u25A0";
|
|
644
645
|
var S_ERROR2 = "\u25B2";
|
|
645
|
-
var noColor2 = process.env.NO_COLOR === "1" || process.env.FORCE_COLOR === "0";
|
|
646
|
-
var dim2 = (s) => noColor2 ? s : chalk3.gray(s);
|
|
647
|
-
var cyan2 = (s) => noColor2 ? s : chalk3.cyan(s);
|
|
648
|
-
var grn2 = (s) => noColor2 ? s : chalk3.green(s);
|
|
649
|
-
var ylw2 = (s) => noColor2 ? s : chalk3.yellow(s);
|
|
650
|
-
var red2 = (s) => noColor2 ? s : chalk3.red(s);
|
|
651
|
-
var bld2 = (s) => noColor2 ? s : chalk3.bold(s);
|
|
652
646
|
function symbol2(state) {
|
|
653
647
|
switch (state) {
|
|
654
648
|
case "submit":
|
|
655
|
-
return
|
|
649
|
+
return grn(S_SUBMIT2);
|
|
656
650
|
case "cancel":
|
|
657
|
-
return
|
|
651
|
+
return red(S_CANCEL2);
|
|
658
652
|
case "error":
|
|
659
|
-
return
|
|
653
|
+
return ylw(S_ERROR2);
|
|
660
654
|
default:
|
|
661
|
-
return
|
|
655
|
+
return active(S_ACTIVE2);
|
|
662
656
|
}
|
|
663
657
|
}
|
|
664
658
|
var MAX_VISIBLE2 = 12;
|
|
@@ -677,18 +671,18 @@ function buildList2(filtered, cursor, scrollOffset, selected) {
|
|
|
677
671
|
const opt = filtered[i];
|
|
678
672
|
const isCursor = i === cursor;
|
|
679
673
|
const isChecked = isMulti && selected.has(opt.bcp47);
|
|
680
|
-
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");
|
|
681
675
|
visibleLines.push(
|
|
682
|
-
`${
|
|
676
|
+
`${info(S_BAR2)} ${icon} ${isCursor ? bld(opt.label) : opt.label}`
|
|
683
677
|
);
|
|
684
678
|
}
|
|
685
679
|
const hidden = filtered.length - (end - scrollOffset);
|
|
686
680
|
if (hidden > 0)
|
|
687
|
-
visibleLines.push(
|
|
688
|
-
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`));
|
|
689
683
|
if (isMulti && selected.size > 0) {
|
|
690
684
|
visibleLines.push(
|
|
691
|
-
|
|
685
|
+
dim(`${S_BAR2} ${selected.size} selected \u2014 Enter to confirm`)
|
|
692
686
|
);
|
|
693
687
|
}
|
|
694
688
|
return visibleLines.join("\n");
|
|
@@ -724,43 +718,43 @@ async function runFilterablePrompt(opts) {
|
|
|
724
718
|
render() {
|
|
725
719
|
const filtered = getFiltered();
|
|
726
720
|
clampCursor(filtered);
|
|
727
|
-
const hdr = `${
|
|
721
|
+
const hdr = `${dim(S_BAR2)}
|
|
728
722
|
${symbol2(this.state)} ${message}
|
|
729
723
|
`;
|
|
730
|
-
const hint = filter.length > 0 ? filter :
|
|
724
|
+
const hint = filter.length > 0 ? filter : dim(
|
|
731
725
|
`type to filter, \u2191\u2193 navigate${multi ? ", space select" : ""}`
|
|
732
726
|
);
|
|
733
727
|
switch (this.state) {
|
|
734
728
|
case "submit": {
|
|
735
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 ?? "";
|
|
736
|
-
return `${hdr}${
|
|
730
|
+
return `${hdr}${dim(S_BAR2)} ${bld(val || dim("none"))}`;
|
|
737
731
|
}
|
|
738
732
|
case "cancel":
|
|
739
|
-
return `${hdr}${
|
|
733
|
+
return `${hdr}${dim(S_BAR2)}`;
|
|
740
734
|
case "error":
|
|
741
735
|
return [
|
|
742
736
|
hdr.trimEnd(),
|
|
743
|
-
`${
|
|
737
|
+
`${ylw(S_BAR2)} ${dim("/")} ${hint}`,
|
|
744
738
|
buildList2(
|
|
745
739
|
filtered,
|
|
746
740
|
cursor,
|
|
747
741
|
scrollOffset,
|
|
748
742
|
multi ? selected : null
|
|
749
743
|
),
|
|
750
|
-
`${
|
|
744
|
+
`${ylw(S_BAR_END2)} ${ylw(this.error)}`,
|
|
751
745
|
""
|
|
752
746
|
].join("\n");
|
|
753
747
|
default:
|
|
754
748
|
return [
|
|
755
749
|
hdr.trimEnd(),
|
|
756
|
-
`${
|
|
750
|
+
`${info(S_BAR2)} ${dim("/")} ${hint}`,
|
|
757
751
|
buildList2(
|
|
758
752
|
filtered,
|
|
759
753
|
cursor,
|
|
760
754
|
scrollOffset,
|
|
761
755
|
multi ? selected : null
|
|
762
756
|
),
|
|
763
|
-
`${
|
|
757
|
+
`${info(S_BAR_END2)}`,
|
|
764
758
|
""
|
|
765
759
|
].join("\n");
|
|
766
760
|
}
|
|
@@ -869,7 +863,7 @@ function buildLanguageOptions(locales) {
|
|
|
869
863
|
async function runProjectCreate(params) {
|
|
870
864
|
const { api, userToken, organizationId, repoCanonical } = params;
|
|
871
865
|
const projectName = (params.defaultName ?? "my-project").trim();
|
|
872
|
-
p3.log.success(`Project: ${
|
|
866
|
+
p3.log.success(`Project: ${chalk3.bold(projectName)}`);
|
|
873
867
|
let sourceLocales;
|
|
874
868
|
try {
|
|
875
869
|
({ sourceLocales } = await api.listLocales(userToken));
|
|
@@ -882,7 +876,7 @@ async function runProjectCreate(params) {
|
|
|
882
876
|
const languageOptions = buildLanguageOptions(sourceLocales);
|
|
883
877
|
const appDir = params.defaultAppDir ?? "";
|
|
884
878
|
if (appDir) {
|
|
885
|
-
p3.log.success(`App directory: ${
|
|
879
|
+
p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
|
|
886
880
|
}
|
|
887
881
|
const sourceLocale = await searchSelectLocale(
|
|
888
882
|
languageOptions,
|
|
@@ -947,7 +941,7 @@ async function runProjectCreate(params) {
|
|
|
947
941
|
appDirs: appDir ? [appDir] : [],
|
|
948
942
|
repoCanonical
|
|
949
943
|
});
|
|
950
|
-
p3.log.success(`Project ${
|
|
944
|
+
p3.log.success(`Project ${chalk3.bold(result.projectName)} created!`);
|
|
951
945
|
return result;
|
|
952
946
|
} catch (error) {
|
|
953
947
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -974,7 +968,7 @@ async function runAppCreate(params) {
|
|
|
974
968
|
return null;
|
|
975
969
|
}
|
|
976
970
|
if (appDir) {
|
|
977
|
-
p3.log.success(`App directory: ${
|
|
971
|
+
p3.log.success(`App directory: ${chalk3.bold(appDir)}`);
|
|
978
972
|
}
|
|
979
973
|
const sourceLocale = await searchSelectLocale(
|
|
980
974
|
languageOptions,
|
|
@@ -1026,7 +1020,7 @@ async function runAppCreate(params) {
|
|
|
1026
1020
|
}
|
|
1027
1021
|
const targetBranches = appPushBranches;
|
|
1028
1022
|
try {
|
|
1029
|
-
const result = await api.
|
|
1023
|
+
const result = await api.createProject(userToken, {
|
|
1030
1024
|
projectId,
|
|
1031
1025
|
appDir,
|
|
1032
1026
|
sourceLocale,
|
|
@@ -1035,7 +1029,7 @@ async function runAppCreate(params) {
|
|
|
1035
1029
|
repoCanonical: repoCanonical ?? ""
|
|
1036
1030
|
});
|
|
1037
1031
|
p3.log.success(
|
|
1038
|
-
`App ${
|
|
1032
|
+
`App ${chalk3.bold(appDir)} added to ${chalk3.bold(projectName)}!`
|
|
1039
1033
|
);
|
|
1040
1034
|
return {
|
|
1041
1035
|
projectId: result.projectId,
|
|
@@ -1054,7 +1048,7 @@ async function runAppCreate(params) {
|
|
|
1054
1048
|
}
|
|
1055
1049
|
|
|
1056
1050
|
// src/commands/init.ts
|
|
1057
|
-
import
|
|
1051
|
+
import chalk5 from "chalk";
|
|
1058
1052
|
import { join as join2 } from "path";
|
|
1059
1053
|
import { config as loadEnv } from "dotenv";
|
|
1060
1054
|
|
|
@@ -1168,7 +1162,7 @@ function resolveGitContext() {
|
|
|
1168
1162
|
|
|
1169
1163
|
// src/utils/workspace.ts
|
|
1170
1164
|
import * as p4 from "@clack/prompts";
|
|
1171
|
-
import
|
|
1165
|
+
import chalk4 from "chalk";
|
|
1172
1166
|
async function selectWorkspace(result) {
|
|
1173
1167
|
const { workspaces, canCreateWorkspace } = result;
|
|
1174
1168
|
if (workspaces.length === 0) {
|
|
@@ -1272,7 +1266,7 @@ function runScaffold(params) {
|
|
|
1272
1266
|
if (detection.ecosystem) {
|
|
1273
1267
|
const frameworkLabel = detection.framework ?? detection.ecosystem;
|
|
1274
1268
|
const pmLabel = detection.packageManager;
|
|
1275
|
-
p5.log.info(`Detected: ${
|
|
1269
|
+
p5.log.info(`Detected: ${chalk5.bold(frameworkLabel)} (${pmLabel})`);
|
|
1276
1270
|
}
|
|
1277
1271
|
const { devPackages, runtimePackages } = getPackagesToInstall(detection);
|
|
1278
1272
|
const allPackages = [...devPackages, ...runtimePackages];
|
|
@@ -1300,10 +1294,10 @@ function runScaffold(params) {
|
|
|
1300
1294
|
devPackages.length > 0 ? buildInstallCommand(detection.packageManager, devPackages, true) : null,
|
|
1301
1295
|
runtimePackages.length > 0 ? buildInstallCommand(detection.packageManager, runtimePackages, false) : null
|
|
1302
1296
|
].filter(Boolean).join(" && ");
|
|
1303
|
-
p5.log.warn(`Run manually: ${
|
|
1297
|
+
p5.log.warn(`Run manually: ${highlight(cmds)}`);
|
|
1304
1298
|
}
|
|
1305
1299
|
} else if (detection.ecosystem) {
|
|
1306
|
-
p5.log.info(`Packages: ${
|
|
1300
|
+
p5.log.info(`Packages: ${chalk5.green("already installed")}`);
|
|
1307
1301
|
}
|
|
1308
1302
|
const snippets = getSetupSnippets({
|
|
1309
1303
|
framework: detection.framework,
|
|
@@ -1333,19 +1327,19 @@ function runScaffold(params) {
|
|
|
1333
1327
|
code: snippets.wrapStep.code
|
|
1334
1328
|
});
|
|
1335
1329
|
p5.log.message("");
|
|
1336
|
-
p5.log.message(
|
|
1330
|
+
p5.log.message(chalk5.bold("Finish setup in your code"));
|
|
1337
1331
|
p5.log.message("");
|
|
1338
1332
|
for (let i = 0; i < steps.length; i++) {
|
|
1339
1333
|
const step = steps[i];
|
|
1340
1334
|
p5.log.step(
|
|
1341
|
-
`${
|
|
1335
|
+
`${chalk5.bold(step.label)} ${chalk5.dim(`\u2014 ${step.hint}`)}`
|
|
1342
1336
|
);
|
|
1343
1337
|
printCodeBlock(step.code);
|
|
1344
1338
|
if (i < steps.length - 1) p5.log.message("");
|
|
1345
1339
|
}
|
|
1346
1340
|
const written = writeVocoderConfig({ targetBranches, useTypeScript });
|
|
1347
1341
|
if (written) {
|
|
1348
|
-
p5.log.success(`Created ${
|
|
1342
|
+
p5.log.success(`Created ${highlight(written)}`);
|
|
1349
1343
|
} else if (!findExistingConfig(process.cwd())) {
|
|
1350
1344
|
const ext = useTypeScript ? "ts" : "js";
|
|
1351
1345
|
p5.log.warn(
|
|
@@ -1353,11 +1347,11 @@ function runScaffold(params) {
|
|
|
1353
1347
|
);
|
|
1354
1348
|
}
|
|
1355
1349
|
p5.log.message("");
|
|
1356
|
-
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");
|
|
1357
1351
|
p5.log.success(
|
|
1358
1352
|
`Push to ${branchList} to trigger your first translation run.`
|
|
1359
1353
|
);
|
|
1360
|
-
p5.log.message(
|
|
1354
|
+
p5.log.message(info(" Docs: https://vocoder.app/docs/getting-started"));
|
|
1361
1355
|
}
|
|
1362
1356
|
function writeApiKeyToEnv(apiKey) {
|
|
1363
1357
|
const envPath = join2(process.cwd(), ".env");
|
|
@@ -1382,12 +1376,12 @@ function writeApiKeyToEnv(apiKey) {
|
|
|
1382
1376
|
function printApiKey(apiKey) {
|
|
1383
1377
|
const saved = writeApiKeyToEnv(apiKey);
|
|
1384
1378
|
p5.log.message("");
|
|
1385
|
-
p5.log.message(
|
|
1379
|
+
p5.log.message(chalk5.bold("Your API Key"));
|
|
1386
1380
|
printCodeBlock(`VOCODER_API_KEY=${apiKey}`);
|
|
1387
1381
|
if (saved) {
|
|
1388
|
-
p5.log.success(
|
|
1382
|
+
p5.log.success(chalk5.dim("Saved to .env"));
|
|
1389
1383
|
} else {
|
|
1390
|
-
p5.log.message(
|
|
1384
|
+
p5.log.message(chalk5.dim(" Add the above to your .env file"));
|
|
1391
1385
|
}
|
|
1392
1386
|
}
|
|
1393
1387
|
function printCodeBlock(code) {
|
|
@@ -1396,20 +1390,20 @@ function printCodeBlock(code) {
|
|
|
1396
1390
|
(max, line) => Math.max(max, line.length),
|
|
1397
1391
|
0
|
|
1398
1392
|
);
|
|
1399
|
-
const bar =
|
|
1393
|
+
const bar = chalk5.gray("\u2502");
|
|
1400
1394
|
const pad = (s) => s + " ".repeat(maxLen - s.length);
|
|
1401
|
-
process.stdout.write(`${
|
|
1395
|
+
process.stdout.write(`${chalk5.gray("\u2502")}
|
|
1402
1396
|
`);
|
|
1403
1397
|
process.stdout.write(
|
|
1404
|
-
`${
|
|
1398
|
+
`${chalk5.gray("\u2502")} ${chalk5.gray(`\u250C${"\u2500".repeat(maxLen + 2)}\u2510`)}
|
|
1405
1399
|
`
|
|
1406
1400
|
);
|
|
1407
1401
|
for (const line of lines) {
|
|
1408
|
-
process.stdout.write(`${
|
|
1402
|
+
process.stdout.write(`${chalk5.gray("\u2502")} ${bar} ${pad(line)} ${bar}
|
|
1409
1403
|
`);
|
|
1410
1404
|
}
|
|
1411
1405
|
process.stdout.write(
|
|
1412
|
-
`${
|
|
1406
|
+
`${chalk5.gray("\u2502")} ${chalk5.gray(`\u2514${"\u2500".repeat(maxLen + 2)}\u2518`)}
|
|
1413
1407
|
`
|
|
1414
1408
|
);
|
|
1415
1409
|
}
|
|
@@ -1548,7 +1542,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
|
|
|
1548
1542
|
return null;
|
|
1549
1543
|
}
|
|
1550
1544
|
const userInfo = await api.getCliUserInfo(rawToken);
|
|
1551
|
-
authSpinner.stop(`Authenticated as ${
|
|
1545
|
+
authSpinner.stop(`Authenticated as ${chalk5.bold(userInfo.email)}`);
|
|
1552
1546
|
return {
|
|
1553
1547
|
token: rawToken,
|
|
1554
1548
|
...userInfo,
|
|
@@ -1558,7 +1552,7 @@ async function runAuthFlow(api, options, reauth = false, repoCanonical) {
|
|
|
1558
1552
|
}
|
|
1559
1553
|
async function init(options = {}) {
|
|
1560
1554
|
const apiUrl = options.apiUrl || process.env.VOCODER_API_URL || "https://vocoder.app";
|
|
1561
|
-
p5.intro(
|
|
1555
|
+
p5.intro(active(chalk5.bold("Vocoder Setup")));
|
|
1562
1556
|
try {
|
|
1563
1557
|
const gitContext = resolveGitContext();
|
|
1564
1558
|
const identity = gitContext.identity;
|
|
@@ -1572,15 +1566,15 @@ async function init(options = {}) {
|
|
|
1572
1566
|
let repoProjectName = null;
|
|
1573
1567
|
if (identity) {
|
|
1574
1568
|
const anonApi = new VocoderAPI({ apiUrl, apiKey: "" });
|
|
1575
|
-
const lookup = await anonApi.
|
|
1569
|
+
const lookup = await anonApi.lookupAppByRepo({
|
|
1576
1570
|
repoCanonical: identity.repoCanonical,
|
|
1577
1571
|
appDir: identity.repoAppDir
|
|
1578
1572
|
});
|
|
1579
1573
|
if (lookup.exactMatch) {
|
|
1580
1574
|
const { exactMatch } = lookup;
|
|
1581
|
-
p5.log.success(`Project: ${
|
|
1575
|
+
p5.log.success(`Project: ${chalk5.bold(exactMatch.projectName)}`);
|
|
1582
1576
|
p5.log.info(
|
|
1583
|
-
`Branches: ${
|
|
1577
|
+
`Branches: ${highlight((exactMatch.targetBranches ?? ["main"]).join(", "))}`
|
|
1584
1578
|
);
|
|
1585
1579
|
const needsKey = await p5.confirm({
|
|
1586
1580
|
message: "Need to regenerate your API key?"
|
|
@@ -1613,18 +1607,18 @@ async function init(options = {}) {
|
|
|
1613
1607
|
}
|
|
1614
1608
|
const isTs = detectLocalEcosystem().isTypeScript;
|
|
1615
1609
|
const written = writeVocoderConfig({ targetBranches: exactMatch.targetBranches ?? ["main"], useTypeScript: isTs });
|
|
1616
|
-
if (written) p5.log.success(`Created ${
|
|
1617
|
-
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."));
|
|
1618
1612
|
return 0;
|
|
1619
1613
|
}
|
|
1620
1614
|
if (lookup.hasWholeRepoApp) {
|
|
1621
1615
|
const wholeRepo = lookup.existingApps.find((a) => a.appDir === "");
|
|
1622
1616
|
if (wholeRepo) {
|
|
1623
|
-
p5.log.success(`Project: ${
|
|
1617
|
+
p5.log.success(`Project: ${chalk5.bold(wholeRepo.projectName)}`);
|
|
1624
1618
|
const isTs = detectLocalEcosystem().isTypeScript;
|
|
1625
1619
|
const written = writeVocoderConfig({ targetBranches: ["main"], useTypeScript: isTs });
|
|
1626
|
-
if (written) p5.log.success(`Created ${
|
|
1627
|
-
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."));
|
|
1628
1622
|
return 0;
|
|
1629
1623
|
}
|
|
1630
1624
|
}
|
|
@@ -1641,7 +1635,7 @@ async function init(options = {}) {
|
|
|
1641
1635
|
let authOrganizationId;
|
|
1642
1636
|
const storedAuth = await verifyStoredAuth(api);
|
|
1643
1637
|
if (storedAuth.status === "valid") {
|
|
1644
|
-
p5.log.success(`Authenticated as ${
|
|
1638
|
+
p5.log.success(`Authenticated as ${chalk5.bold(storedAuth.email)}`);
|
|
1645
1639
|
userToken = storedAuth.token;
|
|
1646
1640
|
userEmail = storedAuth.email;
|
|
1647
1641
|
userName = storedAuth.name;
|
|
@@ -1681,7 +1675,7 @@ async function init(options = {}) {
|
|
|
1681
1675
|
selectedWorkspaceId = authOrganizationId;
|
|
1682
1676
|
selectedWorkspaceName = ws?.name ?? userEmail;
|
|
1683
1677
|
p5.log.success(
|
|
1684
|
-
`Connected as ${
|
|
1678
|
+
`Connected as ${chalk5.bold(userEmail)} \u2014 workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1685
1679
|
);
|
|
1686
1680
|
} else {
|
|
1687
1681
|
const discoveryResult = await api.getCliGitHubDiscovery(userToken).catch(() => null);
|
|
@@ -1732,7 +1726,7 @@ async function init(options = {}) {
|
|
|
1732
1726
|
});
|
|
1733
1727
|
selectedWorkspaceId = claimResult.organizationId;
|
|
1734
1728
|
selectedWorkspaceName = claimResult.organizationName;
|
|
1735
|
-
p5.log.success(`Workspace: ${
|
|
1729
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1736
1730
|
} else {
|
|
1737
1731
|
const workspaceData = await api.listWorkspaces(userToken, {
|
|
1738
1732
|
repo: identity?.repoCanonical
|
|
@@ -1746,13 +1740,13 @@ async function init(options = {}) {
|
|
|
1746
1740
|
const ws = covering[0];
|
|
1747
1741
|
selectedWorkspaceId = ws.id;
|
|
1748
1742
|
selectedWorkspaceName = ws.name;
|
|
1749
|
-
p5.log.success(`Workspace: ${
|
|
1743
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1750
1744
|
} else if (repoCanonical && covering.length > 1) {
|
|
1751
1745
|
const choice = await p5.select({
|
|
1752
1746
|
message: "Select workspace for this repo",
|
|
1753
1747
|
options: covering.map((w) => ({
|
|
1754
1748
|
value: w.id,
|
|
1755
|
-
label: `${w.name} ${
|
|
1749
|
+
label: `${w.name} ${chalk5.dim(`(${w.projectCount} project${w.projectCount !== 1 ? "s" : ""})`)}`
|
|
1756
1750
|
}))
|
|
1757
1751
|
});
|
|
1758
1752
|
if (p5.isCancel(choice)) {
|
|
@@ -1762,11 +1756,11 @@ async function init(options = {}) {
|
|
|
1762
1756
|
const ws = covering.find((w) => w.id === choice);
|
|
1763
1757
|
selectedWorkspaceId = ws.id;
|
|
1764
1758
|
selectedWorkspaceName = ws.name;
|
|
1765
|
-
p5.log.success(`Workspace: ${
|
|
1759
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1766
1760
|
} else if (repoCanonical && covering.length === 0 && connected.length > 0) {
|
|
1767
1761
|
const shortRepo = repoCanonical.split(":")[1] ?? repoCanonical;
|
|
1768
1762
|
p5.log.warn(
|
|
1769
|
-
`${
|
|
1763
|
+
`${chalk5.bold(shortRepo)} isn't accessible from your Vocoder installation.
|
|
1770
1764
|
Grant access to this repository or install on the account that owns it.`
|
|
1771
1765
|
);
|
|
1772
1766
|
const fixOptions = [];
|
|
@@ -1774,13 +1768,13 @@ async function init(options = {}) {
|
|
|
1774
1768
|
if (ws.installationConfigureUrl) {
|
|
1775
1769
|
fixOptions.push({
|
|
1776
1770
|
value: `grant:${ws.id}`,
|
|
1777
|
-
label: `Configure ${
|
|
1771
|
+
label: `Configure ${chalk5.bold(ws.connectionLabel ?? ws.name)}'s GitHub App installation`
|
|
1778
1772
|
});
|
|
1779
1773
|
}
|
|
1780
1774
|
}
|
|
1781
1775
|
fixOptions.push({
|
|
1782
1776
|
value: "install_new",
|
|
1783
|
-
label: `Install on a different GitHub account ${
|
|
1777
|
+
label: `Install on a different GitHub account ${chalk5.dim("(creates a new personal workspace)")}`
|
|
1784
1778
|
});
|
|
1785
1779
|
fixOptions.push({ value: "cancel", label: "Cancel" });
|
|
1786
1780
|
const fix = await p5.select({
|
|
@@ -1795,8 +1789,8 @@ async function init(options = {}) {
|
|
|
1795
1789
|
const ws = connected.find((w) => `grant:${w.id}` === fix);
|
|
1796
1790
|
await tryOpenBrowser2(ws.installationConfigureUrl);
|
|
1797
1791
|
p5.cancel(
|
|
1798
|
-
`Grant access to ${
|
|
1799
|
-
then re-run ${
|
|
1792
|
+
`Grant access to ${chalk5.bold(shortRepo)} in your browser,
|
|
1793
|
+
then re-run ${chalk5.bold("vocoder init")}.`
|
|
1800
1794
|
);
|
|
1801
1795
|
return 1;
|
|
1802
1796
|
}
|
|
@@ -1813,13 +1807,13 @@ async function init(options = {}) {
|
|
|
1813
1807
|
}
|
|
1814
1808
|
selectedWorkspaceId = connectResult.organizationId;
|
|
1815
1809
|
selectedWorkspaceName = connectResult.organizationName;
|
|
1816
|
-
p5.log.success(`Workspace: ${
|
|
1810
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1817
1811
|
} else {
|
|
1818
1812
|
if (workspaceData.workspaces.length === 1 && !workspaceData.canCreateWorkspace) {
|
|
1819
1813
|
const ws = workspaceData.workspaces[0];
|
|
1820
1814
|
selectedWorkspaceId = ws.id;
|
|
1821
1815
|
selectedWorkspaceName = ws.name;
|
|
1822
|
-
p5.log.success(`Workspace: ${
|
|
1816
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1823
1817
|
} else {
|
|
1824
1818
|
const workspaceResult = await selectWorkspace(workspaceData);
|
|
1825
1819
|
if (workspaceResult.action === "cancelled") {
|
|
@@ -1829,7 +1823,7 @@ async function init(options = {}) {
|
|
|
1829
1823
|
if (workspaceResult.action === "use") {
|
|
1830
1824
|
selectedWorkspaceId = workspaceResult.workspace.id;
|
|
1831
1825
|
selectedWorkspaceName = workspaceResult.workspace.name;
|
|
1832
|
-
p5.log.success(`Workspace: ${
|
|
1826
|
+
p5.log.success(`Workspace: ${chalk5.bold(selectedWorkspaceName)}`);
|
|
1833
1827
|
} else {
|
|
1834
1828
|
const connectChoice = await p5.select({
|
|
1835
1829
|
message: "Connect your new workspace to GitHub",
|
|
@@ -1857,7 +1851,7 @@ async function init(options = {}) {
|
|
|
1857
1851
|
selectedWorkspaceId = connectResult.organizationId;
|
|
1858
1852
|
selectedWorkspaceName = connectResult.organizationName;
|
|
1859
1853
|
p5.log.success(
|
|
1860
|
-
`Workspace: ${
|
|
1854
|
+
`Workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1861
1855
|
);
|
|
1862
1856
|
} else {
|
|
1863
1857
|
const installations = await runGitHubDiscoveryFlow({
|
|
@@ -1919,7 +1913,7 @@ async function init(options = {}) {
|
|
|
1919
1913
|
}
|
|
1920
1914
|
}
|
|
1921
1915
|
p5.log.success(
|
|
1922
|
-
`Workspace: ${
|
|
1916
|
+
`Workspace: ${chalk5.bold(selectedWorkspaceName)}`
|
|
1923
1917
|
);
|
|
1924
1918
|
}
|
|
1925
1919
|
}
|
|
@@ -1929,8 +1923,8 @@ async function init(options = {}) {
|
|
|
1929
1923
|
}
|
|
1930
1924
|
if (repoProjectId && repoProjectName && existingAppsForRepo.length > 0) {
|
|
1931
1925
|
p5.log.info(
|
|
1932
|
-
`${
|
|
1933
|
-
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(", ")}`
|
|
1934
1928
|
);
|
|
1935
1929
|
const appResult = await runAppCreate({
|
|
1936
1930
|
api,
|
|
@@ -1951,7 +1945,7 @@ async function init(options = {}) {
|
|
|
1951
1945
|
targetBranches: appResult.targetBranches,
|
|
1952
1946
|
appDir: identity?.repoAppDir
|
|
1953
1947
|
});
|
|
1954
|
-
p5.outro("You're all set.");
|
|
1948
|
+
p5.outro(active("You're all set."));
|
|
1955
1949
|
return 0;
|
|
1956
1950
|
}
|
|
1957
1951
|
try {
|
|
@@ -1959,14 +1953,13 @@ async function init(options = {}) {
|
|
|
1959
1953
|
const ws = wsCheck.workspaces.find((w) => w.id === selectedWorkspaceId);
|
|
1960
1954
|
if (ws && ws.maxProjects !== -1 && ws.projectCount >= ws.maxProjects) {
|
|
1961
1955
|
p5.log.warn(
|
|
1962
|
-
`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.`
|
|
1963
1957
|
);
|
|
1964
|
-
const hasRepoContext = !!identity?.repoCanonical;
|
|
1965
1958
|
const options2 = [];
|
|
1966
|
-
if (
|
|
1959
|
+
if (repoProjectId) {
|
|
1967
1960
|
options2.push({
|
|
1968
1961
|
value: "connect",
|
|
1969
|
-
label:
|
|
1962
|
+
label: `Reconnect this repo to ${chalk5.bold(repoProjectName ?? "existing project")}`
|
|
1970
1963
|
});
|
|
1971
1964
|
}
|
|
1972
1965
|
options2.push({ value: "upgrade", label: "Upgrade plan" });
|
|
@@ -1986,46 +1979,39 @@ async function init(options = {}) {
|
|
|
1986
1979
|
);
|
|
1987
1980
|
return 1;
|
|
1988
1981
|
}
|
|
1989
|
-
const existingProjects = await api.
|
|
1982
|
+
const existingProjects = await api.listApps(
|
|
1990
1983
|
userToken,
|
|
1991
1984
|
selectedWorkspaceId
|
|
1992
1985
|
);
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
message: "Which project should this repo be connected to?",
|
|
1999
|
-
options: existingProjects.map((proj) => ({
|
|
2000
|
-
value: proj.id,
|
|
2001
|
-
label: proj.name
|
|
2002
|
-
}))
|
|
2003
|
-
});
|
|
2004
|
-
if (p5.isCancel(chosenId)) {
|
|
2005
|
-
p5.cancel("Setup cancelled.");
|
|
1986
|
+
const chosenProject = existingProjects.find(
|
|
1987
|
+
(proj) => proj.id === repoProjectId
|
|
1988
|
+
);
|
|
1989
|
+
if (!chosenProject) {
|
|
1990
|
+
p5.log.error("Could not find the project. Try again.");
|
|
2006
1991
|
return 1;
|
|
2007
1992
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
1993
|
+
try {
|
|
1994
|
+
const appResult = await api.createApp(userToken, {
|
|
1995
|
+
projectId: chosenProject.id,
|
|
1996
|
+
appDir: identity?.repoAppDir ?? "",
|
|
1997
|
+
sourceLocale: chosenProject.sourceLocale,
|
|
1998
|
+
targetLocales: chosenProject.targetLocales,
|
|
1999
|
+
targetBranches: chosenProject.targetBranches,
|
|
2000
|
+
repoCanonical: identity?.repoCanonical ?? ""
|
|
2001
|
+
});
|
|
2002
|
+
p5.log.success(`Connected to project: ${chalk5.bold(chosenProject.name)}`);
|
|
2003
|
+
printApiKey(appResult.apiKey);
|
|
2004
|
+
runScaffold({
|
|
2005
|
+
sourceLocale: chosenProject.sourceLocale,
|
|
2006
|
+
targetBranches: chosenProject.targetBranches,
|
|
2007
|
+
appDir: identity?.repoAppDir
|
|
2008
|
+
});
|
|
2009
|
+
} catch (err) {
|
|
2010
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2011
|
+
p5.log.error(`Failed to create app binding: ${msg}`);
|
|
2021
2012
|
return 1;
|
|
2022
2013
|
}
|
|
2023
|
-
|
|
2024
|
-
sourceLocale: appResult.sourceLocale,
|
|
2025
|
-
targetBranches: appResult.targetBranches,
|
|
2026
|
-
appDir: identity?.repoAppDir
|
|
2027
|
-
});
|
|
2028
|
-
p5.outro("You're all set.");
|
|
2014
|
+
p5.outro(active("You're all set."));
|
|
2029
2015
|
return 0;
|
|
2030
2016
|
}
|
|
2031
2017
|
} catch {
|
|
@@ -2052,7 +2038,7 @@ Translations won't run automatically until you grant access.
|
|
|
2052
2038
|
To fix: go to your GitHub App installation settings and add this
|
|
2053
2039
|
repository to the allowed list, or switch to "All repositories".
|
|
2054
2040
|
` + (projectResult.configureUrl ? `
|
|
2055
|
-
${
|
|
2041
|
+
${chalk5.dim(projectResult.configureUrl)}
|
|
2056
2042
|
` : "")
|
|
2057
2043
|
);
|
|
2058
2044
|
}
|
|
@@ -2062,7 +2048,7 @@ Translations won't run automatically until you grant access.
|
|
|
2062
2048
|
appDir: identity?.repoAppDir
|
|
2063
2049
|
});
|
|
2064
2050
|
printApiKey(projectResult.apiKey);
|
|
2065
|
-
p5.outro("You're all set.");
|
|
2051
|
+
p5.outro(active("You're all set."));
|
|
2066
2052
|
return 0;
|
|
2067
2053
|
} catch (error) {
|
|
2068
2054
|
if (error instanceof Error) {
|
|
@@ -2080,7 +2066,7 @@ Translations won't run automatically until you grant access.
|
|
|
2080
2066
|
|
|
2081
2067
|
// src/commands/locales.ts
|
|
2082
2068
|
import * as p8 from "@clack/prompts";
|
|
2083
|
-
import
|
|
2069
|
+
import chalk7 from "chalk";
|
|
2084
2070
|
import { config as loadEnv3 } from "dotenv";
|
|
2085
2071
|
|
|
2086
2072
|
// src/commands/sync.ts
|
|
@@ -2088,7 +2074,7 @@ import { createHash, randomUUID } from "crypto";
|
|
|
2088
2074
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2089
2075
|
import { join as join3 } from "path";
|
|
2090
2076
|
import * as p7 from "@clack/prompts";
|
|
2091
|
-
import
|
|
2077
|
+
import chalk6 from "chalk";
|
|
2092
2078
|
|
|
2093
2079
|
// src/utils/branch.ts
|
|
2094
2080
|
import { execSync as execSync4 } from "child_process";
|
|
@@ -2158,7 +2144,6 @@ function matchBranchPattern(branch, pattern) {
|
|
|
2158
2144
|
|
|
2159
2145
|
// src/utils/config.ts
|
|
2160
2146
|
import * as p6 from "@clack/prompts";
|
|
2161
|
-
import chalk7 from "chalk";
|
|
2162
2147
|
import { config as loadEnv2 } from "dotenv";
|
|
2163
2148
|
loadEnv2();
|
|
2164
2149
|
function validateLocalConfig(config) {
|
|
@@ -2217,7 +2202,7 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
|
|
|
2217
2202
|
const fileConfig = loadVocoderConfig(process.cwd());
|
|
2218
2203
|
if (!fileConfig) {
|
|
2219
2204
|
p6.log.warn(
|
|
2220
|
-
`No ${
|
|
2205
|
+
`No ${highlight("vocoder.config.ts")} found \u2014 run ${highlight("npx @vocoder/cli init")} to generate one.`
|
|
2221
2206
|
);
|
|
2222
2207
|
}
|
|
2223
2208
|
const envExtractionPattern = process.env.VOCODER_INCLUDE_PATTERN;
|
|
@@ -2296,13 +2281,13 @@ async function getMergedConfig(cliOptions, verbose = false, _startDir) {
|
|
|
2296
2281
|
}
|
|
2297
2282
|
if (verbose) {
|
|
2298
2283
|
const lines = [
|
|
2299
|
-
`Include patterns: ${
|
|
2300
|
-
...excludePattern.length > 0 ? [`Exclude patterns: ${
|
|
2301
|
-
`API key: ${
|
|
2302
|
-
`API URL: ${
|
|
2303
|
-
`Sync mode: ${
|
|
2304
|
-
...maxWaitMs ? [`Max wait: ${
|
|
2305
|
-
`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))}`
|
|
2306
2291
|
];
|
|
2307
2292
|
p6.note(lines.join("\n"), "Configuration sources");
|
|
2308
2293
|
}
|
|
@@ -2573,7 +2558,7 @@ async function fetchApiSnapshot(api, params) {
|
|
|
2573
2558
|
async function sync(options = {}) {
|
|
2574
2559
|
const startTime = Date.now();
|
|
2575
2560
|
const projectRoot = process.cwd();
|
|
2576
|
-
p7.intro("Vocoder Sync");
|
|
2561
|
+
p7.intro(active("Vocoder Sync"));
|
|
2577
2562
|
const mergedConfig = await getMergedConfig(options, options.verbose);
|
|
2578
2563
|
if (!mergedConfig.apiKey) {
|
|
2579
2564
|
p7.log.warn("No API key found. Run init to get started:");
|
|
@@ -2582,7 +2567,7 @@ async function sync(options = {}) {
|
|
|
2582
2567
|
p7.log.info(
|
|
2583
2568
|
" Or add your key to .env: VOCODER_API_KEY=vcp_..."
|
|
2584
2569
|
);
|
|
2585
|
-
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."));
|
|
2586
2571
|
return 1;
|
|
2587
2572
|
}
|
|
2588
2573
|
const spinner7 = p7.spinner();
|
|
@@ -2595,7 +2580,7 @@ async function sync(options = {}) {
|
|
|
2595
2580
|
};
|
|
2596
2581
|
validateLocalConfig(localConfig);
|
|
2597
2582
|
const api = new VocoderAPI(localConfig);
|
|
2598
|
-
const apiConfig = await api.
|
|
2583
|
+
const apiConfig = await api.getAppConfig();
|
|
2599
2584
|
const requestedMode = mergedConfig.mode;
|
|
2600
2585
|
const waitTimeoutMs = resolveWaitTimeoutMs({
|
|
2601
2586
|
requestedMaxWaitMs: mergedConfig.maxWaitMs,
|
|
@@ -2612,10 +2597,10 @@ async function sync(options = {}) {
|
|
|
2612
2597
|
...fileConfig?.appIndustry ? { appIndustry: fileConfig.appIndustry } : {},
|
|
2613
2598
|
...fileConfig?.formality ? { formality: fileConfig.formality } : {}
|
|
2614
2599
|
};
|
|
2615
|
-
spinner7.stop(`Branch: ${
|
|
2600
|
+
spinner7.stop(`Branch: ${highlight(branch)}`);
|
|
2616
2601
|
if (!options.force && !isTargetBranch(branch, config.targetBranches)) {
|
|
2617
2602
|
p7.log.warn(
|
|
2618
|
-
`Skipping translations (${
|
|
2603
|
+
`Skipping translations (${highlight(branch)} is not a target branch)`
|
|
2619
2604
|
);
|
|
2620
2605
|
p7.log.info(`Target branches: ${config.targetBranches.join(", ")}`);
|
|
2621
2606
|
p7.log.info("Use --force to translate anyway");
|
|
@@ -2639,7 +2624,7 @@ async function sync(options = {}) {
|
|
|
2639
2624
|
return 0;
|
|
2640
2625
|
}
|
|
2641
2626
|
spinner7.stop(
|
|
2642
|
-
`Extracted ${
|
|
2627
|
+
`Extracted ${highlight(extractedStrings.length)} strings from ${highlight(patternsDisplay)}`
|
|
2643
2628
|
);
|
|
2644
2629
|
if (options.verbose) {
|
|
2645
2630
|
const sampleLines = extractedStrings.slice(0, 5).map((s) => ` "${s.text}" (${s.file}:${s.line})`);
|
|
@@ -2660,7 +2645,7 @@ async function sync(options = {}) {
|
|
|
2660
2645
|
].join("\n"),
|
|
2661
2646
|
"Dry run - would translate"
|
|
2662
2647
|
);
|
|
2663
|
-
p7.outro("No API calls made.");
|
|
2648
|
+
p7.outro(active("No API calls made."));
|
|
2664
2649
|
return 0;
|
|
2665
2650
|
}
|
|
2666
2651
|
const repoIdentity = resolveGitRepositoryIdentity();
|
|
@@ -2682,14 +2667,14 @@ async function sync(options = {}) {
|
|
|
2682
2667
|
const cacheFile = getCacheFilePath(projectRoot, fingerprint);
|
|
2683
2668
|
if (existsSync3(cacheFile)) {
|
|
2684
2669
|
if (options.verbose) {
|
|
2685
|
-
p7.log.info(`Cache hit: ${
|
|
2670
|
+
p7.log.info(`Cache hit: ${chalk6.dim(cacheFile)} (fingerprint ${highlight(fingerprint)})`);
|
|
2686
2671
|
}
|
|
2687
2672
|
const duration2 = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
2688
|
-
p7.outro(`Up to date (${duration2}s)`);
|
|
2673
|
+
p7.outro(active(`Up to date (${duration2}s)`));
|
|
2689
2674
|
return 0;
|
|
2690
2675
|
}
|
|
2691
2676
|
if (options.verbose) {
|
|
2692
|
-
p7.log.info(`No cache for fingerprint ${
|
|
2677
|
+
p7.log.info(`No cache for fingerprint ${highlight(fingerprint)} \u2014 will submit to API`);
|
|
2693
2678
|
}
|
|
2694
2679
|
}
|
|
2695
2680
|
spinner7.start("Submitting strings to Vocoder API");
|
|
@@ -2714,7 +2699,7 @@ async function sync(options = {}) {
|
|
|
2714
2699
|
policy: config.syncPolicy
|
|
2715
2700
|
});
|
|
2716
2701
|
if (options.verbose) {
|
|
2717
|
-
p7.log.info(`Batch: ${
|
|
2702
|
+
p7.log.info(`Batch: ${chalk6.dim(batchResponse.batchId)}`);
|
|
2718
2703
|
p7.log.info(`Requested mode: ${requestedMode}`);
|
|
2719
2704
|
p7.log.info(`Effective mode: ${effectiveMode}`);
|
|
2720
2705
|
p7.log.info(`Wait timeout: ${waitTimeoutMs}ms`);
|
|
@@ -2723,14 +2708,14 @@ async function sync(options = {}) {
|
|
|
2723
2708
|
}
|
|
2724
2709
|
}
|
|
2725
2710
|
if (batchResponse.status === "UP_TO_DATE" && batchResponse.noChanges) {
|
|
2726
|
-
p7.log.success(`Up to date \u2014 ${
|
|
2711
|
+
p7.log.success(`Up to date \u2014 ${highlight(batchResponse.totalStrings)} strings, no changes`);
|
|
2727
2712
|
} else if (batchResponse.newStrings === 0) {
|
|
2728
|
-
const archivedNote = batchResponse.deletedStrings && batchResponse.deletedStrings > 0 ? `, ${
|
|
2729
|
-
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`);
|
|
2730
2715
|
} else {
|
|
2731
|
-
const statParts = [`${
|
|
2716
|
+
const statParts = [`${highlight(batchResponse.newStrings)} new, ${highlight(batchResponse.totalStrings)} total`];
|
|
2732
2717
|
if (batchResponse.deletedStrings && batchResponse.deletedStrings > 0) {
|
|
2733
|
-
statParts.push(`${
|
|
2718
|
+
statParts.push(`${chalk6.yellow(batchResponse.deletedStrings)} archived`);
|
|
2734
2719
|
}
|
|
2735
2720
|
const estTime = batchResponse.estimatedTime ? ` (~${batchResponse.estimatedTime}s)` : "";
|
|
2736
2721
|
p7.log.info(`${statParts.join(", ")} \u2192 syncing to ${config.targetLocales.join(", ")}${estTime}`);
|
|
@@ -2851,7 +2836,7 @@ async function sync(options = {}) {
|
|
|
2851
2836
|
);
|
|
2852
2837
|
}
|
|
2853
2838
|
const duration = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
2854
|
-
p7.outro(`Sync complete! (${duration}s)`);
|
|
2839
|
+
p7.outro(active(`Sync complete! (${duration}s)`));
|
|
2855
2840
|
return 0;
|
|
2856
2841
|
} catch (error) {
|
|
2857
2842
|
spinner7.stop();
|
|
@@ -2914,15 +2899,15 @@ async function listProjectLocales(options = {}) {
|
|
|
2914
2899
|
if (!config) return 1;
|
|
2915
2900
|
const api = new VocoderAPI(config);
|
|
2916
2901
|
try {
|
|
2917
|
-
const
|
|
2902
|
+
const projectConfig = await api.getAppConfig();
|
|
2918
2903
|
p8.log.info(
|
|
2919
|
-
`Source locale: ${
|
|
2904
|
+
`Source locale: ${highlight(projectConfig.sourceLocale)}`
|
|
2920
2905
|
);
|
|
2921
|
-
if (
|
|
2906
|
+
if (projectConfig.targetLocales.length === 0) {
|
|
2922
2907
|
p8.log.info("Target locales: (none configured)");
|
|
2923
2908
|
} else {
|
|
2924
2909
|
p8.log.info(
|
|
2925
|
-
`Target locales: ${
|
|
2910
|
+
`Target locales: ${projectConfig.targetLocales.map((l) => highlight(l)).join(", ")}`
|
|
2926
2911
|
);
|
|
2927
2912
|
}
|
|
2928
2913
|
return 0;
|
|
@@ -2949,9 +2934,9 @@ async function addLocales(locales, options = {}) {
|
|
|
2949
2934
|
try {
|
|
2950
2935
|
const result = await api.addLocale(locale);
|
|
2951
2936
|
lastTargetLocales = result.targetLocales;
|
|
2952
|
-
spinner7.stop(`Added ${
|
|
2937
|
+
spinner7.stop(`Added ${highlight(locale)}`);
|
|
2953
2938
|
} catch (error) {
|
|
2954
|
-
spinner7.stop(`Failed to add ${
|
|
2939
|
+
spinner7.stop(`Failed to add ${chalk7.red(locale)}`);
|
|
2955
2940
|
hadError = true;
|
|
2956
2941
|
if (error instanceof VocoderAPIError && error.limitError) {
|
|
2957
2942
|
const { limitError } = error;
|
|
@@ -2968,7 +2953,7 @@ async function addLocales(locales, options = {}) {
|
|
|
2968
2953
|
}
|
|
2969
2954
|
if (lastTargetLocales.length > 0) {
|
|
2970
2955
|
p8.log.info(
|
|
2971
|
-
`Target locales now: ${lastTargetLocales.map((l) =>
|
|
2956
|
+
`Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
|
|
2972
2957
|
);
|
|
2973
2958
|
}
|
|
2974
2959
|
return hadError ? 1 : 0;
|
|
@@ -2989,9 +2974,9 @@ async function removeLocales(locales, options = {}) {
|
|
|
2989
2974
|
try {
|
|
2990
2975
|
const result = await api.removeLocale(locale);
|
|
2991
2976
|
lastTargetLocales = result.targetLocales;
|
|
2992
|
-
spinner7.stop(`Removed ${
|
|
2977
|
+
spinner7.stop(`Removed ${highlight(locale)}`);
|
|
2993
2978
|
} catch (error) {
|
|
2994
|
-
spinner7.stop(`Failed to remove ${
|
|
2979
|
+
spinner7.stop(`Failed to remove ${chalk7.red(locale)}`);
|
|
2995
2980
|
hadError = true;
|
|
2996
2981
|
p8.log.error(
|
|
2997
2982
|
error instanceof Error ? error.message : "Unknown error"
|
|
@@ -3000,7 +2985,7 @@ async function removeLocales(locales, options = {}) {
|
|
|
3000
2985
|
}
|
|
3001
2986
|
if (lastTargetLocales.length > 0) {
|
|
3002
2987
|
p8.log.info(
|
|
3003
|
-
`Target locales now: ${lastTargetLocales.map((l) =>
|
|
2988
|
+
`Target locales now: ${lastTargetLocales.map((l) => highlight(l)).join(", ")}`
|
|
3004
2989
|
);
|
|
3005
2990
|
} else if (!hadError) {
|
|
3006
2991
|
p8.log.info("Target locales now: (none configured)");
|
|
@@ -3013,10 +2998,10 @@ async function listSupportedLocales(options = {}) {
|
|
|
3013
2998
|
const api = new VocoderAPI(config);
|
|
3014
2999
|
try {
|
|
3015
3000
|
const result = await api.listLocales(config.apiKey);
|
|
3016
|
-
p8.log.info(
|
|
3001
|
+
p8.log.info(chalk7.bold("Source locales:"));
|
|
3017
3002
|
printLocaleTable(result.sourceLocales);
|
|
3018
3003
|
p8.log.info("");
|
|
3019
|
-
p8.log.info(
|
|
3004
|
+
p8.log.info(chalk7.bold("Target locales:"));
|
|
3020
3005
|
printLocaleTable(result.targetLocales);
|
|
3021
3006
|
return 0;
|
|
3022
3007
|
} catch (error) {
|
|
@@ -3029,7 +3014,7 @@ async function listSupportedLocales(options = {}) {
|
|
|
3029
3014
|
function printLocaleTable(locales) {
|
|
3030
3015
|
for (const locale of locales) {
|
|
3031
3016
|
const native = locale.nativeName && locale.nativeName !== locale.name ? ` (${locale.nativeName})` : "";
|
|
3032
|
-
p8.log.info(` ${
|
|
3017
|
+
p8.log.info(` ${highlight(locale.code.padEnd(10))} ${locale.name}${native}`);
|
|
3033
3018
|
}
|
|
3034
3019
|
}
|
|
3035
3020
|
|
|
@@ -3052,12 +3037,12 @@ async function logout(options = {}) {
|
|
|
3052
3037
|
return 0;
|
|
3053
3038
|
}
|
|
3054
3039
|
|
|
3055
|
-
// src/commands/
|
|
3040
|
+
// src/commands/app-config.ts
|
|
3056
3041
|
import * as p10 from "@clack/prompts";
|
|
3057
|
-
import
|
|
3042
|
+
import chalk8 from "chalk";
|
|
3058
3043
|
import { config as loadEnv4 } from "dotenv";
|
|
3059
3044
|
loadEnv4();
|
|
3060
|
-
async function
|
|
3045
|
+
async function appConfig(options = {}) {
|
|
3061
3046
|
const apiKey = process.env.VOCODER_API_KEY;
|
|
3062
3047
|
if (!apiKey) {
|
|
3063
3048
|
p10.log.error(
|
|
@@ -3068,21 +3053,21 @@ async function projectConfig(options = {}) {
|
|
|
3068
3053
|
const apiUrl = options.apiUrl ?? process.env.VOCODER_API_URL ?? "https://vocoder.app";
|
|
3069
3054
|
const api = new VocoderAPI({ apiKey, apiUrl });
|
|
3070
3055
|
try {
|
|
3071
|
-
const config = await api.
|
|
3056
|
+
const config = await api.getAppConfig();
|
|
3072
3057
|
const lines = [
|
|
3073
|
-
`
|
|
3058
|
+
`App: ${chalk8.bold(config.projectName)}`,
|
|
3074
3059
|
`Organization: ${config.organizationName}`,
|
|
3075
|
-
`Source locale: ${
|
|
3076
|
-
`Target locales: ${config.targetLocales.length > 0 ? config.targetLocales.map((l) =>
|
|
3077
|
-
`Target branches: ${config.targetBranches.map((b) =>
|
|
3078
|
-
...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)}`] : [],
|
|
3079
3064
|
`Sync policy:`,
|
|
3080
|
-
` Blocking branches: ${config.syncPolicy.blockingBranches.map((b) =>
|
|
3081
|
-
` Blocking mode: ${
|
|
3082
|
-
` Non-blocking mode: ${
|
|
3083
|
-
` 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`
|
|
3084
3069
|
];
|
|
3085
|
-
p10.note(lines.join("\n"), `${config.projectName} \u2014
|
|
3070
|
+
p10.note(lines.join("\n"), `${config.projectName} \u2014 app config`);
|
|
3086
3071
|
return 0;
|
|
3087
3072
|
} catch (error) {
|
|
3088
3073
|
p10.log.error(
|
|
@@ -3096,7 +3081,6 @@ async function projectConfig(options = {}) {
|
|
|
3096
3081
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
3097
3082
|
import { join as join4 } from "path";
|
|
3098
3083
|
import * as p11 from "@clack/prompts";
|
|
3099
|
-
import chalk11 from "chalk";
|
|
3100
3084
|
import { config as loadEnv5 } from "dotenv";
|
|
3101
3085
|
loadEnv5();
|
|
3102
3086
|
async function getTranslations(options = {}) {
|
|
@@ -3119,17 +3103,17 @@ async function getTranslations(options = {}) {
|
|
|
3119
3103
|
return 1;
|
|
3120
3104
|
}
|
|
3121
3105
|
const spinner7 = p11.spinner();
|
|
3122
|
-
spinner7.start(`Fetching translations for ${
|
|
3106
|
+
spinner7.start(`Fetching translations for ${highlight(branch)}\u2026`);
|
|
3123
3107
|
try {
|
|
3124
|
-
const
|
|
3125
|
-
const targetLocales = options.locale ? [options.locale] :
|
|
3108
|
+
const projectConfig = await api.getAppConfig();
|
|
3109
|
+
const targetLocales = options.locale ? [options.locale] : projectConfig.targetLocales;
|
|
3126
3110
|
if (targetLocales.length === 0) {
|
|
3127
3111
|
spinner7.stop("No target locales configured.");
|
|
3128
3112
|
p11.log.info("Add target locales with `vocoder locales add <code>`.");
|
|
3129
3113
|
return 1;
|
|
3130
3114
|
}
|
|
3131
3115
|
const snapshot = await api.getTranslationSnapshot({ branch, targetLocales });
|
|
3132
|
-
spinner7.stop(`Fetched translations for ${
|
|
3116
|
+
spinner7.stop(`Fetched translations for ${highlight(branch)}`);
|
|
3133
3117
|
if (snapshot.status === "NOT_FOUND") {
|
|
3134
3118
|
p11.log.warn(
|
|
3135
3119
|
`No translation snapshot found for branch "${branch}". Run \`vocoder sync\` to generate one.`
|
|
@@ -3157,16 +3141,16 @@ function writeLocaleFiles(translations, outputDir) {
|
|
|
3157
3141
|
for (const [locale, strings] of Object.entries(translations)) {
|
|
3158
3142
|
const filePath = join4(outputDir, `${locale}.json`);
|
|
3159
3143
|
writeFileSync4(filePath, JSON.stringify(strings, null, 2) + "\n", "utf-8");
|
|
3160
|
-
p11.log.success(`Wrote ${
|
|
3144
|
+
p11.log.success(`Wrote ${highlight(filePath)}`);
|
|
3161
3145
|
}
|
|
3162
3146
|
}
|
|
3163
3147
|
|
|
3164
|
-
// src/commands/create-
|
|
3148
|
+
// src/commands/create-app.ts
|
|
3165
3149
|
import * as p12 from "@clack/prompts";
|
|
3166
|
-
import
|
|
3150
|
+
import chalk9 from "chalk";
|
|
3167
3151
|
import { config as loadEnv6 } from "dotenv";
|
|
3168
3152
|
loadEnv6();
|
|
3169
|
-
async function
|
|
3153
|
+
async function createApp(options) {
|
|
3170
3154
|
const authData = readAuthData();
|
|
3171
3155
|
if (!authData) {
|
|
3172
3156
|
p12.log.error(
|
|
@@ -3196,7 +3180,7 @@ async function createProject(options) {
|
|
|
3196
3180
|
const targetLocales = options.targetLocales ? options.targetLocales.split(",").map((l) => l.trim()).filter(Boolean) : [];
|
|
3197
3181
|
const targetBranches = options.targetBranches ? options.targetBranches.split(",").map((b) => b.trim()).filter(Boolean) : ["main"];
|
|
3198
3182
|
const spinner7 = p12.spinner();
|
|
3199
|
-
spinner7.start(`Creating
|
|
3183
|
+
spinner7.start(`Creating app "${options.name}"\u2026`);
|
|
3200
3184
|
try {
|
|
3201
3185
|
const result = await api.createProject(authData.token, {
|
|
3202
3186
|
organizationId: options.workspace,
|
|
@@ -3207,16 +3191,16 @@ async function createProject(options) {
|
|
|
3207
3191
|
appDirs: [appDir],
|
|
3208
3192
|
...repoCanonical ? { repoCanonical } : {}
|
|
3209
3193
|
});
|
|
3210
|
-
spinner7.stop(`Created
|
|
3194
|
+
spinner7.stop(`Created app ${chalk9.bold(result.projectName)}`);
|
|
3211
3195
|
const lines = [
|
|
3212
3196
|
`Project ID: ${result.projectId}`,
|
|
3213
|
-
`Source locale: ${
|
|
3214
|
-
`Target locales: ${result.targetLocales.length > 0 ? result.targetLocales.map((l) =>
|
|
3215
|
-
`Branches: ${result.targetBranches.map((b) =>
|
|
3216
|
-
...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})` : ""}`] : [],
|
|
3217
3201
|
"",
|
|
3218
3202
|
`Add this to your .env file:`,
|
|
3219
|
-
` ${
|
|
3203
|
+
` ${chalk9.bold("VOCODER_API_KEY")}=${highlight(result.apiKey)}`
|
|
3220
3204
|
];
|
|
3221
3205
|
p12.note(lines.join("\n"), "Project created");
|
|
3222
3206
|
if (!result.repositoryBound && repoCanonical) {
|
|
@@ -3244,7 +3228,7 @@ async function createProject(options) {
|
|
|
3244
3228
|
|
|
3245
3229
|
// src/commands/whoami.ts
|
|
3246
3230
|
import * as p13 from "@clack/prompts";
|
|
3247
|
-
import
|
|
3231
|
+
import chalk10 from "chalk";
|
|
3248
3232
|
async function whoami(options = {}) {
|
|
3249
3233
|
const stored = readAuthData();
|
|
3250
3234
|
if (!stored) {
|
|
@@ -3254,10 +3238,10 @@ async function whoami(options = {}) {
|
|
|
3254
3238
|
const apiUrl = options.apiUrl ?? stored.apiUrl ?? "https://vocoder.app";
|
|
3255
3239
|
const api = new VocoderAPI({ apiUrl, apiKey: "" });
|
|
3256
3240
|
try {
|
|
3257
|
-
const
|
|
3258
|
-
p13.log.info(`Logged in as ${
|
|
3259
|
-
if (
|
|
3260
|
-
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}`);
|
|
3261
3245
|
}
|
|
3262
3246
|
p13.log.info(`API: ${apiUrl}`);
|
|
3263
3247
|
return 0;
|
|
@@ -3282,7 +3266,7 @@ program.name("vocoder").description("Vocoder CLI - Project setup and string extr
|
|
|
3282
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(
|
|
3283
3267
|
"--ci",
|
|
3284
3268
|
"Non-interactive mode: print auth URL to stdout, skip browser open"
|
|
3285
|
-
).option("--
|
|
3269
|
+
).option("--app-name <name>", "Starter app name to create").option("--source-locale <locale>", "Source locale for the starter project").option(
|
|
3286
3270
|
"--target-locales <list>",
|
|
3287
3271
|
"Comma-separated target locales (e.g. es,fr,de)"
|
|
3288
3272
|
).action((options) => runCommand(init, options));
|
|
@@ -3302,9 +3286,9 @@ localesCmd.command("remove <codes...>").description("Remove one or more target l
|
|
|
3302
3286
|
(codes, options) => runCommand((opts) => removeLocales(codes, opts), options)
|
|
3303
3287
|
);
|
|
3304
3288
|
localesCmd.command("supported").description("List all locales supported by Vocoder").option("--api-url <url>", "Override Vocoder API URL").action((options) => runCommand(listSupportedLocales, options));
|
|
3305
|
-
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));
|
|
3306
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));
|
|
3307
|
-
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(
|
|
3308
3292
|
"--target-locales <codes>",
|
|
3309
3293
|
"Comma-separated target locale codes (e.g. fr,de,pt-BR)"
|
|
3310
3294
|
).option(
|
|
@@ -3325,7 +3309,7 @@ program.command("create-project").description("Create a new Vocoder project (req
|
|
|
3325
3309
|
targetBranches: options.targetBranches,
|
|
3326
3310
|
workspace: options.workspace
|
|
3327
3311
|
};
|
|
3328
|
-
return runCommand(
|
|
3312
|
+
return runCommand(createApp, translated);
|
|
3329
3313
|
});
|
|
3330
3314
|
program.parse(process.argv);
|
|
3331
3315
|
//# sourceMappingURL=bin.mjs.map
|