mimi-seed 0.6.0 → 0.7.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) {
@@ -517,9 +487,7 @@ async function runMcpBin(bin, extraArgs = []) {
517
487
  env: { ...process.env, MIMI_SEED_LANG: resolveLang() }
518
488
  });
519
489
  child.on("error", (e) => {
520
- process.stderr.write(`
521
- \u274C ${cmd} \uC2E4\uD589 \uC2E4\uD328: ${e.message}
522
- `);
490
+ process.stderr.write(t().auth.npxFailed(cmd, e.message));
523
491
  resolve(1);
524
492
  });
525
493
  child.on("exit", (code) => resolve(code ?? 0));
@@ -527,28 +495,28 @@ async function runMcpBin(bin, extraArgs = []) {
527
495
  }
528
496
 
529
497
  // 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()) {
498
+ import fs2 from "fs";
499
+ import os2 from "os";
500
+ import path2 from "path";
501
+ var CONFIG_DIR = path2.join(os2.homedir(), ".mimi-seed");
502
+ var JENKINS_PATH = path2.join(CONFIG_DIR, "jenkins.json");
503
+ var LEGACY_PATH = path2.join(CONFIG_DIR, "config.json");
504
+ function loadJenkinsConfig(home = os2.homedir()) {
537
505
  try {
538
- const p = path3.join(home, ".mimi-seed", "jenkins.json");
539
- return JSON.parse(fs3.readFileSync(p, "utf-8"));
506
+ const p = path2.join(home, ".mimi-seed", "jenkins.json");
507
+ return JSON.parse(fs2.readFileSync(p, "utf-8"));
540
508
  } catch {
541
509
  return null;
542
510
  }
543
511
  }
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;
512
+ function migrateLegacyJenkins(home = os2.homedir()) {
513
+ const dir = path2.join(home, ".mimi-seed");
514
+ const jenkinsPath = path2.join(dir, "jenkins.json");
515
+ const legacyPath = path2.join(dir, "config.json");
516
+ if (fs2.existsSync(jenkinsPath)) return false;
549
517
  let legacy;
550
518
  try {
551
- legacy = JSON.parse(fs3.readFileSync(legacyPath, "utf-8"));
519
+ legacy = JSON.parse(fs2.readFileSync(legacyPath, "utf-8"));
552
520
  } catch {
553
521
  return false;
554
522
  }
@@ -562,28 +530,28 @@ function migrateLegacyJenkins(home = os3.homedir()) {
562
530
  ...j.jobAndroid ? { jobAndroid: j.jobAndroid } : {},
563
531
  ...j.jobIos ? { jobIos: j.jobIos } : {}
564
532
  };
565
- fs3.mkdirSync(dir, { recursive: true, mode: 448 });
566
- fs3.writeFileSync(jenkinsPath, JSON.stringify(migrated, null, 2), { mode: 384 });
533
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
534
+ fs2.writeFileSync(jenkinsPath, JSON.stringify(migrated, null, 2), { mode: 384 });
567
535
  delete legacy.jenkins;
568
536
  const tmp = `${legacyPath}.tmp`;
569
- fs3.writeFileSync(tmp, JSON.stringify(legacy, null, 2), { mode: 384 });
570
- fs3.renameSync(tmp, legacyPath);
537
+ fs2.writeFileSync(tmp, JSON.stringify(legacy, null, 2), { mode: 384 });
538
+ fs2.renameSync(tmp, legacyPath);
571
539
  return true;
572
540
  }
573
541
 
574
542
  // src/detect.ts
575
- import fs4 from "fs/promises";
576
- import path4 from "path";
543
+ import fs3 from "fs/promises";
544
+ import path3 from "path";
577
545
  async function readIfExists(p) {
578
546
  try {
579
- return await fs4.readFile(p, "utf8");
547
+ return await fs3.readFile(p, "utf8");
580
548
  } catch {
581
549
  return null;
582
550
  }
583
551
  }
