mimi-seed 0.6.0 → 0.9.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.
@@ -1,51 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ catalog,
4
+ isLangUnset,
5
+ resolveLang,
6
+ t,
7
+ writeSettings
8
+ } from "./chunk-SZQFVTEW.js";
2
9
 
3
10
  // src/setup.ts
4
11
  import kleur2 from "kleur";
5
12
  import * as readline2 from "readline";
6
- import os6 from "os";
13
+ import os5 from "os";
7
14
 
8
15
  // src/credentials.ts
9
- import fs2 from "fs";
10
- import os2 from "os";
11
- import path2 from "path";
12
-
13
- // src/settings.ts
14
16
  import fs from "fs";
15
17
  import os from "os";
16
18
  import path from "path";
17
- var DEFAULT_LANG = "ko";
18
- function settingsPath(home) {
19
- return path.join(home, ".mimi-seed", "settings.json");
20
- }
21
- function readSettings(home = os.homedir()) {
22
- try {
23
- return JSON.parse(fs.readFileSync(settingsPath(home), "utf-8"));
24
- } catch {
25
- return {};
26
- }
27
- }
28
- function writeSettings(next, home = os.homedir()) {
29
- const dir = path.join(home, ".mimi-seed");
30
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
31
- const merged = { ...readSettings(home), ...next };
32
- fs.writeFileSync(settingsPath(home), JSON.stringify(merged, null, 2));
33
- }
34
- function isLangUnset(home = os.homedir()) {
35
- return !process.env.MIMI_SEED_LANG && !readSettings(home).lang;
36
- }
37
- function isLang(v) {
38
- return v === "ko" || v === "en";
39
- }
40
- function resolveLang(home = os.homedir()) {
41
- const env = process.env.MIMI_SEED_LANG?.toLowerCase();
42
- if (isLang(env)) return env;
43
- const saved = readSettings(home).lang;
44
- if (isLang(saved)) return saved;
45
- return DEFAULT_LANG;
46
- }
47
-
48
- // src/credentials.ts
49
19
  function credLabel(spec, lang = resolveLang()) {
50
20
  return spec.label[lang];
51
21
  }
@@ -56,21 +26,21 @@ function credObtain(spec, lang = resolveLang()) {
56
26
  return spec.obtain[lang];
57
27
  }
58
28
  function credDir(home) {
59
- return path2.join(home, ".mimi-seed");
29
+ return path.join(home, ".mimi-seed");
60
30
  }
61
31
  function hasFile(home, name) {
62
- return fs2.existsSync(path2.join(credDir(home), name));
32
+ return fs.existsSync(path.join(credDir(home), name));
63
33
  }
64
34
  function anyFileStarting(home, prefix) {
65
35
  try {
66
- return fs2.readdirSync(credDir(home)).some((f) => f.startsWith(prefix));
36
+ return fs.readdirSync(credDir(home)).some((f) => f.startsWith(prefix));
67
37
  } catch {
68
38
  return false;
69
39
  }
70
40
  }
71
41
  function readJson(home, name) {
72
42
  try {
73
- return JSON.parse(fs2.readFileSync(path2.join(credDir(home), name), "utf-8"));
43
+ return JSON.parse(fs.readFileSync(path.join(credDir(home), name), "utf-8"));
74
44
  } catch {
75
45
  return null;
76
46
  }
@@ -78,7 +48,7 @@ function readJson(home, name) {
78
48
  function hasPlaySa(home) {
79
49
  if (hasFile(home, "play-service-account.json")) return true;
80
50
  try {
81
- return fs2.readdirSync(path2.join(credDir(home), "play-service-accounts")).some((f) => f.endsWith(".json"));
51
+ return fs.readdirSync(path.join(credDir(home), "play-service-accounts")).some((f) => f.endsWith(".json"));
82
52
  } catch {
83
53
  return false;
84
54
  }
@@ -464,7 +434,7 @@ var CREDENTIALS = [
464
434
  function tryCredById(id) {
465
435
  return CREDENTIALS.find((c) => c.id === id);
466
436
  }
467
- function detectAll(home = os2.homedir()) {
437
+ function detectAll(home = os.homedir()) {
468
438
  return new Map(CREDENTIALS.map((c) => [c.id, c.detect(home)]));
469
439
  }
470
440
  function isSatisfied(spec, detected) {
@@ -509,7 +479,8 @@ function resolveOnPath(bin) {
509
479
  async function runMcpBin(bin, extraArgs = []) {
510
480
  const local = resolveOnPath(bin);
511
481
  const cmd = local ? bin : "npx";
512
- const args = local ? extraArgs : ["-y", MCP_PKG, bin, ...extraArgs];
482
+ const pkg = process.env.MIMI_SEED_FORCE_NPX ? `${MCP_PKG}@latest` : MCP_PKG;
483
+ const args = local ? extraArgs : ["-y", pkg, bin, ...extraArgs];
513
484
  return new Promise((resolve) => {
514
485
  const child = spawn(cmd, args, {
515
486
  stdio: "inherit",
@@ -517,9 +488,7 @@ async function runMcpBin(bin, extraArgs = []) {
517
488
  env: { ...process.env, MIMI_SEED_LANG: resolveLang() }
518
489
  });
519
490
  child.on("error", (e) => {
520
- process.stderr.write(`
521
- \u274C ${cmd} \uC2E4\uD589 \uC2E4\uD328: ${e.message}
522
- `);
491
+ process.stderr.write(t().auth.npxFailed(cmd, e.message));
523
492
  resolve(1);
524
493
  });
525
494
  child.on("exit", (code) => resolve(code ?? 0));
@@ -527,28 +496,28 @@ async function runMcpBin(bin, extraArgs = []) {
527
496
  }
528
497
 
529
498
  // src/jenkins-config.ts
530
- import fs3 from "fs";
531
- import os3 from "os";
532
- import path3 from "path";
533
- var CONFIG_DIR = path3.join(os3.homedir(), ".mimi-seed");
534
- var JENKINS_PATH = path3.join(CONFIG_DIR, "jenkins.json");
535
- var LEGACY_PATH = path3.join(CONFIG_DIR, "config.json");
536
- function loadJenkinsConfig(home = os3.homedir()) {
499
+ import fs2 from "fs";
500
+ import os2 from "os";
501
+ import path2 from "path";
502
+ var CONFIG_DIR = path2.join(os2.homedir(), ".mimi-seed");
503
+ var JENKINS_PATH = path2.join(CONFIG_DIR, "jenkins.json");
504
+ var LEGACY_PATH = path2.join(CONFIG_DIR, "config.json");
505
+ function loadJenkinsConfig(home = os2.homedir()) {
537
506
  try {
538
- const p = path3.join(home, ".mimi-seed", "jenkins.json");
539
- return JSON.parse(fs3.readFileSync(p, "utf-8"));
507
+ const p = path2.join(home, ".mimi-seed", "jenkins.json");
508
+ return JSON.parse(fs2.readFileSync(p, "utf-8"));
540
509
  } catch {
541
510
  return null;
542
511
  }
543
512
  }
544
- function migrateLegacyJenkins(home = os3.homedir()) {
545
- const dir = path3.join(home, ".mimi-seed");
546
- const jenkinsPath = path3.join(dir, "jenkins.json");
547
- const legacyPath = path3.join(dir, "config.json");
548
- if (fs3.existsSync(jenkinsPath)) return false;
513
+ function migrateLegacyJenkins(home = os2.homedir()) {
514
+ const dir = path2.join(home, ".mimi-seed");
515
+ const jenkinsPath = path2.join(dir, "jenkins.json");
516
+ const legacyPath = path2.join(dir, "config.json");
517
+ if (fs2.existsSync(jenkinsPath)) return false;
549
518
  let legacy;
550
519
  try {
551
- legacy = JSON.parse(fs3.readFileSync(legacyPath, "utf-8"));
520
+ legacy = JSON.parse(fs2.readFileSync(legacyPath, "utf-8"));
552
521
  } catch {
553
522
  return false;
554
523
  }
@@ -562,28 +531,28 @@ function migrateLegacyJenkins(home = os3.homedir()) {
562
531
  ...j.jobAndroid ? { jobAndroid: j.jobAndroid } : {},
563
532
  ...j.jobIos ? { jobIos: j.jobIos } : {}
564
533
  };
565
- fs3.mkdirSync(dir, { recursive: true, mode: 448 });
566
- fs3.writeFileSync(jenkinsPath, JSON.stringify(migrated, null, 2), { mode: 384 });
534
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
535
+ fs2.writeFileSync(jenkinsPath, JSON.stringify(migrated, null, 2), { mode: 384 });
567
536
  delete legacy.jenkins;
568
537
  const tmp = `${legacyPath}.tmp`;
569
- fs3.writeFileSync(tmp, JSON.stringify(legacy, null, 2), { mode: 384 });
570
- fs3.renameSync(tmp, legacyPath);
538
+ fs2.writeFileSync(tmp, JSON.stringify(legacy, null, 2), { mode: 384 });
539
+ fs2.renameSync(tmp, legacyPath);
571
540
  return true;
572
541
  }
573
542
 
574
543
  // src/detect.ts
575
- import fs4 from "fs/promises";
576
- import path4 from "path";
544
+ import fs3 from "fs/promises";
545
+ import path3 from "path";
577
546
  async function readIfExists(p) {
578
547
  try {
579
- return await fs4.readFile(p, "utf8");
548
+ return await fs3.readFile(p, "utf8");
580
549
  } catch {
581
550
  return null;
582
551
  }
583
552
  }
584
553
  async function pathExists(p) {
585
554
  try {
586
- await fs4.access(p);
555
+ await fs3.access(p);
587
556
  return true;
588
557
  } catch {
589
558
  return false;
@@ -605,16 +574,16 @@ async function walk(root, match, maxDepth = 5) {
605
574
  if (depth > maxDepth) return;
606
575
  let entries;
607
576
  try {
608
- entries = await fs4.readdir(dir, { withFileTypes: true });
577
+ entries = await fs3.readdir(dir, { withFileTypes: true });
609
578
  } catch {
610
579
  return;
611
580
  }
612
581
  for (const e of entries) {
613
582
  if (e.isDirectory()) {
614
583
  if (skipDirs.has(e.name)) continue;
615
- await visit(path4.join(dir, e.name), depth + 1);
584
+ await visit(path3.join(dir, e.name), depth + 1);
616
585
  } else if (e.isFile() && match(e.name)) {
617
- found.push(path4.join(dir, e.name));
586
+ found.push(path3.join(dir, e.name));
618
587
  }
619
588
  }
620
589
  }
@@ -624,7 +593,7 @@ async function walk(root, match, maxDepth = 5) {
624
593
  async function detectHints(cwd) {
625
594
  const hints = [];
626
595
  for (const fname of ["app.json", "app.config.json"]) {
627
- const txt = await readIfExists(path4.join(cwd, fname));
596
+ const txt = await readIfExists(path3.join(cwd, fname));
628
597
  if (!txt) continue;
629
598
  try {
630
599
  const json = JSON.parse(txt);
@@ -655,7 +624,7 @@ async function detectHints(cwd) {
655
624
  if (m?.[1]) {
656
625
  const pkg = m[1];
657
626
  if (!hints.some((h) => h.packageName === pkg)) {
658
- hints.push({ packageName: pkg, source: [path4.relative(cwd, f)] });
627
+ hints.push({ packageName: pkg, source: [path3.relative(cwd, f)] });
659
628
  }
660
629
  }
661
630
  }
@@ -670,7 +639,7 @@ async function detectHints(cwd) {
670
639
  let bid = m[1];
671
640
  if (bid.includes("$(PRODUCT_BUNDLE_IDENTIFIER)")) continue;
672
641
  if (!hints.some((h) => h.bundleId === bid)) {
673
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
642
+ hints.push({ bundleId: bid, source: [path3.relative(cwd, f)] });
674
643
  }
675
644
  }
676
645
  }
@@ -683,11 +652,11 @@ async function detectHints(cwd) {
683
652
  const bid = m[1].trim().replace(/^["']|["']$/g, "");
684
653
  if (!bid || bid.includes("$")) continue;
685
654
  if (!hints.some((h) => h.bundleId === bid)) {
686
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
655
+ hints.push({ bundleId: bid, source: [path3.relative(cwd, f)] });
687
656
  }
688
657
  }
689
658
  }
690
- const pkgJson = await readIfExists(path4.join(cwd, "package.json"));
659
+ const pkgJson = await readIfExists(path3.join(cwd, "package.json"));
691
660
  if (pkgJson) {
692
661
  try {
693
662
  const json = JSON.parse(pkgJson);
@@ -723,7 +692,7 @@ async function detectHints(cwd) {
723
692
  return merged.filter((h) => h.packageName || h.bundleId);
724
693
  }
725
694
  async function hasAnyProjectSignal(cwd) {
726
- return await pathExists(path4.join(cwd, "package.json")) || await pathExists(path4.join(cwd, "app.json")) || await pathExists(path4.join(cwd, "android")) || await pathExists(path4.join(cwd, "ios"));
695
+ return await pathExists(path3.join(cwd, "package.json")) || await pathExists(path3.join(cwd, "app.json")) || await pathExists(path3.join(cwd, "android")) || await pathExists(path3.join(cwd, "ios"));
727
696
  }
728
697
 
729
698
  // src/deploy.ts
@@ -731,28 +700,28 @@ import kleur from "kleur";
731
700
  import * as readline from "readline";
732
701
 
733
702
  // src/config.ts
734
- import fs5 from "fs/promises";
735
- import path5 from "path";
736
- import os4 from "os";
737
- var CONFIG_DIR2 = path5.join(os4.homedir(), ".mimi-seed");
738
- var CONFIG_PATH = path5.join(CONFIG_DIR2, "config.json");
703
+ import fs4 from "fs/promises";
704
+ import path4 from "path";
705
+ import os3 from "os";
706
+ var CONFIG_DIR2 = path4.join(os3.homedir(), ".mimi-seed");
707
+ var CONFIG_PATH = path4.join(CONFIG_DIR2, "config.json");
739
708
  async function readConfig() {
740
709
  try {
741
- const txt = await fs5.readFile(CONFIG_PATH, "utf8");
710
+ const txt = await fs4.readFile(CONFIG_PATH, "utf8");
742
711
  return JSON.parse(txt);
743
712
  } catch {
744
713
  return null;
745
714
  }
746
715
  }
747
716
  async function writeConfig(cfg) {
748
- await fs5.mkdir(CONFIG_DIR2, { recursive: true });
749
- await fs5.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
717
+ await fs4.mkdir(CONFIG_DIR2, { recursive: true });
718
+ await fs4.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
750
719
  if (process.platform !== "win32") {
751
- await fs5.chmod(CONFIG_PATH, 384);
720
+ await fs4.chmod(CONFIG_PATH, 384);
752
721
  }
753
722
  }
754
723
  async function deleteConfig() {
755
- await fs5.rm(CONFIG_PATH, { force: true });
724
+ await fs4.rm(CONFIG_PATH, { force: true });
756
725
  }
757
726
  var CONFIG_LOCATION = CONFIG_PATH;
758
727
  async function getEffectiveConfig() {
@@ -771,13 +740,28 @@ async function getEffectiveConfig() {
771
740
  }
772
741
 
773
742
  // src/ci-providers.ts
774
- import fs6 from "fs";
775
- import path6 from "path";
776
- import os5 from "os";
777
- var CI_CONFIG_PATH = path6.join(os5.homedir(), ".mimi-seed", "ci.json");
743
+ import fs5 from "fs";
744
+ import path5 from "path";
745
+ import os4 from "os";
746
+ var CI_CONFIG_PATH = path5.join(os4.homedir(), ".mimi-seed", "ci.json");
747
+ var M = catalog(
748
+ {
749
+ badToken: (provider, status) => `${provider} ${status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544`,
750
+ noScope: (scopes) => `\uD1A0\uD070\uC5D0 \`workflow\` \uC2A4\uCF54\uD504\uAC00 \uC5C6\uC5B4 (\uD604\uC7AC: ${scopes || "\uC5C6\uC74C"}). \uC6CC\uD06C\uD50C\uB85C \uC2E4\uD589\uC774 403 \uC73C\uB85C \uB9C9\uD78C\uB2E4.`,
751
+ pollFailed: (provider) => `${provider} API \uC5F0\uC18D \uC624\uB958 3\uD68C`,
752
+ pollFailedHttp: (provider, status) => `${provider} API \uC5F0\uC18D \uC624\uB958 (HTTP ${status})`
753
+ },
754
+ {
755
+ badToken: (provider, status) => `${provider} ${status} \u2014 the token is not valid`,
756
+ noScope: (scopes) => `The token is missing the \`workflow\` scope (currently: ${scopes || "none"}). Triggering a workflow will be blocked with a 403.`,
757
+ // 프로바이더 이름("GitHub API")은 두 언어 모두에 남는다 — 테스트가 그걸로 단언한다.
758
+ pollFailed: (provider) => `${provider} API failed 3 times in a row`,
759
+ pollFailedHttp: (provider, status) => `${provider} API failed 3 times in a row (HTTP ${status})`
760
+ }
761
+ );
778
762
  function loadCiProviderConfig() {
779
763
  try {
780
- const cfg = JSON.parse(fs6.readFileSync(CI_CONFIG_PATH, "utf-8"));
764
+ const cfg = JSON.parse(fs5.readFileSync(CI_CONFIG_PATH, "utf-8"));
781
765
  const host = normalizeHost(cfg.host);
782
766
  return host ? { ...cfg, host } : { ...cfg, host: void 0 };
783
767
  } catch {
@@ -791,15 +775,15 @@ function normalizeHost(host) {
791
775
  return withScheme.replace(/\/+$/, "");
792
776
  }
793
777
  function saveCiProviderConfig(cfg) {
794
- const dir = path6.dirname(CI_CONFIG_PATH);
795
- if (!fs6.existsSync(dir)) {
796
- fs6.mkdirSync(dir, { recursive: true, mode: 448 });
778
+ const dir = path5.dirname(CI_CONFIG_PATH);
779
+ if (!fs5.existsSync(dir)) {
780
+ fs5.mkdirSync(dir, { recursive: true, mode: 448 });
797
781
  }
798
782
  const normalized = { ...cfg, host: normalizeHost(cfg.host) };
799
783
  if (!normalized.host) delete normalized.host;
800
- fs6.writeFileSync(CI_CONFIG_PATH, JSON.stringify(normalized, null, 2));
784
+ fs5.writeFileSync(CI_CONFIG_PATH, JSON.stringify(normalized, null, 2));
801
785
  if (process.platform !== "win32") {
802
- fs6.chmodSync(CI_CONFIG_PATH, 384);
786
+ fs5.chmodSync(CI_CONFIG_PATH, 384);
803
787
  }
804
788
  }
805
789
  async function verifyCiToken(cfg) {
@@ -810,21 +794,18 @@ async function verifyCiToken(cfg) {
810
794
  headers: { Authorization: `Bearer ${probe.token}`, Accept: "application/vnd.github+json" }
811
795
  });
812
796
  if (!res2.ok) {
813
- return { ok: false, reason: `GitHub ${res2.status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544` };
797
+ return { ok: false, reason: M().badToken("GitHub", res2.status) };
814
798
  }
815
799
  const user2 = await res2.json();
816
800
  const scopes = res2.headers.get("x-oauth-scopes");
817
801
  if (scopes !== null && !scopes.split(/,\s*/).filter(Boolean).includes("workflow")) {
818
- return {
819
- ok: false,
820
- reason: `\uD1A0\uD070\uC5D0 \`workflow\` \uC2A4\uCF54\uD504\uAC00 \uC5C6\uC5B4 (\uD604\uC7AC: ${scopes || "\uC5C6\uC74C"}). \uC6CC\uD06C\uD50C\uB85C \uC2E4\uD589\uC774 403 \uC73C\uB85C \uB9C9\uD78C\uB2E4.`
821
- };
802
+ return { ok: false, reason: M().noScope(scopes) };
822
803
  }
823
804
  return { ok: true, login: user2.login };
824
805
  }
825
806
  const res = await fetch(`${glBase(probe)}/user`, { headers: { "PRIVATE-TOKEN": probe.token } });
826
807
  if (!res.ok) {
827
- return { ok: false, reason: `GitLab ${res.status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544` };
808
+ return { ok: false, reason: M().badToken("GitLab", res.status) };
828
809
  }
829
810
  const user = await res.json();
830
811
  return { ok: true, login: user.username };
@@ -882,12 +863,12 @@ async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3, interval
882
863
  );
883
864
  } catch {
884
865
  consecutiveErrors++;
885
- if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
866
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailed("GitHub"));
886
867
  continue;
887
868
  }
888
869
  if (!res.ok) {
889
870
  consecutiveErrors++;
890
- if (consecutiveErrors >= 3) throw new Error(`GitHub API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
871
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailedHttp("GitHub", res.status));
891
872
  continue;
892
873
  }
893
874
  consecutiveErrors = 0;
@@ -933,12 +914,12 @@ async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3
933
914
  );
934
915
  } catch {
935
916
  consecutiveErrors++;
936
- if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
917
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailed("GitLab"));
937
918
  continue;
938
919
  }
939
920
  if (!res.ok) {
940
921
  consecutiveErrors++;
941
- if (consecutiveErrors >= 3) throw new Error(`GitLab API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
922
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailedHttp("GitLab", res.status));
942
923
  continue;
943
924
  }
944
925
  consecutiveErrors = 0;
@@ -964,6 +945,144 @@ var PHASE_ICON = {
964
945
  function log(msg) {
965
946
  process.stdout.write(msg + "\n");
966
947
  }
948
+ var M2 = catalog(
949
+ {
950
+ // Jenkins / 빌드
951
+ jenkinsTriggerFailed: (status, body) => `Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${status}: ${body}`,
952
+ buildStatusFailed: (status) => `\uBE4C\uB4DC \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328 ${status}`,
953
+ jenkinsConnErrorFatal: "Jenkins \uC5F0\uACB0 \uC624\uB958 3\uD68C \uC5F0\uC18D \u2014 \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uC138\uC694",
954
+ jenkinsConnErrorRetry: (n) => `Jenkins \uC5F0\uACB0 \uC624\uB958 (${n}/3\uD68C), \uC7AC\uC2DC\uB3C4...`,
955
+ buildRunning: "\uBE4C\uB4DC \uC9C4\uD589 \uC911",
956
+ buildTimeout: "\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)",
957
+ // 서버 스트림
958
+ serverDeployFailed: (status, body) => `\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${status}: ${body}`,
959
+ noSseStream: "SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C",
960
+ // CI provider 설정 프롬프트
961
+ githubSetupTitle: "GitHub Actions \uC124\uC815",
962
+ gitlabSetupTitle: "GitLab CI \uC124\uC815",
963
+ githubTokenPrompt: " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): ",
964
+ gitlabTokenPrompt: " GitLab Personal Access Token: ",
965
+ githubOwnerPrompt: " Owner (org/user): ",
966
+ gitlabOwnerPrompt: " Namespace/group: ",
967
+ repoPrompt: " Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ",
968
+ githubHostPrompt: " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): ",
969
+ gitlabHostPrompt: " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ",
970
+ githubSaved: "\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json",
971
+ gitlabSaved: "\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json",
972
+ noCiConfig: "CI \uC124\uC815 \uC5C6\uC74C. \uB2E4\uC74C \uC911 \uD558\uB098 \uC2E4\uD589:\n \u2022 mimi-seed deploy setup-jenkins\n \u2022 mimi-seed deploy setup-github\n \u2022 mimi-seed deploy setup-gitlab",
973
+ // GitHub / GitLab 빌드
974
+ workflowRequired: "--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)",
975
+ ghTrigger: (workflow, ref) => `\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${workflow} @ ${ref}`,
976
+ ghRunIdFailed: "GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.",
977
+ ghRunId: (id, url) => ` Run ID: ${id} \u2192 ${url}`,
978
+ glTrigger: (ref) => `\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${ref}`,
979
+ glPipelineId: (id, url) => ` Pipeline ID: ${id} \u2192 ${url}`,
980
+ waitingForCompletion: " \uC644\uB8CC \uB300\uAE30 \uC911...",
981
+ buildSucceeded: (n) => `\u2705 \uBE4C\uB4DC #${n} \uC131\uACF5`,
982
+ buildEnded: (result) => `\uBE4C\uB4DC \uC885\uB8CC: ${result}`,
983
+ alreadyBuiltHint: (versionCode, platform) => ` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${versionCode} --platform ${platform}`,
984
+ // cmdDeploy
985
+ noAccount: "\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589.",
986
+ title: (platform) => `mimi-seed deploy \u2014 ${platform}`,
987
+ dryRunNotice: " [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
988
+ ciLine: (kind) => ` CI: ${kind}`,
989
+ noJenkinsConfig: "Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9.",
990
+ noJenkinsJob: (platform) => `${platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`,
991
+ jenkinsTrigger: (job) => `\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${job}`,
992
+ noQueueItem: " \u26A0 Queue item ID\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBE4C\uB4DC\uB294 \uC2DC\uC791\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
993
+ queueItem: (id) => ` Queue item: ${id}`,
994
+ waitingForBuildNumber: " \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...",
995
+ noBuildNumber: " \uBE4C\uB4DC \uBC88\uD638\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. --skip-build + --version-code \uB85C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5.",
996
+ buildStarted: (n) => ` \uBE4C\uB4DC #${n} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`,
997
+ buildFailed: (result) => `\uBE4C\uB4DC \uC2E4\uD328: ${result}`,
998
+ jenkinsLink: (url) => ` Jenkins: ${url}`,
999
+ versionCodeFromBuild: (n) => ` versionCode = buildNumber (${n})`,
1000
+ noProviderConfig: (kind) => `${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`,
1001
+ versionCodeUnsuitable: (kind, buildId) => `\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`,
1002
+ recommendation: " \uAD8C\uC7A5 \uC0AC\uD56D:",
1003
+ recommendationCi: " \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825",
1004
+ recommendationNext: " \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>",
1005
+ versionCodeUnknown: "versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694.",
1006
+ appLine: (name, id) => ` \uC571: ${name} (${id})`,
1007
+ noAppId: "appId\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --app <id> \uB85C \uC9C0\uC815\uD558\uAC70\uB098 `mimi-seed init` \uC73C\uB85C \uC571 \uB4F1\uB85D.",
1008
+ // 프로덕션 확인
1009
+ targetIos: "App Store \uC2EC\uC0AC \uC81C\uCD9C",
1010
+ targetAndroid: "Play Store production \uD2B8\uB799",
1011
+ realDeploy: (platform, versionCode, target) => `\u26A0 \uC2E4\uC81C \uBC30\uD3EC: ${platform} \xB7 versionCode ${versionCode} \u2192 ${target}`,
1012
+ confirmPrompt: "\uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694? [y/N]: ",
1013
+ confirmCancelled: "\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)",
1014
+ pipelineStarting: "\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...",
1015
+ done: "\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."
1016
+ },
1017
+ {
1018
+ // Jenkins / build
1019
+ jenkinsTriggerFailed: (status, body) => `Jenkins trigger failed ${status}: ${body}`,
1020
+ buildStatusFailed: (status) => `Failed to fetch build status ${status}`,
1021
+ jenkinsConnErrorFatal: "Jenkins connection failed 3 times in a row \u2014 check your network",
1022
+ jenkinsConnErrorRetry: (n) => `Jenkins connection error (${n}/3), retrying...`,
1023
+ buildRunning: "Build running",
1024
+ buildTimeout: "Build timed out (30 min)",
1025
+ // Server stream
1026
+ serverDeployFailed: (status, body) => `Server deploy failed ${status}: ${body}`,
1027
+ noSseStream: "No SSE stream",
1028
+ // CI provider setup prompts
1029
+ githubSetupTitle: "GitHub Actions setup",
1030
+ gitlabSetupTitle: "GitLab CI setup",
1031
+ githubTokenPrompt: " GitHub Personal Access Token (repo+workflow scopes): ",
1032
+ gitlabTokenPrompt: " GitLab Personal Access Token: ",
1033
+ githubOwnerPrompt: " Owner (org/user): ",
1034
+ gitlabOwnerPrompt: " Namespace/group: ",
1035
+ repoPrompt: " Repo name (without the path): ",
1036
+ githubHostPrompt: " GitHub Enterprise host (optional, Enter = github.com): ",
1037
+ gitlabHostPrompt: " GitLab self-hosted URL (optional, Enter = gitlab.com): ",
1038
+ githubSaved: "\u2705 GitHub Actions config saved \u2192 ~/.mimi-seed/ci.json",
1039
+ gitlabSaved: "\u2705 GitLab CI config saved \u2192 ~/.mimi-seed/ci.json",
1040
+ noCiConfig: "No CI configured. Run one of:\n \u2022 mimi-seed deploy setup-jenkins\n \u2022 mimi-seed deploy setup-github\n \u2022 mimi-seed deploy setup-gitlab",
1041
+ // GitHub / GitLab build
1042
+ workflowRequired: "--workflow is required (e.g. --workflow deploy.yml)",
1043
+ ghTrigger: (workflow, ref) => `\u{1F528} Triggering GitHub Actions: ${workflow} @ ${ref}`,
1044
+ ghRunIdFailed: "Could not resolve the GitHub Actions run_id. Check with ci_list_recent_builds in a moment.",
1045
+ ghRunId: (id, url) => ` Run ID: ${id} \u2192 ${url}`,
1046
+ glTrigger: (ref) => `\u{1F528} Triggering GitLab Pipeline: ${ref}`,
1047
+ glPipelineId: (id, url) => ` Pipeline ID: ${id} \u2192 ${url}`,
1048
+ waitingForCompletion: " Waiting for it to finish...",
1049
+ buildSucceeded: (n) => `\u2705 Build #${n} succeeded`,
1050
+ buildEnded: (result) => `Build ended: ${result}`,
1051
+ alreadyBuiltHint: (versionCode, platform) => ` If the build already finished: mimi-seed deploy --skip-build --version-code ${versionCode} --platform ${platform}`,
1052
+ // cmdDeploy
1053
+ noAccount: "No account connected. Run `mimi-seed init`.",
1054
+ title: (platform) => `mimi-seed deploy \u2014 ${platform}`,
1055
+ dryRunNotice: " [dry-run] nothing will actually be deployed",
1056
+ ciLine: (kind) => ` CI: ${kind}`,
1057
+ noJenkinsConfig: "Jenkins is not configured. Run `mimi-seed deploy setup-jenkins`, or use --skip-build.",
1058
+ noJenkinsJob: (platform) => `No Jenkins job configured for ${platform}. Run setup-jenkins.`,
1059
+ jenkinsTrigger: (job) => `\u{1F528} Triggering Jenkins build: ${job}`,
1060
+ noQueueItem: " \u26A0 Could not read the queue item ID. The build may have started anyway.",
1061
+ queueItem: (id) => ` Queue item: ${id}`,
1062
+ waitingForBuildNumber: " Waiting for the build number...",
1063
+ noBuildNumber: " Could not read the build number. Retry with --skip-build + --version-code.",
1064
+ buildStarted: (n) => ` Build #${n} started. Waiting for it to finish...`,
1065
+ buildFailed: (result) => `Build failed: ${result}`,
1066
+ jenkinsLink: (url) => ` Jenkins: ${url}`,
1067
+ versionCodeFromBuild: (n) => ` versionCode = buildNumber (${n})`,
1068
+ noProviderConfig: (kind) => `${kind} is not configured. Run setup-${kind}.`,
1069
+ versionCodeUnsuitable: (kind, buildId) => `\u2717 No versionCode given (${kind} run_id ${buildId} is not usable as a versionCode)`,
1070
+ recommendation: " Recommended:",
1071
+ recommendationCi: " \u2022 Decide the versionCode inside the CI workflow and print it",
1072
+ recommendationNext: " \u2022 Then run: mimi-seed deploy --skip-build --version-code <N>",
1073
+ versionCodeUnknown: "Unknown versionCode. Pass it with --version-code <N>.",
1074
+ appLine: (name, id) => ` App: ${name} (${id})`,
1075
+ noAppId: "Could not resolve the appId. Pass --app <id>, or register the app with `mimi-seed init`.",
1076
+ // Production confirmation
1077
+ targetIos: "App Store review submission",
1078
+ targetAndroid: "Play Store production track",
1079
+ realDeploy: (platform, versionCode, target) => `\u26A0 Real deploy: ${platform} \xB7 versionCode ${versionCode} \u2192 ${target}`,
1080
+ confirmPrompt: "Continue? [y/N]: ",
1081
+ confirmCancelled: "Cancelled. (use --yes to skip this confirmation)",
1082
+ pipelineStarting: "\u{1F4E1} Starting the server deploy pipeline...",
1083
+ done: "Done. Check the deploy status in Play Console."
1084
+ }
1085
+ );
967
1086
  function jenkinsHeaders(cfg) {
968
1087
  const creds = Buffer.from(`${cfg.username || "admin"}:${cfg.token}`).toString("base64");
969
1088
  return { Authorization: `Basic ${creds}`, "Content-Type": "application/json" };
@@ -973,7 +1092,7 @@ async function triggerBuild(cfg, jobName, params) {
973
1092
  const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/buildWithParameters?${qs}`;
974
1093
  const res = await fetch(url, { method: "POST", headers: jenkinsHeaders(cfg) });
975
1094
  if (!res.ok) {
976
- throw new Error(`Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${res.status}: ${await res.text()}`);
1095
+ throw new Error(M2().jenkinsTriggerFailed(res.status, await res.text()));
977
1096
  }
978
1097
  const location = res.headers.get("Location") ?? "";
979
1098
  const match = location.match(/\/queue\/item\/(\d+)\//);
@@ -989,7 +1108,7 @@ async function getQueueBuildNumber(cfg, queueItemId) {
989
1108
  async function getBuildStatus(cfg, jobName, buildNumber) {
990
1109
  const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`;
991
1110
  const res = await fetch(url, { headers: jenkinsHeaders(cfg) });
992
- if (!res.ok) throw new Error(`\uBE4C\uB4DC \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328 ${res.status}`);
1111
+ if (!res.ok) throw new Error(M2().buildStatusFailed(res.status));
993
1112
  const data = await res.json();
994
1113
  return {
995
1114
  building: data.building ?? true,
@@ -1010,18 +1129,18 @@ async function pollBuildComplete(cfg, jobName, buildNumber, timeoutMs = 30 * 60
1010
1129
  consecutiveErrors = 0;
1011
1130
  } catch {
1012
1131
  consecutiveErrors++;
1013
- if (consecutiveErrors >= 3) throw new Error("Jenkins \uC5F0\uACB0 \uC624\uB958 3\uD68C \uC5F0\uC18D \u2014 \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uC138\uC694");
1014
- process.stdout.write(`\r \u26A0 Jenkins \uC5F0\uACB0 \uC624\uB958 (${consecutiveErrors}/3\uD68C), \uC7AC\uC2DC\uB3C4... `);
1132
+ if (consecutiveErrors >= 3) throw new Error(M2().jenkinsConnErrorFatal);
1133
+ process.stdout.write(`\r \u26A0 ${M2().jenkinsConnErrorRetry(consecutiveErrors)} `);
1015
1134
  continue;
1016
1135
  }
1017
1136
  dots = (dots + 1) % 4;
1018
- process.stdout.write(`\r \u23F3 \uBE4C\uB4DC \uC9C4\uD589 \uC911${".".repeat(dots + 1)} `);
1137
+ process.stdout.write(`\r \u23F3 ${M2().buildRunning}${".".repeat(dots + 1)} `);
1019
1138
  if (!status.building) {
1020
1139
  process.stdout.write("\n");
1021
1140
  return status.result ?? "FAILURE";
1022
1141
  }
1023
1142
  }
1024
- throw new Error("\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)");
1143
+ throw new Error(M2().buildTimeout);
1025
1144
  }
1026
1145
  async function streamDeploy(webBase, token, body) {
1027
1146
  const res = await fetch(`${webBase}/api/deploy`, {
@@ -1034,10 +1153,10 @@ async function streamDeploy(webBase, token, body) {
1034
1153
  });
1035
1154
  if (!res.ok) {
1036
1155
  const text = await res.text().catch(() => "");
1037
- throw new Error(`\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${res.status}: ${text.slice(0, 200)}`);
1156
+ throw new Error(M2().serverDeployFailed(res.status, text.slice(0, 200)));
1038
1157
  }
1039
1158
  const reader = res.body?.getReader();
1040
- if (!reader) throw new Error("SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
1159
+ if (!reader) throw new Error(M2().noSseStream);
1041
1160
  const decoder = new TextDecoder();
1042
1161
  let buf = "";
1043
1162
  while (true) {
@@ -1097,12 +1216,13 @@ async function promptGitProviderSetup(provider) {
1097
1216
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1098
1217
  const ask2 = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
1099
1218
  const isGh = provider === "github";
1100
- log(kleur.bold(isGh ? "GitHub Actions \uC124\uC815" : "GitLab CI \uC124\uC815"));
1101
- const tokenLabel = isGh ? " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): " : " GitLab Personal Access Token: ";
1219
+ const m = M2();
1220
+ log(kleur.bold(isGh ? m.githubSetupTitle : m.gitlabSetupTitle));
1221
+ const tokenLabel = isGh ? m.githubTokenPrompt : m.gitlabTokenPrompt;
1102
1222
  const token = await ask2(tokenLabel);
1103
- const owner = await ask2(isGh ? " Owner (org/user): " : " Namespace/group: ");
1104
- const repo = await ask2(" Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ");
1105
- const hostPrompt = isGh ? " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): " : " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ";
1223
+ const owner = await ask2(isGh ? m.githubOwnerPrompt : m.gitlabOwnerPrompt);
1224
+ const repo = await ask2(m.repoPrompt);
1225
+ const hostPrompt = isGh ? m.githubHostPrompt : m.gitlabHostPrompt;
1106
1226
  const host = await ask2(hostPrompt);
1107
1227
  rl.close();
1108
1228
  return {
@@ -1117,38 +1237,36 @@ function resolveCi(ciOption, jenkins, ciProvider) {
1117
1237
  if (ciOption !== "auto") return ciOption;
1118
1238
  if (jenkins?.url && jenkins.token) return "jenkins";
1119
1239
  if (ciProvider) return ciProvider.provider;
1120
- throw new Error(
1121
- "CI \uC124\uC815 \uC5C6\uC74C. \uB2E4\uC74C \uC911 \uD558\uB098 \uC2E4\uD589:\n \u2022 mimi-seed deploy setup-jenkins\n \u2022 mimi-seed deploy setup-github\n \u2022 mimi-seed deploy setup-gitlab"
1122
- );
1240
+ throw new Error(M2().noCiConfig);
1123
1241
  }
1124
1242
  async function runGitProviderBuild(cfg, args) {
1125
1243
  let runUrl = "";
1126
1244
  let runId;
1127
1245
  if (cfg.provider === "github") {
1128
1246
  if (!args.workflow) {
1129
- throw new Error("--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)");
1247
+ throw new Error(M2().workflowRequired);
1130
1248
  }
1131
- log(`\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${kleur.cyan(args.workflow)} @ ${args.ref}`);
1249
+ log(M2().ghTrigger(kleur.cyan(args.workflow), args.ref));
1132
1250
  const inputs = {};
1133
1251
  if (args.appId) inputs.MIMI_APP_ID = args.appId;
1134
1252
  inputs.PLATFORM = args.platform;
1135
1253
  const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
1136
1254
  if (!result2) {
1137
- throw new Error("GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.");
1255
+ throw new Error(M2().ghRunIdFailed);
1138
1256
  }
1139
1257
  runId = result2.runId;
1140
1258
  runUrl = result2.url;
1141
- log(kleur.dim(` Run ID: ${runId} \u2192 ${runUrl}`));
1259
+ log(kleur.dim(M2().ghRunId(runId, runUrl)));
1142
1260
  } else {
1143
- log(`\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${args.ref}`);
1261
+ log(M2().glTrigger(args.ref));
1144
1262
  const variables = { PLATFORM: args.platform };
1145
1263
  if (args.appId) variables.MIMI_APP_ID = args.appId;
1146
1264
  const result2 = await glTriggerPipeline(cfg, args.ref, variables);
1147
1265
  runId = result2.pipelineId;
1148
1266
  runUrl = result2.url;
1149
- log(kleur.dim(` Pipeline ID: ${runId} \u2192 ${runUrl}`));
1267
+ log(kleur.dim(M2().glPipelineId(runId, runUrl)));
1150
1268
  }
1151
- log(" \uC644\uB8CC \uB300\uAE30 \uC911...");
1269
+ log(M2().waitingForCompletion);
1152
1270
  let dots = 0;
1153
1271
  const onTick = (status) => {
1154
1272
  dots = (dots + 1) % 4;
@@ -1157,19 +1275,19 @@ async function runGitProviderBuild(cfg, args) {
1157
1275
  const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
1158
1276
  process.stdout.write("\n");
1159
1277
  if (result === "success") {
1160
- log(kleur.green(`\u2705 \uBE4C\uB4DC #${runId} \uC131\uACF5`));
1278
+ log(kleur.green(M2().buildSucceeded(runId)));
1161
1279
  return runId;
1162
1280
  }
1163
- log(kleur.red(`\uBE4C\uB4DC \uC885\uB8CC: ${result}`));
1281
+ log(kleur.red(M2().buildEnded(result)));
1164
1282
  log(kleur.dim(` ${runUrl}`));
1165
- log(kleur.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code <N> --platform ${args.platform}`));
1283
+ log(kleur.dim(M2().alreadyBuiltHint("<N>", args.platform)));
1166
1284
  process.exit(1);
1167
1285
  }
1168
1286
  async function cmdDeploy(argv) {
1169
1287
  const args = parseArgs(argv);
1170
1288
  const cfg = await getEffectiveConfig();
1171
1289
  if (!cfg) {
1172
- log(kleur.red("\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589."));
1290
+ log(kleur.red(M2().noAccount));
1173
1291
  process.exit(1);
1174
1292
  }
1175
1293
  if (args.setupJenkins) {
@@ -1180,17 +1298,17 @@ async function cmdDeploy(argv) {
1180
1298
  if (args.setupGithub) {
1181
1299
  const ciCfg = await promptGitProviderSetup("github");
1182
1300
  saveCiProviderConfig(ciCfg);
1183
- log(kleur.green(`\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1301
+ log(kleur.green(M2().githubSaved));
1184
1302
  return;
1185
1303
  }
1186
1304
  if (args.setupGitlab) {
1187
1305
  const ciCfg = await promptGitProviderSetup("gitlab");
1188
1306
  saveCiProviderConfig(ciCfg);
1189
- log(kleur.green(`\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1307
+ log(kleur.green(M2().gitlabSaved));
1190
1308
  return;
1191
1309
  }
1192
- log(kleur.bold(`mimi-seed deploy \u2014 ${args.platform}`));
1193
- if (args.dryRun) log(kleur.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
1310
+ log(kleur.bold(M2().title(args.platform)));
1311
+ if (args.dryRun) log(kleur.yellow(M2().dryRunNotice));
1194
1312
  log("");
1195
1313
  let versionCode = args.versionCode;
1196
1314
  if (!args.skipBuild) {
@@ -1198,30 +1316,30 @@ async function cmdDeploy(argv) {
1198
1316
  migrateLegacyJenkins();
1199
1317
  const jenkinsCfg = loadJenkinsConfig() ?? void 0;
1200
1318
  const kind = resolveCi(args.ci, jenkinsCfg, ciProvider);
1201
- log(kleur.dim(` CI: ${kind}`));
1319
+ log(kleur.dim(M2().ciLine(kind)));
1202
1320
  if (kind === "jenkins") {
1203
1321
  if (!jenkinsCfg?.url || !jenkinsCfg?.token) {
1204
- log(kleur.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
1322
+ log(kleur.yellow(M2().noJenkinsConfig));
1205
1323
  process.exit(1);
1206
1324
  }
1207
1325
  const jenkins = jenkinsCfg;
1208
1326
  const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1209
1327
  if (!jobName) {
1210
- log(kleur.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1328
+ log(kleur.red(M2().noJenkinsJob(args.platform)));
1211
1329
  process.exit(1);
1212
1330
  }
1213
- log(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur.cyan(jobName)}`);
1331
+ log(M2().jenkinsTrigger(kleur.cyan(jobName)));
1214
1332
  const buildParams = {};
1215
1333
  if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1216
1334
  const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1217
1335
  if (!queueItemId) {
1218
- log(kleur.yellow(" \u26A0 Queue item ID\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBE4C\uB4DC\uB294 \uC2DC\uC791\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
1336
+ log(kleur.yellow(M2().noQueueItem));
1219
1337
  } else {
1220
- log(kleur.dim(` Queue item: ${queueItemId}`));
1338
+ log(kleur.dim(M2().queueItem(queueItemId)));
1221
1339
  }
1222
1340
  let buildNumber = null;
1223
1341
  if (queueItemId) {
1224
- log(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1342
+ log(M2().waitingForBuildNumber);
1225
1343
  for (let i = 0; i < 6; i++) {
1226
1344
  await new Promise((r) => setTimeout(r, 5e3));
1227
1345
  buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
@@ -1229,77 +1347,77 @@ async function cmdDeploy(argv) {
1229
1347
  }
1230
1348
  }
1231
1349
  if (!buildNumber) {
1232
- log(kleur.yellow(" \uBE4C\uB4DC \uBC88\uD638\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. --skip-build + --version-code \uB85C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5."));
1350
+ log(kleur.yellow(M2().noBuildNumber));
1233
1351
  process.exit(1);
1234
1352
  }
1235
- log(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1353
+ log(M2().buildStarted(buildNumber));
1236
1354
  const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1237
1355
  if (result !== "SUCCESS") {
1238
- log(kleur.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
1239
- log(kleur.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
1240
- log(kleur.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
1356
+ log(kleur.red(M2().buildFailed(result)));
1357
+ log(kleur.dim(M2().jenkinsLink(`${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`)));
1358
+ log(kleur.dim(M2().alreadyBuiltHint(String(buildNumber), args.platform)));
1241
1359
  process.exit(1);
1242
1360
  }
1243
- log(kleur.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1361
+ log(kleur.green(M2().buildSucceeded(buildNumber)));
1244
1362
  if (!versionCode) {
1245
1363
  versionCode = buildNumber;
1246
- log(kleur.dim(` versionCode = buildNumber (${versionCode})`));
1364
+ log(kleur.dim(M2().versionCodeFromBuild(versionCode)));
1247
1365
  }
1248
1366
  } else {
1249
1367
  if (!ciProvider) {
1250
- log(kleur.red(`${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`));
1368
+ log(kleur.red(M2().noProviderConfig(kind)));
1251
1369
  process.exit(1);
1252
1370
  }
1253
1371
  const buildId = await runGitProviderBuild(ciProvider, args);
1254
1372
  if (!versionCode) {
1255
- log(kleur.red(`\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`));
1256
- log(kleur.dim(" \uAD8C\uC7A5 \uC0AC\uD56D:"));
1257
- log(kleur.dim(" \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825"));
1258
- log(kleur.dim(" \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>"));
1373
+ log(kleur.red(M2().versionCodeUnsuitable(kind, buildId)));
1374
+ log(kleur.dim(M2().recommendation));
1375
+ log(kleur.dim(M2().recommendationCi));
1376
+ log(kleur.dim(M2().recommendationNext));
1259
1377
  process.exit(1);
1260
1378
  }
1261
1379
  }
1262
1380
  }
1263
1381
  if (!versionCode) {
1264
- log(kleur.red("versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694."));
1382
+ log(kleur.red(M2().versionCodeUnknown));
1265
1383
  process.exit(1);
1266
1384
  }
1267
1385
  let appId = args.appId;
1268
1386
  if (!appId) {
1269
- const { mcpCall } = await import("./mcp-client-SLIKR42C.js");
1387
+ const { mcpCall } = await import("./mcp-client-PVWDCYJN.js");
1270
1388
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1271
1389
  if (!r.isError) {
1272
1390
  try {
1273
1391
  const apps = JSON.parse(r.text);
1274
1392
  if (apps.length > 0) {
1275
1393
  appId = apps[0].id;
1276
- log(kleur.dim(` \uC571: ${apps[0].name} (${appId})`));
1394
+ log(kleur.dim(M2().appLine(apps[0].name, appId)));
1277
1395
  }
1278
1396
  } catch {
1279
1397
  }
1280
1398
  }
1281
1399
  }
1282
1400
  if (!appId) {
1283
- log(kleur.red("appId\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --app <id> \uB85C \uC9C0\uC815\uD558\uAC70\uB098 `mimi-seed init` \uC73C\uB85C \uC571 \uB4F1\uB85D."));
1401
+ log(kleur.red(M2().noAppId));
1284
1402
  process.exit(1);
1285
1403
  }
1286
1404
  const needsConfirm = !args.dryRun && !args.yes && process.stdout.isTTY && !process.env.MIMI_SEED_TOKEN;
1287
1405
  if (needsConfirm) {
1288
- const target = args.platform === "ios" ? "App Store \uC2EC\uC0AC \uC81C\uCD9C" : "Play Store production \uD2B8\uB799";
1406
+ const target = args.platform === "ios" ? M2().targetIos : M2().targetAndroid;
1289
1407
  log("");
1290
- log(kleur.yellow(`\u26A0 \uC2E4\uC81C \uBC30\uD3EC: ${args.platform} \xB7 versionCode ${versionCode} \u2192 ${target}`));
1408
+ log(kleur.yellow(M2().realDeploy(args.platform, versionCode, target)));
1291
1409
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1292
1410
  const answer = await new Promise(
1293
- (resolve) => rl.question(kleur.bold("\uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694? [y/N]: "), (a) => resolve(a.trim().toLowerCase()))
1411
+ (resolve) => rl.question(kleur.bold(M2().confirmPrompt), (a) => resolve(a.trim().toLowerCase()))
1294
1412
  );
1295
1413
  rl.close();
1296
1414
  if (answer !== "y" && answer !== "yes") {
1297
- log(kleur.dim("\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)"));
1415
+ log(kleur.dim(M2().confirmCancelled));
1298
1416
  return;
1299
1417
  }
1300
1418
  }
1301
1419
  log("");
1302
- log("\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...");
1420
+ log(M2().pipelineStarting);
1303
1421
  log("");
1304
1422
  const deployBuildNumber = !args.skipBuild ? versionCode : void 0;
1305
1423
  await streamDeploy(cfg.webBase, cfg.token, {
@@ -1313,214 +1431,10 @@ async function cmdDeploy(argv) {
1313
1431
  dryRun: args.dryRun
1314
1432
  });
1315
1433
  log("");
1316
- log(kleur.bold("\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1434
+ log(kleur.bold(M2().done));
1317
1435
  log(kleur.dim(" https://play.google.com/console/developers"));
1318
1436
  }
1319
1437
 
1320
- // src/i18n.ts
1321
- var ko = {
1322
- common: {
1323
- yes: "y",
1324
- optional: "\uC120\uD0DD",
1325
- required: "\uD544\uC218",
1326
- skip: "\uAC74\uB108\uB700",
1327
- cancelled: "\uCDE8\uC18C\uB428",
1328
- unknownCommand: (cmd) => `\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`,
1329
- error: (msg) => `\uC624\uB958: ${msg}`,
1330
- checkWith: "\uC810\uAC80: mimi-seed doctor"
1331
- },
1332
- lang: {
1333
- ask: " \uC5B8\uC5B4\uB97C \uC120\uD0DD\uD574\uC918 [1] \uD55C\uAD6D\uC5B4 [2] English (\uC5D4\uD130=\uD55C\uAD6D\uC5B4): ",
1334
- saved: (l) => ` \u2705 \uC5B8\uC5B4: ${l === "ko" ? "\uD55C\uAD6D\uC5B4" : "English"} (\uB098\uC911\uC5D0 \uBC14\uAFB8\uAE30: mimi-seed lang en)`,
1335
- usage: `${"mimi-seed lang"} \u2014 CLI \uCD9C\uB825 \uC5B8\uC5B4
1336
-
1337
- mimi-seed lang \uD604\uC7AC \uC5B8\uC5B4 \uD45C\uC2DC
1338
- mimi-seed lang ko \uD55C\uAD6D\uC5B4
1339
- mimi-seed lang en English
1340
-
1341
- \uD658\uACBD\uBCC0\uC218 MIMI_SEED_LANG \uAC00 \uC788\uC73C\uBA74 \uADF8\uAC8C \uC6B0\uC120\uD569\uB2C8\uB2E4.`,
1342
- current: (l) => `\uD604\uC7AC \uC5B8\uC5B4: ${l === "ko" ? "\uD55C\uAD6D\uC5B4 (ko)" : "English (en)"}`,
1343
- invalid: (v) => `\uC54C \uC218 \uC5C6\uB294 \uC5B8\uC5B4: ${v} (ko \uB610\uB294 en)`
1344
- },
1345
- setup: {
1346
- title: "mimi-seed setup",
1347
- platformsDetected: (p) => ` \uAC10\uC9C0\uB41C \uD50C\uB7AB\uD3FC: ${p}`,
1348
- statusTitle: "\uC5F0\uACB0 \uC0C1\uD0DC",
1349
- statusDir: "(~/.mimi-seed)",
1350
- groupCore: "\uD575\uC2EC",
1351
- groupCi: "\uBE4C\uB4DC / CI",
1352
- groupMarketing: "\uB9C8\uCF00\uD305 \xB7 AI",
1353
- fallbackWorking: "\uD3F4\uBC31\uC73C\uB85C \uB3D9\uC791 \uC911",
1354
- missingRequired: " \uD544\uC218 \uD56D\uBAA9 \uB204\uB77D:",
1355
- cannotInteract: " \u2717 \uC774 \uC790\uACA9\uC99D\uBA85\uC740 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD574\uC11C \uC5EC\uAE30\uC11C\uB294 \uC124\uC815\uD560 \uC218 \uC5C6\uC5B4:",
1356
- cannotInteractHint: " \uD130\uBBF8\uB110\uC5D0\uC11C \uC2E4\uD589\uD574\uC918 (Git Bash \uB4F1 TTY \uBBF8\uAC10\uC9C0 \uD658\uACBD\uC774\uBA74 --interactive).",
1357
- runInTerminal: " \uB300\uD654\uD615\uC73C\uB85C \uC5F0\uACB0\uD558\uB824\uBA74 \uD130\uBBF8\uB110\uC5D0\uC11C: mimi-seed setup",
1358
- onlyAlreadyDone: " \u2705 \uC694\uCCAD\uD55C \uD56D\uBAA9\uC740 \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4.",
1359
- onlyReconnectHint: " \uB2E4\uC2DC \uC124\uC815\uD558\uB824\uBA74: mimi-seed setup --reconnect <id>",
1360
- allDone: " \u2705 \uC5F0\uACB0\uD560 \uAC8C \uB354 \uC5C6\uC5B4. \uB2E4 \uB410\uB2E4.",
1361
- planCount: (n) => ` ${n}\uAC1C \uD56D\uBAA9\uC744 \uC21C\uC11C\uB300\uB85C \uBB3C\uC5B4\uBCFC\uAC8C. \uC5B8\uC81C\uB4E0 s=\uAC74\uB108\uB6F0\uAE30, q=\uC885\uB8CC.`,
1362
- prompt: " [c] \uC5F0\uACB0 [s] \uAC74\uB108\uB6F0\uAE30 [?] \uC774\uAC74 \uC5B4\uB5BB\uAC8C \uAD6C\uD558\uB098\uC694 [q] \uC885\uB8CC : ",
1363
- promptInvalid: " c / s / ? / q \uC911\uC5D0\uC11C \uACE8\uB77C\uC918.",
1364
- quit: " \uC911\uB2E8\uD588\uC5B4. \uC774\uC5B4\uC11C \uD558\uB824\uBA74 \uB2E4\uC2DC: mimi-seed setup",
1365
- skipped: (fix) => ` \uAC74\uB108\uB700. \uB098\uC911\uC5D0: ${fix}`,
1366
- obtainTitle: (label) => ` ${label} \u2014 \uBBF8\uB9AC \uC900\uBE44\uD560 \uAC83`,
1367
- obtainMore: (anchor) => ` \uC790\uC138\uD788: docs/credentials.md#${anchor}`,
1368
- neededFor: (platform) => `(${platform} \uBC30\uD3EC\uC5D0 \uD544\uC694)`,
1369
- binFailed: (label, code, fix) => ` \u26A0 ${label} \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC5B4 (exit ${code}). \uB098\uC911\uC5D0 \uB2E4\uC2DC: ${fix}`,
1370
- verifying: " \u{1F50E} \uD1A0\uD070 \uAC80\uC99D \uC911...",
1371
- verifyFailed: (reason) => ` \u274C \uD1A0\uD070 \uAC80\uC99D \uC2E4\uD328: ${reason}`,
1372
- notSaved: (fix) => ` \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC5B4. \uB2E4\uC2DC: ${fix}`,
1373
- ciSaved: (label, who) => ` \u2705 ${label} \uC5F0\uACB0\uB428${who} \u2192 ~/.mimi-seed/ci.json`,
1374
- runSeparately: (cmd) => ` \uC774\uAC74 \uBCC4\uB3C4 \uBA85\uB839\uC73C\uB85C \uC2E4\uD589\uD574\uC918: ${cmd}`,
1375
- envVar: " \uD658\uACBD\uBCC0\uC218\uB85C \uC124\uC815\uD558\uB294 \uD56D\uBAA9\uC774\uC57C:",
1376
- pressEnter: " (\uC5D4\uD130\uB97C \uB204\uB974\uBA74 \uACC4\uC18D) ",
1377
- stillMissing: " \uC544\uC9C1 \uD544\uC218 \uD56D\uBAA9\uC774 \uB0A8\uC544 \uC788\uC5B4:",
1378
- requiredDone: " \u2705 \uD544\uC218 \uC5F0\uACB0 \uC644\uB8CC.",
1379
- nextSteps: " \uC810\uAC80: mimi-seed doctor \xB7 \uBC30\uD3EC: mimi-seed deploy"
1380
- },
1381
- doctor: {
1382
- title: "mimi-seed doctor",
1383
- secAuth: "\uC778\uC99D",
1384
- secCreds: "\uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 (~/.mimi-seed)",
1385
- secEnv: "\uB85C\uCEEC \uD658\uACBD",
1386
- secApps: "\uC571 \uAC10\uC9C0",
1387
- noToken: "Mimi Seed \uD1A0\uD070 \uC5C6\uC74C",
1388
- noTokenFix: "`mimi-seed init` \uC2E4\uD589 \uD544\uC694",
1389
- tokenSaved: "\uD1A0\uD070 \uC800\uC7A5\uB428",
1390
- endpoint: "\uC5D4\uB4DC\uD3EC\uC778\uD2B8",
1391
- ciMode: "CI \uBAA8\uB4DC",
1392
- ciModeDetail: "MIMI_SEED_TOKEN \uD658\uACBD\uBCC0\uC218 \uC0AC\uC6A9 \uC911",
1393
- tokenInvalid: "\uD1A0\uD070 \uAC80\uC99D \uC2E4\uD328",
1394
- serverOk: "Mimi Seed \uC11C\uBC84 \uC5F0\uACB0\uB428",
1395
- appCount: (n) => `\uC571 ${n}\uAC1C`,
1396
- unknownService: (id) => `${id} (\uC54C \uC218 \uC5C6\uB294 \uC11C\uBE44\uC2A4)`,
1397
- credsHint: " \uC804\uBD80 \uC5F0\uACB0\uD558\uAE30: mimi-seed setup \xB7 OAuth \uC2E0\uC120\uB3C4: mimi-seed auth status\n",
1398
- nodeTooOld: (v) => `${v} \u2014 v20 \uC774\uC0C1 \uD544\uC694 (.nvmrc \uCC38\uACE0)`,
1399
- gitRepo: "Git \uC800\uC7A5\uC18C",
1400
- gitTag: (t2) => `\uCD5C\uC2E0 \uD0DC\uADF8: ${t2}`,
1401
- gitCommits: (n) => `\uCEE4\uBC0B ${n}\uAC1C`,
1402
- noGit: "Git \uC800\uC7A5\uC18C \uC5C6\uC74C",
1403
- noGitDetail: "mimi-seed notes \uC0AC\uC6A9 \uBD88\uAC00",
1404
- noApp: "\uC571 \uAC10\uC9C0 \uC5C6\uC74C",
1405
- noAppDetail: "app.json / build.gradle / Info.plist \uC5C6\uC74C",
1406
- unnamed: "(\uC774\uB984 \uBBF8\uC0C1)",
1407
- requirements: (proj) => `${proj} \uC694\uAD6C\uC0AC\uD56D (.mimi-seed.json)`,
1408
- thisProject: "\uC774 \uD504\uB85C\uC81D\uD2B8"
1409
- },
1410
- auth: {
1411
- title: "mimi-seed auth \u2014 \uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC778\uC99D/\uAD00\uB9AC",
1412
- statusTitle: "\uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC0C1\uD0DC",
1413
- connectAll: "\n \uD55C \uBC88\uC5D0 \uC5F0\uACB0: mimi-seed setup",
1414
- unknownSub: (sub) => `\uC54C \uC218 \uC5C6\uB294 auth \uC11C\uBE0C\uBA85\uB839: ${sub}`,
1415
- npxFailed: (cmd, msg) => `
1416
- \u274C ${cmd} \uC2E4\uD589 \uC2E4\uD328: ${msg}
1417
- `
1418
- }
1419
- };
1420
- var en = {
1421
- common: {
1422
- yes: "y",
1423
- optional: "optional",
1424
- required: "required",
1425
- skip: "skipped",
1426
- cancelled: "Cancelled",
1427
- unknownCommand: (cmd) => `Unknown command: ${cmd}`,
1428
- error: (msg) => `Error: ${msg}`,
1429
- checkWith: "Check with: mimi-seed doctor"
1430
- },
1431
- lang: {
1432
- ask: " Choose a language [1] \uD55C\uAD6D\uC5B4 [2] English (Enter = \uD55C\uAD6D\uC5B4): ",
1433
- saved: (l) => ` \u2705 Language: ${l === "ko" ? "\uD55C\uAD6D\uC5B4" : "English"} (change later: mimi-seed lang ko)`,
1434
- usage: `mimi-seed lang \u2014 CLI output language
1435
-
1436
- mimi-seed lang show current language
1437
- mimi-seed lang ko \uD55C\uAD6D\uC5B4
1438
- mimi-seed lang en English
1439
-
1440
- MIMI_SEED_LANG takes precedence when set.`,
1441
- current: (l) => `Current language: ${l === "ko" ? "\uD55C\uAD6D\uC5B4 (ko)" : "English (en)"}`,
1442
- invalid: (v) => `Unknown language: ${v} (use ko or en)`
1443
- },
1444
- setup: {
1445
- title: "mimi-seed setup",
1446
- platformsDetected: (p) => ` Detected platforms: ${p}`,
1447
- statusTitle: "Connection status",
1448
- statusDir: "(~/.mimi-seed)",
1449
- groupCore: "Core",
1450
- groupCi: "Build / CI",
1451
- groupMarketing: "Marketing \xB7 AI",
1452
- fallbackWorking: "working via fallback",
1453
- missingRequired: " Missing required:",
1454
- cannotInteract: " \u2717 These need interactive input and cannot be set up here:",
1455
- cannotInteractHint: " Run it in a terminal (add --interactive if your shell hides the TTY, e.g. Git Bash).",
1456
- runInTerminal: " To connect interactively, run: mimi-seed setup",
1457
- onlyAlreadyDone: " \u2705 What you asked for is already connected.",
1458
- onlyReconnectHint: " To redo it: mimi-seed setup --reconnect <id>",
1459
- allDone: " \u2705 Nothing left to connect. You're set.",
1460
- planCount: (n) => ` I'll walk you through ${n} item(s). s = skip, q = quit, anytime.`,
1461
- prompt: " [c] connect [s] skip [?] how do I get this [q] quit : ",
1462
- promptInvalid: " Please choose c / s / ? / q.",
1463
- quit: " Stopped. To pick up where you left off: mimi-seed setup",
1464
- skipped: (fix) => ` Skipped. Later: ${fix}`,
1465
- obtainTitle: (label) => ` ${label} \u2014 what to get first`,
1466
- obtainMore: (anchor) => ` Details: docs/credentials.md#${anchor}`,
1467
- neededFor: (platform) => `(needed to ship to ${platform})`,
1468
- binFailed: (label, code, fix) => ` \u26A0 ${label} was not completed (exit ${code}). Try again later: ${fix}`,
1469
- verifying: " \u{1F50E} Verifying token...",
1470
- verifyFailed: (reason) => ` \u274C Token verification failed: ${reason}`,
1471
- notSaved: (fix) => ` Nothing was saved. Retry: ${fix}`,
1472
- ciSaved: (label, who) => ` \u2705 ${label} connected${who} \u2192 ~/.mimi-seed/ci.json`,
1473
- runSeparately: (cmd) => ` Run this one separately: ${cmd}`,
1474
- envVar: " This one is set through an environment variable:",
1475
- pressEnter: " (press Enter to continue) ",
1476
- stillMissing: " Still missing, and required:",
1477
- requiredDone: " \u2705 All required credentials connected.",
1478
- nextSteps: " Check: mimi-seed doctor \xB7 Ship: mimi-seed deploy"
1479
- },
1480
- doctor: {
1481
- title: "mimi-seed doctor",
1482
- secAuth: "Account",
1483
- secCreds: "Local credentials (~/.mimi-seed)",
1484
- secEnv: "Environment",
1485
- secApps: "App detection",
1486
- noToken: "No Mimi Seed token",
1487
- noTokenFix: "run `mimi-seed init`",
1488
- tokenSaved: "Token stored",
1489
- endpoint: "Endpoint",
1490
- ciMode: "CI mode",
1491
- ciModeDetail: "using MIMI_SEED_TOKEN",
1492
- tokenInvalid: "Token rejected",
1493
- serverOk: "Connected to Mimi Seed",
1494
- appCount: (n) => `${n} app(s)`,
1495
- unknownService: (id) => `${id} (unknown service)`,
1496
- credsHint: " Connect everything: mimi-seed setup \xB7 OAuth freshness: mimi-seed auth status\n",
1497
- nodeTooOld: (v) => `${v} \u2014 v20+ required (see .nvmrc)`,
1498
- gitRepo: "Git repository",
1499
- gitTag: (t2) => `latest tag: ${t2}`,
1500
- gitCommits: (n) => `${n} commit(s)`,
1501
- noGit: "Not a git repository",
1502
- noGitDetail: "mimi-seed notes is unavailable",
1503
- noApp: "No app detected",
1504
- noAppDetail: "no app.json / build.gradle / Info.plist",
1505
- unnamed: "(unnamed)",
1506
- requirements: (proj) => `${proj} requirements (.mimi-seed.json)`,
1507
- thisProject: "This project"
1508
- },
1509
- auth: {
1510
- title: "mimi-seed auth \u2014 local credential setup",
1511
- statusTitle: "Local credential status",
1512
- connectAll: "\n Connect everything at once: mimi-seed setup",
1513
- unknownSub: (sub) => `Unknown auth subcommand: ${sub}`,
1514
- npxFailed: (cmd, msg) => `
1515
- \u274C Failed to run ${cmd}: ${msg}
1516
- `
1517
- }
1518
- };
1519
- var CATALOGS = { ko, en };
1520
- function t() {
1521
- return CATALOGS[resolveLang()];
1522
- }
1523
-
1524
1438
  // src/setup.ts
1525
1439
  function log2(msg = "") {
1526
1440
  process.stdout.write(msg + "\n");
@@ -1651,7 +1565,7 @@ async function connectOne(spec) {
1651
1565
  }
1652
1566
  async function cmdSetup(argv) {
1653
1567
  const opts = parseSetupArgs(argv);
1654
- const home = os6.homedir();
1568
+ const home = os5.homedir();
1655
1569
  migrateLegacyJenkins(home);
1656
1570
  if (resolveMode(opts, process.env, process.stdin.isTTY) === "interactive") {
1657
1571
  await ensureLangChosen();
@@ -1756,9 +1670,6 @@ export {
1756
1670
  deleteConfig,
1757
1671
  CONFIG_LOCATION,
1758
1672
  getEffectiveConfig,
1759
- writeSettings,
1760
- isLang,
1761
- resolveLang,
1762
1673
  credLabel,
1763
1674
  credNote,
1764
1675
  CREDENTIALS,
@@ -1766,7 +1677,6 @@ export {
1766
1677
  detectAll,
1767
1678
  isSatisfied,
1768
1679
  migrateLegacyJenkins,
1769
- t,
1770
1680
  MCP_PKG,
1771
1681
  runMcpBin,
1772
1682
  cmdDeploy,