584
552
  async function pathExists(p) {
585
553
  try {
586
- await fs4.access(p);
554
+ await fs3.access(p);
587
555
  return true;
588
556
  } catch {
589
557
  return false;
@@ -605,16 +573,16 @@ async function walk(root, match, maxDepth = 5) {
605
573
  if (depth > maxDepth) return;
606
574
  let entries;
607
575
  try {
608
- entries = await fs4.readdir(dir, { withFileTypes: true });
576
+ entries = await fs3.readdir(dir, { withFileTypes: true });
609
577
  } catch {
610
578
  return;
611
579
  }
612
580
  for (const e of entries) {
613
581
  if (e.isDirectory()) {
614
582
  if (skipDirs.has(e.name)) continue;
615
- await visit(path4.join(dir, e.name), depth + 1);
583
+ await visit(path3.join(dir, e.name), depth + 1);
616
584
  } else if (e.isFile() && match(e.name)) {
617
- found.push(path4.join(dir, e.name));
585
+ found.push(path3.join(dir, e.name));
618
586
  }
619
587
  }
620
588
  }
@@ -624,7 +592,7 @@ async function walk(root, match, maxDepth = 5) {
624
592
  async function detectHints(cwd) {
625
593
  const hints = [];
626
594
  for (const fname of ["app.json", "app.config.json"]) {
627
- const txt = await readIfExists(path4.join(cwd, fname));
595
+ const txt = await readIfExists(path3.join(cwd, fname));
628
596
  if (!txt) continue;
629
597
  try {
630
598
  const json = JSON.parse(txt);
@@ -655,7 +623,7 @@ async function detectHints(cwd) {
655
623
  if (m?.[1]) {
656
624
  const pkg = m[1];
657
625
  if (!hints.some((h) => h.packageName === pkg)) {
658
- hints.push({ packageName: pkg, source: [path4.relative(cwd, f)] });
626
+ hints.push({ packageName: pkg, source: [path3.relative(cwd, f)] });
659
627
  }
660
628
  }
661
629
  }
@@ -670,7 +638,7 @@ async function detectHints(cwd) {
670
638
  let bid = m[1];
671
639
  if (bid.includes("$(PRODUCT_BUNDLE_IDENTIFIER)")) continue;
672
640
  if (!hints.some((h) => h.bundleId === bid)) {
673
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
641
+ hints.push({ bundleId: bid, source: [path3.relative(cwd, f)] });
674
642
  }
675
643
  }
676
644
  }
@@ -683,11 +651,11 @@ async function detectHints(cwd) {
683
651
  const bid = m[1].trim().replace(/^["']|["']$/g, "");
684
652
  if (!bid || bid.includes("$")) continue;
685
653
  if (!hints.some((h) => h.bundleId === bid)) {
686
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
654
+ hints.push({ bundleId: bid, source: [path3.relative(cwd, f)] });
687
655
  }
688
656
  }
689
657
  }
690
- const pkgJson = await readIfExists(path4.join(cwd, "package.json"));
658
+ const pkgJson = await readIfExists(path3.join(cwd, "package.json"));
691
659
  if (pkgJson) {
692
660
  try {
693
661
  const json = JSON.parse(pkgJson);
@@ -723,7 +691,7 @@ async function detectHints(cwd) {
723
691
  return merged.filter((h) => h.packageName || h.bundleId);
724
692
  }
725
693
  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"));
694
+ 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
695
  }
728
696
 
729
697
  // src/deploy.ts
@@ -731,28 +699,28 @@ import kleur from "kleur";
731
699
  import * as readline from "readline";
732
700
 
733
701
  // 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");
702
+ import fs4 from "fs/promises";
703
+ import path4 from "path";
704
+ import os3 from "os";
705
+ var CONFIG_DIR2 = path4.join(os3.homedir(), ".mimi-seed");
706
+ var CONFIG_PATH = path4.join(CONFIG_DIR2, "config.json");
739
707
  async function readConfig() {
740
708
  try {
741
- const txt = await fs5.readFile(CONFIG_PATH, "utf8");
709
+ const txt = await fs4.readFile(CONFIG_PATH, "utf8");
742
710
  return JSON.parse(txt);
743
711
  } catch {
744
712
  return null;
745
713
  }
746
714
  }
747
715
  async function writeConfig(cfg) {
748
- await fs5.mkdir(CONFIG_DIR2, { recursive: true });
749
- await fs5.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
716
+ await fs4.mkdir(CONFIG_DIR2, { recursive: true });
717
+ await fs4.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
750
718
  if (process.platform !== "win32") {
751
- await fs5.chmod(CONFIG_PATH, 384);
719
+ await fs4.chmod(CONFIG_PATH, 384);
752
720
  }
753
721
  }
754
722
  async function deleteConfig() {
755
- await fs5.rm(CONFIG_PATH, { force: true });
723
+ await fs4.rm(CONFIG_PATH, { force: true });
756
724
  }
757
725
  var CONFIG_LOCATION = CONFIG_PATH;
758
726
  async function getEffectiveConfig() {
@@ -771,13 +739,28 @@ async function getEffectiveConfig() {
771
739
  }
772
740
 
773
741
  // 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");
742
+ import fs5 from "fs";
743
+ import path5 from "path";
744
+ import os4 from "os";
745
+ var CI_CONFIG_PATH = path5.join(os4.homedir(), ".mimi-seed", "ci.json");
746
+ var M = catalog(
747
+ {
748
+ badToken: (provider, status) => `${provider} ${status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544`,
749
+ 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.`,
750
+ pollFailed: (provider) => `${provider} API \uC5F0\uC18D \uC624\uB958 3\uD68C`,
751
+ pollFailedHttp: (provider, status) => `${provider} API \uC5F0\uC18D \uC624\uB958 (HTTP ${status})`
752
+ },
753
+ {
754
+ badToken: (provider, status) => `${provider} ${status} \u2014 the token is not valid`,
755
+ noScope: (scopes) => `The token is missing the \`workflow\` scope (currently: ${scopes || "none"}). Triggering a workflow will be blocked with a 403.`,
756
+ // 프로바이더 이름("GitHub API")은 두 언어 모두에 남는다 — 테스트가 그걸로 단언한다.
757
+ pollFailed: (provider) => `${provider} API failed 3 times in a row`,
758
+ pollFailedHttp: (provider, status) => `${provider} API failed 3 times in a row (HTTP ${status})`
759
+ }
760
+ );
778
761
  function loadCiProviderConfig() {
779
762
  try {
780
- const cfg = JSON.parse(fs6.readFileSync(CI_CONFIG_PATH, "utf-8"));
763
+ const cfg = JSON.parse(fs5.readFileSync(CI_CONFIG_PATH, "utf-8"));
781
764
  const host = normalizeHost(cfg.host);
782
765
  return host ? { ...cfg, host } : { ...cfg, host: void 0 };
783
766
  } catch {
@@ -791,15 +774,15 @@ function normalizeHost(host) {
791
774
  return withScheme.replace(/\/+$/, "");
792
775
  }
793
776
  function saveCiProviderConfig(cfg) {
794
- const dir = path6.dirname(CI_CONFIG_PATH);
795
- if (!fs6.existsSync(dir)) {
796
- fs6.mkdirSync(dir, { recursive: true, mode: 448 });
777
+ const dir = path5.dirname(CI_CONFIG_PATH);
778
+ if (!fs5.existsSync(dir)) {
779
+ fs5.mkdirSync(dir, { recursive: true, mode: 448 });
797
780
  }
798
781
  const normalized = { ...cfg, host: normalizeHost(cfg.host) };
799
782
  if (!normalized.host) delete normalized.host;
800
- fs6.writeFileSync(CI_CONFIG_PATH, JSON.stringify(normalized, null, 2));
783
+ fs5.writeFileSync(CI_CONFIG_PATH, JSON.stringify(normalized, null, 2));
801
784
  if (process.platform !== "win32") {
802
- fs6.chmodSync(CI_CONFIG_PATH, 384);
785
+ fs5.chmodSync(CI_CONFIG_PATH, 384);
803
786
  }
804
787
  }
805
788
  async function verifyCiToken(cfg) {
@@ -810,21 +793,18 @@ async function verifyCiToken(cfg) {
810
793
  headers: { Authorization: `Bearer ${probe.token}`, Accept: "application/vnd.github+json" }
811
794
  });
812
795
  if (!res2.ok) {
813
- return { ok: false, reason: `GitHub ${res2.status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544` };
796
+ return { ok: false, reason: M().badToken("GitHub", res2.status) };
814
797
  }
815
798
  const user2 = await res2.json();
816
799
  const scopes = res2.headers.get("x-oauth-scopes");
817
800
  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
- };
801
+ return { ok: false, reason: M().noScope(scopes) };
822
802
  }
823
803
  return { ok: true, login: user2.login };
824
804
  }
825
805
  const res = await fetch(`${glBase(probe)}/user`, { headers: { "PRIVATE-TOKEN": probe.token } });
826
806
  if (!res.ok) {
827
- return { ok: false, reason: `GitLab ${res.status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544` };
807
+ return { ok: false, reason: M().badToken("GitLab", res.status) };
828
808
  }
829
809
  const user = await res.json();
830
810
  return { ok: true, login: user.username };
@@ -882,12 +862,12 @@ async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3, interval
882
862
  );
883
863
  } catch {
884
864
  consecutiveErrors++;
885
- if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
865
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailed("GitHub"));
886
866
  continue;
887
867
  }
888
868
  if (!res.ok) {
889
869
  consecutiveErrors++;
890
- if (consecutiveErrors >= 3) throw new Error(`GitHub API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
870
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailedHttp("GitHub", res.status));
891
871
  continue;
892
872
  }
893
873
  consecutiveErrors = 0;
@@ -933,12 +913,12 @@ async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3
933
913
  );
934
914
  } catch {
935
915
  consecutiveErrors++;
936
- if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
916
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailed("GitLab"));
937
917
  continue;
938
918
  }
939
919
  if (!res.ok) {
940
920
  consecutiveErrors++;
941
- if (consecutiveErrors >= 3) throw new Error(`GitLab API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
921
+ if (consecutiveErrors >= 3) throw new Error(M().pollFailedHttp("GitLab", res.status));
942
922
  continue;
943
923
  }
944
924
  consecutiveErrors = 0;
@@ -964,6 +944,144 @@ var PHASE_ICON = {
964
944
  function log(msg) {
965
945
  process.stdout.write(msg + "\n");
966
946
  }
947
+ var M2 = catalog(
948
+ {
949
+ // Jenkins / 빌드
950
+ jenkinsTriggerFailed: (status, body) => `Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${status}: ${body}`,
951
+ buildStatusFailed: (status) => `\uBE4C\uB4DC \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328 ${status}`,
952
+ jenkinsConnErrorFatal: "Jenkins \uC5F0\uACB0 \uC624\uB958 3\uD68C \uC5F0\uC18D \u2014 \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uC138\uC694",
953
+ jenkinsConnErrorRetry: (n) => `Jenkins \uC5F0\uACB0 \uC624\uB958 (${n}/3\uD68C), \uC7AC\uC2DC\uB3C4...`,
954
+ buildRunning: "\uBE4C\uB4DC \uC9C4\uD589 \uC911",
955
+ buildTimeout: "\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)",
956
+ // 서버 스트림
957
+ serverDeployFailed: (status, body) => `\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${status}: ${body}`,
958
+ noSseStream: "SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C",
959
+ // CI provider 설정 프롬프트
960
+ githubSetupTitle: "GitHub Actions \uC124\uC815",
961
+ gitlabSetupTitle: "GitLab CI \uC124\uC815",
962
+ githubTokenPrompt: " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): ",
963
+ gitlabTokenPrompt: " GitLab Personal Access Token: ",
964
+ githubOwnerPrompt: " Owner (org/user): ",
965
+ gitlabOwnerPrompt: " Namespace/group: ",
966
+ repoPrompt: " Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ",
967
+ githubHostPrompt: " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): ",
968
+ gitlabHostPrompt: " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ",
969
+ githubSaved: "\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json",
970
+ gitlabSaved: "\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json",
971
+ 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",
972
+ // GitHub / GitLab 빌드
973
+ workflowRequired: "--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)",
974
+ ghTrigger: (workflow, ref) => `\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${workflow} @ ${ref}`,
975
+ ghRunIdFailed: "GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.",
976
+ ghRunId: (id, url) => ` Run ID: ${id} \u2192 ${url}`,
977
+ glTrigger: (ref) => `\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${ref}`,
978
+ glPipelineId: (id, url) => ` Pipeline ID: ${id} \u2192 ${url}`,
979
+ waitingForCompletion: " \uC644\uB8CC \uB300\uAE30 \uC911...",
980
+ buildSucceeded: (n) => `\u2705 \uBE4C\uB4DC #${n} \uC131\uACF5`,
981
+ buildEnded: (result) => `\uBE4C\uB4DC \uC885\uB8CC: ${result}`,
982
+ alreadyBuiltHint: (versionCode, platform) => ` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${versionCode} --platform ${platform}`,
983
+ // cmdDeploy
984
+ noAccount: "\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589.",
985
+ title: (platform) => `mimi-seed deploy \u2014 ${platform}`,
986
+ dryRunNotice: " [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
987
+ ciLine: (kind) => ` CI: ${kind}`,
988
+ noJenkinsConfig: "Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9.",
989
+ noJenkinsJob: (platform) => `${platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`,
990
+ jenkinsTrigger: (job) => `\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${job}`,
991
+ 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.",
992
+ queueItem: (id) => ` Queue item: ${id}`,
993
+ waitingForBuildNumber: " \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...",
994
+ 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.",
995
+ buildStarted: (n) => ` \uBE4C\uB4DC #${n} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`,
996
+ buildFailed: (result) => `\uBE4C\uB4DC \uC2E4\uD328: ${result}`,
997
+ jenkinsLink: (url) => ` Jenkins: ${url}`,
998
+ versionCodeFromBuild: (n) => ` versionCode = buildNumber (${n})`,
999
+ noProviderConfig: (kind) => `${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`,
1000
+ versionCodeUnsuitable: (kind, buildId) => `\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`,
1001
+ recommendation: " \uAD8C\uC7A5 \uC0AC\uD56D:",
1002
+ recommendationCi: " \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825",
1003
+ recommendationNext: " \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>",
1004
+ versionCodeUnknown: "versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694.",
1005
+ appLine: (name, id) => ` \uC571: ${name} (${id})`,
1006
+ 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.",
1007
+ // 프로덕션 확인
1008
+ targetIos: "App Store \uC2EC\uC0AC \uC81C\uCD9C",
1009
+ targetAndroid: "Play Store production \uD2B8\uB799",
1010
+ realDeploy: (platform, versionCode, target) => `\u26A0 \uC2E4\uC81C \uBC30\uD3EC: ${platform} \xB7 versionCode ${versionCode} \u2192 ${target}`,
1011
+ confirmPrompt: "\uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694? [y/N]: ",
1012
+ confirmCancelled: "\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)",
1013
+ pipelineStarting: "\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...",
1014
+ done: "\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."
1015
+ },
1016
+ {
1017
+ // Jenkins / build
1018
+ jenkinsTriggerFailed: (status, body) => `Jenkins trigger failed ${status}: ${body}`,
1019
+ buildStatusFailed: (status) => `Failed to fetch build status ${status}`,
1020
+ jenkinsConnErrorFatal: "Jenkins connection failed 3 times in a row \u2014 check your network",
1021
+ jenkinsConnErrorRetry: (n) => `Jenkins connection error (${n}/3), retrying...`,
1022
+ buildRunning: "Build running",
1023
+ buildTimeout: "Build timed out (30 min)",
1024
+ // Server stream
1025
+ serverDeployFailed: (status, body) => `Server deploy failed ${status}: ${body}`,
1026
+ noSseStream: "No SSE stream",
1027
+ // CI provider setup prompts
1028
+ githubSetupTitle: "GitHub Actions setup",
1029
+ gitlabSetupTitle: "GitLab CI setup",
1030
+ githubTokenPrompt: " GitHub Personal Access Token (repo+workflow scopes): ",
1031
+ gitlabTokenPrompt: " GitLab Personal Access Token: ",
1032
+ githubOwnerPrompt: " Owner (org/user): ",
1033
+ gitlabOwnerPrompt: " Namespace/group: ",
1034
+ repoPrompt: " Repo name (without the path): ",
1035
+ githubHostPrompt: " GitHub Enterprise host (optional, Enter = github.com): ",
1036
+ gitlabHostPrompt: " GitLab self-hosted URL (optional, Enter = gitlab.com): ",
1037
+ githubSaved: "\u2705 GitHub Actions config saved \u2192 ~/.mimi-seed/ci.json",
1038
+ gitlabSaved: "\u2705 GitLab CI config saved \u2192 ~/.mimi-seed/ci.json",
1039
+ 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",
1040
+ // GitHub / GitLab build
1041
+ workflowRequired: "--workflow is required (e.g. --workflow deploy.yml)",
1042
+ ghTrigger: (workflow, ref) => `\u{1F528} Triggering GitHub Actions: ${workflow} @ ${ref}`,
1043
+ ghRunIdFailed: "Could not resolve the GitHub Actions run_id. Check with ci_list_recent_builds in a moment.",
1044
+ ghRunId: (id, url) => ` Run ID: ${id} \u2192 ${url}`,
1045
+ glTrigger: (ref) => `\u{1F528} Triggering GitLab Pipeline: ${ref}`,
1046
+ glPipelineId: (id, url) => ` Pipeline ID: ${id} \u2192 ${url}`,
1047
+ waitingForCompletion: " Waiting for it to finish...",
1048
+ buildSucceeded: (n) => `\u2705 Build #${n} succeeded`,
1049
+ buildEnded: (result) => `Build ended: ${result}`,
1050
+ alreadyBuiltHint: (versionCode, platform) => ` If the build already finished: mimi-seed deploy --skip-build --version-code ${versionCode} --platform ${platform}`,
1051
+ // cmdDeploy
1052
+ noAccount: "No account connected. Run `mimi-seed init`.",
1053
+ title: (platform) => `mimi-seed deploy \u2014 ${platform}`,
1054
+ dryRunNotice: " [dry-run] nothing will actually be deployed",
1055
+ ciLine: (kind) => ` CI: ${kind}`,
1056
+ noJenkinsConfig: "Jenkins is not configured. Run `mimi-seed deploy setup-jenkins`, or use --skip-build.",
1057
+ noJenkinsJob: (platform) => `No Jenkins job configured for ${platform}. Run setup-jenkins.`,
1058
+ jenkinsTrigger: (job) => `\u{1F528} Triggering Jenkins build: ${job}`,
1059
+ noQueueItem: " \u26A0 Could not read the queue item ID. The build may have started anyway.",
1060
+ queueItem: (id) => ` Queue item: ${id}`,
1061
+ waitingForBuildNumber: " Waiting for the build number...",
1062
+ noBuildNumber: " Could not read the build number. Retry with --skip-build + --version-code.",
1063
+ buildStarted: (n) => ` Build #${n} started. Waiting for it to finish...`,
1064
+ buildFailed: (result) => `Build failed: ${result}`,
1065
+ jenkinsLink: (url) => ` Jenkins: ${url}`,
1066
+ versionCodeFromBuild: (n) => ` versionCode = buildNumber (${n})`,
1067
+ noProviderConfig: (kind) => `${kind} is not configured. Run setup-${kind}.`,
1068
+ versionCodeUnsuitable: (kind, buildId) => `\u2717 No versionCode given (${kind} run_id ${buildId} is not usable as a versionCode)`,
1069
+ recommendation: " Recommended:",
1070
+ recommendationCi: " \u2022 Decide the versionCode inside the CI workflow and print it",
1071
+ recommendationNext: " \u2022 Then run: mimi-seed deploy --skip-build --version-code <N>",
1072
+ versionCodeUnknown: "Unknown versionCode. Pass it with --version-code <N>.",
1073
+ appLine: (name, id) => ` App: ${name} (${id})`,
1074
+ noAppId: "Could not resolve the appId. Pass --app <id>, or register the app with `mimi-seed init`.",
1075
+ // Production confirmation
1076
+ targetIos: "App Store review submission",
1077
+ targetAndroid: "Play Store production track",
1078
+ realDeploy: (platform, versionCode, target) => `\u26A0 Real deploy: ${platform} \xB7 versionCode ${versionCode} \u2192 ${target}`,
1079
+ confirmPrompt: "Continue? [y/N]: ",
1080
+ confirmCancelled: "Cancelled. (use --yes to skip this confirmation)",
1081
+ pipelineStarting: "\u{1F4E1} Starting the server deploy pipeline...",
1082
+ done: "Done. Check the deploy status in Play Console."
1083
+ }
1084
+ );
967
1085
  function jenkinsHeaders(cfg) {
968
1086
  const creds = Buffer.from(`${cfg.username || "admin"}:${cfg.token}`).toString("base64");
969
1087
  return { Authorization: `Basic ${creds}`, "Content-Type": "application/json" };
@@ -973,7 +1091,7 @@ async function triggerBuild(cfg, jobName, params) {
973
1091
  const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/buildWithParameters?${qs}`;
974
1092
  const res = await fetch(url, { method: "POST", headers: jenkinsHeaders(cfg) });
975
1093
  if (!res.ok) {
976
- throw new Error(`Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${res.status}: ${await res.text()}`);
1094
+ throw new Error(M2().jenkinsTriggerFailed(res.status, await res.text()));
977
1095
  }
978
1096
  const location = res.headers.get("Location") ?? "";
979
1097
  const match = location.match(/\/queue\/item\/(\d+)\//);
@@ -989,7 +1107,7 @@ async function getQueueBuildNumber(cfg, queueItemId) {
989
1107
  async function getBuildStatus(cfg, jobName, buildNumber) {
990
1108
  const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`;
991
1109
  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}`);
1110
+ if (!res.ok) throw new Error(M2().buildStatusFailed(res.status));
993
1111
  const data = await res.json();
994
1112
  return {
995
1113
  building: data.building ?? true,
@@ -1010,18 +1128,18 @@ async function pollBuildComplete(cfg, jobName, buildNumber, timeoutMs = 30 * 60
1010
1128
  consecutiveErrors = 0;
1011
1129
  } catch {
1012
1130
  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... `);
1131
+ if (consecutiveErrors >= 3) throw new Error(M2().jenkinsConnErrorFatal);
1132
+ process.stdout.write(`\r \u26A0 ${M2().jenkinsConnErrorRetry(consecutiveErrors)} `);
1015
1133
  continue;
1016
1134
  }
1017
1135
  dots = (dots + 1) % 4;
1018
- process.stdout.write(`\r \u23F3 \uBE4C\uB4DC \uC9C4\uD589 \uC911${".".repeat(dots + 1)} `);
1136
+ process.stdout.write(`\r \u23F3 ${M2().buildRunning}${".".repeat(dots + 1)} `);
1019
1137
  if (!status.building) {
1020
1138
  process.stdout.write("\n");
1021
1139
  return status.result ?? "FAILURE";
1022
1140
  }
1023
1141
  }
1024
- throw new Error("\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)");
1142
+ throw new Error(M2().buildTimeout);
1025
1143
  }
1026
1144
  async function streamDeploy(webBase, token, body) {
1027
1145
  const res = await fetch(`${webBase}/api/deploy`, {
@@ -1034,10 +1152,10 @@ async function streamDeploy(webBase, token, body) {
1034
1152
  });
1035
1153
  if (!res.ok) {
1036
1154
  const text = await res.text().catch(() => "");
1037
- throw new Error(`\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${res.status}: ${text.slice(0, 200)}`);
1155
+ throw new Error(M2().serverDeployFailed(res.status, text.slice(0, 200)));
1038
1156
  }
1039
1157
  const reader = res.body?.getReader();
1040
- if (!reader) throw new Error("SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
1158
+ if (!reader) throw new Error(M2().noSseStream);
1041
1159
  const decoder = new TextDecoder();
1042
1160
  let buf = "";
1043
1161
  while (true) {
@@ -1097,12 +1215,13 @@ async function promptGitProviderSetup(provider) {
1097
1215
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1098
1216
  const ask2 = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
1099
1217
  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: ";
1218
+ const m = M2();
1219
+ log(kleur.bold(isGh ? m.githubSetupTitle : m.gitlabSetupTitle));
1220
+ const tokenLabel = isGh ? m.githubTokenPrompt : m.gitlabTokenPrompt;
1102
1221
  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): ";
1222
+ const owner = await ask2(isGh ? m.githubOwnerPrompt : m.gitlabOwnerPrompt);
1223
+ const repo = await ask2(m.repoPrompt);
1224
+ const hostPrompt = isGh ? m.githubHostPrompt : m.gitlabHostPrompt;
1106
1225
  const host = await ask2(hostPrompt);
1107
1226
  rl.close();
1108
1227
  return {
@@ -1117,38 +1236,36 @@ function resolveCi(ciOption, jenkins, ciProvider) {
1117
1236
  if (ciOption !== "auto") return ciOption;
1118
1237
  if (jenkins?.url && jenkins.token) return "jenkins";
1119
1238
  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
- );
1239
+ throw new Error(M2().noCiConfig);
1123
1240
  }
1124
1241
  async function runGitProviderBuild(cfg, args) {
1125
1242
  let runUrl = "";
1126
1243
  let runId;
1127
1244
  if (cfg.provider === "github") {
1128
1245
  if (!args.workflow) {
1129
- throw new Error("--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)");
1246
+ throw new Error(M2().workflowRequired);
1130
1247
  }
1131
- log(`\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${kleur.cyan(args.workflow)} @ ${args.ref}`);
1248
+ log(M2().ghTrigger(kleur.cyan(args.workflow), args.ref));
1132
1249
  const inputs = {};
1133
1250
  if (args.appId) inputs.MIMI_APP_ID = args.appId;
1134
1251
  inputs.PLATFORM = args.platform;
1135
1252
  const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
1136
1253
  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.");
1254
+ throw new Error(M2().ghRunIdFailed);
1138
1255
  }
1139
1256
  runId = result2.runId;
1140
1257
  runUrl = result2.url;
1141
- log(kleur.dim(` Run ID: ${runId} \u2192 ${runUrl}`));
1258
+ log(kleur.dim(M2().ghRunId(runId, runUrl)));
1142
1259
  } else {
1143
- log(`\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${args.ref}`);
1260
+ log(M2().glTrigger(args.ref));
1144
1261
  const variables = { PLATFORM: args.platform };
1145
1262
  if (args.appId) variables.MIMI_APP_ID = args.appId;
1146
1263
  const result2 = await glTriggerPipeline(cfg, args.ref, variables);
1147
1264
  runId = result2.pipelineId;
1148
1265
  runUrl = result2.url;
1149
- log(kleur.dim(` Pipeline ID: ${runId} \u2192 ${runUrl}`));
1266
+ log(kleur.dim(M2().glPipelineId(runId, runUrl)));
1150
1267
  }
1151
- log(" \uC644\uB8CC \uB300\uAE30 \uC911...");
1268
+ log(M2().waitingForCompletion);
1152
1269
  let dots = 0;
1153
1270
  const onTick = (status) => {
1154
1271
  dots = (dots + 1) % 4;
@@ -1157,19 +1274,19 @@ async function runGitProviderBuild(cfg, args) {
1157
1274
  const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
1158
1275
  process.stdout.write("\n");
1159
1276
  if (result === "success") {
1160
- log(kleur.green(`\u2705 \uBE4C\uB4DC #${runId} \uC131\uACF5`));
1277
+ log(kleur.green(M2().buildSucceeded(runId)));
1161
1278
  return runId;
1162
1279
  }
1163
- log(kleur.red(`\uBE4C\uB4DC \uC885\uB8CC: ${result}`));
1280
+ log(kleur.red(M2().buildEnded(result)));
1164
1281
  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}`));
1282
+ log(kleur.dim(M2().alreadyBuiltHint("<N>", args.platform)));
1166
1283
  process.exit(1);
1167
1284
  }
1168
1285
  async function cmdDeploy(argv) {
1169
1286
  const args = parseArgs(argv);
1170
1287
  const cfg = await getEffectiveConfig();
1171
1288
  if (!cfg) {
1172
- log(kleur.red("\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589."));
1289
+ log(kleur.red(M2().noAccount));
1173
1290
  process.exit(1);
1174
1291
  }
1175
1292
  if (args.setupJenkins) {
@@ -1180,17 +1297,17 @@ async function cmdDeploy(argv) {
1180
1297
  if (args.setupGithub) {
1181
1298
  const ciCfg = await promptGitProviderSetup("github");
1182
1299
  saveCiProviderConfig(ciCfg);
1183
- log(kleur.green(`\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1300
+ log(kleur.green(M2().githubSaved));
1184
1301
  return;
1185
1302
  }
1186
1303
  if (args.setupGitlab) {
1187
1304
  const ciCfg = await promptGitProviderSetup("gitlab");
1188
1305
  saveCiProviderConfig(ciCfg);
1189
- log(kleur.green(`\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1306
+ log(kleur.green(M2().gitlabSaved));
1190
1307
  return;
1191
1308
  }
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"));
1309
+ log(kleur.bold(M2().title(args.platform)));
1310
+ if (args.dryRun) log(kleur.yellow(M2().dryRunNotice));
1194
1311
  log("");
1195
1312
  let versionCode = args.versionCode;
1196
1313
  if (!args.skipBuild) {
@@ -1198,30 +1315,30 @@ async function cmdDeploy(argv) {
1198
1315
  migrateLegacyJenkins();
1199
1316
  const jenkinsCfg = loadJenkinsConfig() ?? void 0;
1200
1317
  const kind = resolveCi(args.ci, jenkinsCfg, ciProvider);
1201
- log(kleur.dim(` CI: ${kind}`));
1318
+ log(kleur.dim(M2().ciLine(kind)));
1202
1319
  if (kind === "jenkins") {
1203
1320
  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."));
1321
+ log(kleur.yellow(M2().noJenkinsConfig));
1205
1322
  process.exit(1);
1206
1323
  }
1207
1324
  const jenkins = jenkinsCfg;
1208
1325
  const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1209
1326
  if (!jobName) {
1210
- log(kleur.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1327
+ log(kleur.red(M2().noJenkinsJob(args.platform)));
1211
1328
  process.exit(1);
1212
1329
  }
1213
- log(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur.cyan(jobName)}`);
1330
+ log(M2().jenkinsTrigger(kleur.cyan(jobName)));
1214
1331
  const buildParams = {};
1215
1332
  if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1216
1333
  const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1217
1334
  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."));
1335
+ log(kleur.yellow(M2().noQueueItem));
1219
1336
  } else {
1220
- log(kleur.dim(` Queue item: ${queueItemId}`));
1337
+ log(kleur.dim(M2().queueItem(queueItemId)));
1221
1338
  }
1222
1339
  let buildNumber = null;
1223
1340
  if (queueItemId) {
1224
- log(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1341
+ log(M2().waitingForBuildNumber);
1225
1342
  for (let i = 0; i < 6; i++) {
1226
1343
  await new Promise((r) => setTimeout(r, 5e3));
1227
1344
  buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
@@ -1229,77 +1346,77 @@ async function cmdDeploy(argv) {
1229
1346
  }
1230
1347
  }
1231
1348
  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."));
1349
+ log(kleur.yellow(M2().noBuildNumber));
1233
1350
  process.exit(1);
1234
1351
  }
1235
- log(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1352
+ log(M2().buildStarted(buildNumber));
1236
1353
  const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1237
1354
  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}`));
1355
+ log(kleur.red(M2().buildFailed(result)));
1356
+ log(kleur.dim(M2().jenkinsLink(`${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`)));
1357
+ log(kleur.dim(M2().alreadyBuiltHint(String(buildNumber), args.platform)));
1241
1358
  process.exit(1);
1242
1359
  }
1243
- log(kleur.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1360
+ log(kleur.green(M2().buildSucceeded(buildNumber)));
1244
1361
  if (!versionCode) {
1245
1362
  versionCode = buildNumber;
1246
- log(kleur.dim(` versionCode = buildNumber (${versionCode})`));
1363
+ log(kleur.dim(M2().versionCodeFromBuild(versionCode)));
1247
1364
  }
1248
1365
  } else {
1249
1366
  if (!ciProvider) {
1250
- log(kleur.red(`${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`));
1367
+ log(kleur.red(M2().noProviderConfig(kind)));
1251
1368
  process.exit(1);
1252
1369
  }
1253
1370
  const buildId = await runGitProviderBuild(ciProvider, args);
1254
1371
  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>"));
1372
+ log(kleur.red(M2().versionCodeUnsuitable(kind, buildId)));
1373
+ log(kleur.dim(M2().recommendation));
1374
+ log(kleur.dim(M2().recommendationCi));
1375
+ log(kleur.dim(M2().recommendationNext));
1259
1376
  process.exit(1);
1260
1377
  }
1261
1378
  }
1262
1379
  }
1263
1380
  if (!versionCode) {
1264
- log(kleur.red("versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694."));
1381
+ log(kleur.red(M2().versionCodeUnknown));
1265
1382
  process.exit(1);
1266
1383
  }
1267
1384
  let appId = args.appId;
1268
1385
  if (!appId) {
1269
- const { mcpCall } = await import("./mcp-client-SLIKR42C.js");
1386
+ const { mcpCall } = await import("./mcp-client-PVWDCYJN.js");
1270
1387
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1271
1388
  if (!r.isError) {
1272
1389
  try {
1273
1390
  const apps = JSON.parse(r.text);
1274
1391
  if (apps.length > 0) {
1275
1392
  appId = apps[0].id;
1276
- log(kleur.dim(` \uC571: ${apps[0].name} (${appId})`));
1393
+ log(kleur.dim(M2().appLine(apps[0].name, appId)));
1277
1394
  }
1278
1395
  } catch {
1279
1396
  }
1280
1397
  }
1281
1398
  }
1282
1399
  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."));
1400
+ log(kleur.red(M2().noAppId));
1284
1401
  process.exit(1);
1285
1402
  }
1286
1403
  const needsConfirm = !args.dryRun && !args.yes && process.stdout.isTTY && !process.env.MIMI_SEED_TOKEN;
1287
1404
  if (needsConfirm) {
1288
- const target = args.platform === "ios" ? "App Store \uC2EC\uC0AC \uC81C\uCD9C" : "Play Store production \uD2B8\uB799";
1405
+ const target = args.platform === "ios" ? M2().targetIos : M2().targetAndroid;
1289
1406
  log("");
1290
- log(kleur.yellow(`\u26A0 \uC2E4\uC81C \uBC30\uD3EC: ${args.platform} \xB7 versionCode ${versionCode} \u2192 ${target}`));
1407
+ log(kleur.yellow(M2().realDeploy(args.platform, versionCode, target)));
1291
1408
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1292
1409
  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()))
1410
+ (resolve) => rl.question(kleur.bold(M2().confirmPrompt), (a) => resolve(a.trim().toLowerCase()))
1294
1411
  );
1295
1412
  rl.close();
1296
1413
  if (answer !== "y" && answer !== "yes") {
1297
- log(kleur.dim("\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)"));
1414
+ log(kleur.dim(M2().confirmCancelled));
1298
1415
  return;
1299
1416
  }
1300
1417
  }
1301
1418
  log("");
1302
- log("\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...");
1419
+ log(M2().pipelineStarting);
1303
1420
  log("");
1304
1421
  const deployBuildNumber = !args.skipBuild ? versionCode : void 0;
1305
1422
  await streamDeploy(cfg.webBase, cfg.token, {
@@ -1313,214 +1430,10 @@ async function cmdDeploy(argv) {
1313
1430
  dryRun: args.dryRun
1314
1431
  });
1315
1432
  log("");
1316
- log(kleur.bold("\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1433
+ log(kleur.bold(M2().done));
1317
1434
  log(kleur.dim(" https://play.google.com/console/developers"));
1318
1435
  }
1319
1436
 
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
1437
  // src/setup.ts
1525
1438
  function log2(msg = "") {
1526
1439
  process.stdout.write(msg + "\n");
@@ -1651,7 +1564,7 @@ async function connectOne(spec) {
1651
1564
  }
1652
1565
  async function cmdSetup(argv) {
1653
1566
  const opts = parseSetupArgs(argv);
1654
- const home = os6.homedir();
1567
+ const home = os5.homedir();
1655
1568
  migrateLegacyJenkins(home);
1656
1569
  if (resolveMode(opts, process.env, process.stdin.isTTY) === "interactive") {
1657
1570
  await ensureLangChosen();
@@ -1756,9 +1669,6 @@ export {
1756
1669
  deleteConfig,
1757
1670
  CONFIG_LOCATION,
1758
1671
  getEffectiveConfig,
1759
- writeSettings,
1760
- isLang,
1761
- resolveLang,
1762
1672
  credLabel,
1763
1673
  credNote,
1764
1674
  CREDENTIALS,
@@ -1766,7 +1676,6 @@ export {
1766
1676
  detectAll,
1767
1677
  isSatisfied,
1768
1678
  migrateLegacyJenkins,
1769
- t,
1770
1679
  MCP_PKG,
1771
1680
  runMcpBin,
1772
1681
  cmdDeploy,