mimi-seed 0.2.2 → 0.2.3

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.
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/mcp-client.ts
4
+ async function postMcp(endpoint, token, body, sessionId) {
5
+ const headers = {
6
+ "Content-Type": "application/json",
7
+ Accept: "application/json, text/event-stream",
8
+ Authorization: `Bearer ${token}`
9
+ };
10
+ if (sessionId) headers["Mcp-Session-Id"] = sessionId;
11
+ const res = await fetch(endpoint, { method: "POST", headers, body: JSON.stringify(body) });
12
+ const newSession = res.headers.get("Mcp-Session-Id") ?? sessionId ?? null;
13
+ const contentType = res.headers.get("content-type") ?? "";
14
+ let payload;
15
+ if (contentType.includes("text/event-stream")) {
16
+ const text = await res.text();
17
+ const line = text.split("\n").map((l) => l.trim()).find((l) => l.startsWith("data:"));
18
+ if (!line) throw new Error("MCP SSE \uC751\uB2F5\uC5D0 data \uC5C6\uC74C");
19
+ payload = JSON.parse(line.slice(5).trim());
20
+ } else {
21
+ payload = await res.json();
22
+ }
23
+ return { payload, sessionId: newSession };
24
+ }
25
+ async function mcpCall(endpoint, token, name, args) {
26
+ const { payload: initPayload, sessionId } = await postMcp(endpoint, token, {
27
+ jsonrpc: "2.0",
28
+ id: 1,
29
+ method: "initialize",
30
+ params: {
31
+ protocolVersion: "2024-11-05",
32
+ capabilities: {},
33
+ clientInfo: { name: "mimi-seed-cli", version: "1.0.0" }
34
+ }
35
+ });
36
+ if (initPayload.error) throw new Error(`MCP init \uC2E4\uD328: ${initPayload.error.message}`);
37
+ await postMcp(endpoint, token, {
38
+ jsonrpc: "2.0",
39
+ method: "notifications/initialized"
40
+ }, sessionId ?? void 0).catch(() => null);
41
+ const { payload } = await postMcp(endpoint, token, {
42
+ jsonrpc: "2.0",
43
+ id: 2,
44
+ method: "tools/call",
45
+ params: { name, arguments: args }
46
+ }, sessionId ?? void 0);
47
+ if (payload.error) {
48
+ return { text: payload.error.message, isError: true };
49
+ }
50
+ const content = payload.result?.content ?? [];
51
+ const text = content.filter((c) => c.type === "text").map((c) => c.text ?? "").join("\n");
52
+ return { text, isError: payload.result?.isError ?? false };
53
+ }
54
+
55
+ export {
56
+ mcpCall
57
+ };
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ mcpCall
4
+ } from "./chunk-FW4DVFZD.js";
2
5
 
3
6
  // src/index.ts
4
7
  import os2 from "os";
5
- import kleur6 from "kleur";
8
+ import kleur7 from "kleur";
6
9
  import open from "open";
7
10
 
8
11
  // src/detect.ts
@@ -209,40 +212,6 @@ async function awaitHandshake(timeoutMs) {
209
212
  return { port, promise };
210
213
  }
211
214
 
212
- // src/mcp-client.ts
213
- async function mcpCall(endpoint, token, name, args) {
214
- const res = await fetch(endpoint, {
215
- method: "POST",
216
- headers: {
217
- "Content-Type": "application/json",
218
- Accept: "application/json, text/event-stream",
219
- Authorization: `Bearer ${token}`
220
- },
221
- body: JSON.stringify({
222
- jsonrpc: "2.0",
223
- id: 1,
224
- method: "tools/call",
225
- params: { name, arguments: args }
226
- })
227
- });
228
- const contentType = res.headers.get("content-type") ?? "";
229
- let payload;
230
- if (contentType.includes("text/event-stream")) {
231
- const text2 = await res.text();
232
- const line = text2.split("\n").map((l) => l.trim()).find((l) => l.startsWith("data:"));
233
- if (!line) throw new Error("MCP SSE \uC751\uB2F5\uC5D0 data \uC5C6\uC74C");
234
- payload = JSON.parse(line.slice(5).trim());
235
- } else {
236
- payload = await res.json();
237
- }
238
- if (payload.error) {
239
- return { text: payload.error.message, isError: true };
240
- }
241
- const content = payload.result?.content ?? [];
242
- const text = content.filter((c) => c.type === "text").map((c) => c.text ?? "").join("\n");
243
- return { text, isError: payload.result?.isError ?? false };
244
- }
245
-
246
215
  // src/config.ts
247
216
  import fs2 from "fs/promises";
248
217
  import path2 from "path";
@@ -259,7 +228,10 @@ async function readConfig() {
259
228
  }
260
229
  async function writeConfig(cfg) {
261
230
  await fs2.mkdir(CONFIG_DIR, { recursive: true });
262
- await fs2.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2), { mode: 384 });
231
+ await fs2.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
232
+ if (process.platform !== "win32") {
233
+ await fs2.chmod(CONFIG_PATH, 384);
234
+ }
263
235
  }
264
236
  async function deleteConfig() {
265
237
  await fs2.rm(CONFIG_PATH, { force: true });
@@ -876,62 +848,308 @@ async function cmdAuth(args) {
876
848
  if (code !== 0) process.exit(code);
877
849
  }
878
850
 
851
+ // src/deploy.ts
852
+ import kleur6 from "kleur";
853
+ import * as readline from "readline";
854
+ var PHASE_ICON = {
855
+ init: "\u{1F680}",
856
+ verify: "\u{1F50D}",
857
+ notes: "\u{1F4DD}",
858
+ apply: "\u{1F4E4}",
859
+ promote: "\u{1F3AF}",
860
+ done: "\u{1F389}",
861
+ error: "\u274C"
862
+ };
863
+ function log2(msg) {
864
+ process.stdout.write(msg + "\n");
865
+ }
866
+ function jenkinsHeaders(cfg) {
867
+ const creds = Buffer.from(`${cfg.user ?? "admin"}:${cfg.token}`).toString("base64");
868
+ return { Authorization: `Basic ${creds}`, "Content-Type": "application/json" };
869
+ }
870
+ async function triggerBuild(cfg, jobName, params) {
871
+ const qs = new URLSearchParams(params).toString();
872
+ const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/buildWithParameters?${qs}`;
873
+ const res = await fetch(url, { method: "POST", headers: jenkinsHeaders(cfg) });
874
+ if (!res.ok) {
875
+ throw new Error(`Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${res.status}: ${await res.text()}`);
876
+ }
877
+ const location = res.headers.get("Location") ?? "";
878
+ const match = location.match(/\/queue\/item\/(\d+)\//);
879
+ return match?.[1] ?? "";
880
+ }
881
+ async function getQueueBuildNumber(cfg, queueItemId) {
882
+ const url = `${cfg.url}/queue/item/${queueItemId}/api/json`;
883
+ const res = await fetch(url, { headers: jenkinsHeaders(cfg) });
884
+ if (!res.ok) return null;
885
+ const data = await res.json();
886
+ return data.executable?.number ?? null;
887
+ }
888
+ async function getBuildStatus(cfg, jobName, buildNumber) {
889
+ const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`;
890
+ const res = await fetch(url, { headers: jenkinsHeaders(cfg) });
891
+ if (!res.ok) throw new Error(`\uBE4C\uB4DC \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328 ${res.status}`);
892
+ const data = await res.json();
893
+ return {
894
+ building: data.building ?? true,
895
+ result: data.result ?? null,
896
+ duration: data.duration ?? 0
897
+ };
898
+ }
899
+ async function pollBuildComplete(cfg, jobName, buildNumber, timeoutMs = 30 * 60 * 1e3) {
900
+ const start = Date.now();
901
+ const intervalMs = 15e3;
902
+ let dots = 0;
903
+ let consecutiveErrors = 0;
904
+ while (Date.now() - start < timeoutMs) {
905
+ await new Promise((r) => setTimeout(r, intervalMs));
906
+ let status;
907
+ try {
908
+ status = await getBuildStatus(cfg, jobName, buildNumber);
909
+ consecutiveErrors = 0;
910
+ } catch {
911
+ consecutiveErrors++;
912
+ 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");
913
+ process.stdout.write(`\r \u26A0 Jenkins \uC5F0\uACB0 \uC624\uB958 (${consecutiveErrors}/3\uD68C), \uC7AC\uC2DC\uB3C4... `);
914
+ continue;
915
+ }
916
+ dots = (dots + 1) % 4;
917
+ process.stdout.write(`\r \u23F3 \uBE4C\uB4DC \uC9C4\uD589 \uC911${".".repeat(dots + 1)} `);
918
+ if (!status.building) {
919
+ process.stdout.write("\n");
920
+ return status.result ?? "FAILURE";
921
+ }
922
+ }
923
+ throw new Error("\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)");
924
+ }
925
+ async function streamDeploy(webBase, token, body) {
926
+ const res = await fetch(`${webBase}/api/deploy`, {
927
+ method: "POST",
928
+ headers: {
929
+ "Content-Type": "application/json",
930
+ Authorization: `Bearer ${token}`
931
+ },
932
+ body: JSON.stringify(body)
933
+ });
934
+ if (!res.ok) {
935
+ const text = await res.text().catch(() => "");
936
+ throw new Error(`\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${res.status}: ${text.slice(0, 200)}`);
937
+ }
938
+ const reader = res.body?.getReader();
939
+ if (!reader) throw new Error("SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
940
+ const decoder = new TextDecoder();
941
+ let buf = "";
942
+ while (true) {
943
+ const { done, value } = await reader.read();
944
+ if (done) break;
945
+ buf += decoder.decode(value, { stream: true });
946
+ const lines = buf.split("\n");
947
+ buf = lines.pop() ?? "";
948
+ for (const line of lines) {
949
+ if (!line.startsWith("data: ")) continue;
950
+ try {
951
+ const event = JSON.parse(line.slice(6));
952
+ const icon = PHASE_ICON[event.phase] ?? "\u25B8";
953
+ const color = event.status === "done" ? kleur6.green : event.status === "failed" ? kleur6.red : event.status === "skipped" ? kleur6.yellow : kleur6.dim;
954
+ log2(` ${icon} ${color(event.message)}`);
955
+ if (event.phase === "error" || event.phase === "verify" && event.status === "failed" || event.phase === "promote" && event.status === "failed") {
956
+ process.exit(1);
957
+ }
958
+ } catch {
959
+ }
960
+ }
961
+ }
962
+ }
963
+ function parseArgs4(argv) {
964
+ const args = { platform: "android", language: "ko-KR", dryRun: false, skipBuild: false, setupJenkins: false };
965
+ for (let i = 0; i < argv.length; i++) {
966
+ if ((argv[i] === "--platform" || argv[i] === "-p") && argv[i + 1]) args.platform = argv[++i];
967
+ if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
968
+ if (argv[i] === "--version-code" && argv[i + 1]) args.versionCode = Number(argv[++i]);
969
+ if (argv[i] === "--from" && argv[i + 1]) args.fromRef = argv[++i];
970
+ if (argv[i] === "--to" && argv[i + 1]) args.toRef = argv[++i];
971
+ if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
972
+ if (argv[i] === "--dry-run") args.dryRun = true;
973
+ if (argv[i] === "--skip-build") args.skipBuild = true;
974
+ if (argv[i] === "setup-jenkins") args.setupJenkins = true;
975
+ }
976
+ return args;
977
+ }
978
+ async function promptJenkinsSetup() {
979
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
980
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
981
+ log2(kleur6.bold("Jenkins \uC124\uC815"));
982
+ const url = await ask(" Jenkins URL (\uC608: http://192.168.30.16:8080): ");
983
+ const user = await ask(" Jenkins \uC0AC\uC6A9\uC790\uBA85 (\uC608: admin): ");
984
+ const token = await ask(" Jenkins API Token: ");
985
+ const jobAndroid = await ask(" Android Job \uC774\uB984 (\uC608: my-app-android): ");
986
+ const jobIos = await ask(" iOS Job \uC774\uB984 (\uC120\uD0DD, \uC5D4\uD130 \uC2A4\uD0B5): ");
987
+ rl.close();
988
+ return { url, user, token, jobAndroid: jobAndroid || void 0, jobIos: jobIos || void 0 };
989
+ }
990
+ async function cmdDeploy(argv) {
991
+ const args = parseArgs4(argv);
992
+ const cfg = await getEffectiveConfig();
993
+ if (!cfg) {
994
+ log2(kleur6.red("\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589."));
995
+ process.exit(1);
996
+ }
997
+ if (args.setupJenkins) {
998
+ const jenkins = await promptJenkinsSetup();
999
+ await writeConfig({ ...cfg, jenkins });
1000
+ log2(kleur6.green("\u2705 Jenkins \uC124\uC815 \uC800\uC7A5\uB428"));
1001
+ return;
1002
+ }
1003
+ log2(kleur6.bold(`mimi-seed deploy \u2014 ${args.platform}`));
1004
+ if (args.dryRun) log2(kleur6.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
1005
+ log2("");
1006
+ let versionCode = args.versionCode;
1007
+ if (!args.skipBuild) {
1008
+ if (!cfg.jenkins?.url || !cfg.jenkins?.token) {
1009
+ log2(kleur6.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
1010
+ log2(kleur6.dim(" \uB610\uB294 \uC11C\uBC84 /workspace/integrations\uC5D0\uC11C jenkins \uD504\uB85C\uBC14\uC774\uB354 \uB4F1\uB85D \uD6C4 \uC11C\uBC84\uC0AC\uC774\uB4DC \uD2B8\uB9AC\uAC70 \uAC00\uB2A5."));
1011
+ process.exit(1);
1012
+ }
1013
+ const jenkins = cfg.jenkins;
1014
+ const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1015
+ if (!jobName) {
1016
+ log2(kleur6.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1017
+ process.exit(1);
1018
+ }
1019
+ log2(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur6.cyan(jobName)}`);
1020
+ const buildParams = {};
1021
+ if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1022
+ const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1023
+ if (!queueItemId) {
1024
+ log2(kleur6.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."));
1025
+ } else {
1026
+ log2(kleur6.dim(` Queue item: ${queueItemId}`));
1027
+ }
1028
+ let buildNumber = null;
1029
+ if (queueItemId) {
1030
+ log2(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1031
+ for (let i = 0; i < 6; i++) {
1032
+ await new Promise((r) => setTimeout(r, 5e3));
1033
+ buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
1034
+ if (buildNumber) break;
1035
+ }
1036
+ }
1037
+ if (!buildNumber) {
1038
+ log2(kleur6.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."));
1039
+ process.exit(1);
1040
+ }
1041
+ log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1042
+ const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1043
+ if (result !== "SUCCESS") {
1044
+ log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
1045
+ log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
1046
+ log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
1047
+ process.exit(1);
1048
+ }
1049
+ log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1050
+ if (!versionCode) {
1051
+ versionCode = buildNumber;
1052
+ log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
1053
+ }
1054
+ }
1055
+ if (!versionCode) {
1056
+ log2(kleur6.red("versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694."));
1057
+ process.exit(1);
1058
+ }
1059
+ let appId = args.appId;
1060
+ if (!appId) {
1061
+ const { mcpCall: mcpCall2 } = await import("./mcp-client-U3CS5OZN.js");
1062
+ const r = await mcpCall2(cfg.endpoint, cfg.token, "list_apps", {});
1063
+ if (!r.isError) {
1064
+ try {
1065
+ const apps = JSON.parse(r.text);
1066
+ if (apps.length > 0) {
1067
+ appId = apps[0].id;
1068
+ log2(kleur6.dim(` \uC571: ${apps[0].name} (${appId})`));
1069
+ }
1070
+ } catch {
1071
+ }
1072
+ }
1073
+ }
1074
+ if (!appId) {
1075
+ log2(kleur6.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."));
1076
+ process.exit(1);
1077
+ }
1078
+ log2("");
1079
+ log2("\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...");
1080
+ log2("");
1081
+ const deployBuildNumber = !args.skipBuild ? versionCode : void 0;
1082
+ await streamDeploy(cfg.webBase, cfg.token, {
1083
+ appId,
1084
+ platform: args.platform,
1085
+ versionCode,
1086
+ ...deployBuildNumber ? { buildNumber: deployBuildNumber } : {},
1087
+ fromRef: args.fromRef,
1088
+ toRef: args.toRef,
1089
+ language: args.language,
1090
+ dryRun: args.dryRun
1091
+ });
1092
+ log2("");
1093
+ log2(kleur6.bold("\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1094
+ log2(kleur6.dim(" https://play.google.com/console/developers"));
1095
+ }
1096
+
879
1097
  // src/index.ts
880
1098
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
881
1099
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
882
- function log2(msg) {
1100
+ function log3(msg) {
883
1101
  process.stdout.write(msg + "\n");
884
1102
  }
885
1103
  async function cmdInit() {
886
1104
  const cwd = process.cwd();
887
- log2(kleur6.bold("Mimi Seed CLI \u2014 init"));
888
- log2(kleur6.dim(cwd));
889
- log2("");
1105
+ log3(kleur7.bold("Mimi Seed CLI \u2014 init"));
1106
+ log3(kleur7.dim(cwd));
1107
+ log3("");
890
1108
  if (!await hasAnyProjectSignal(cwd)) {
891
- log2(kleur6.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
1109
+ log3(kleur7.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
892
1110
  }
893
- log2("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
1111
+ log3("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
894
1112
  const hints = await detectHints(cwd);
895
1113
  if (hints.length === 0) {
896
- log2(kleur6.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1114
+ log3(kleur7.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
897
1115
  } else {
898
1116
  for (const h of hints) {
899
1117
  const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
900
- log2(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur6.dim(tag)}`);
1118
+ log3(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur7.dim(tag)}`);
901
1119
  }
902
1120
  }
903
- log2("");
1121
+ log3("");
904
1122
  if (process.env.MIMI_SEED_TOKEN) {
905
1123
  const cfg2 = await getEffectiveConfig();
906
1124
  if (cfg2 && hints.length > 0) {
907
- log2(kleur6.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1125
+ log3(kleur7.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
908
1126
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
909
1127
  const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
910
1128
  if (result.isError) {
911
- log2(kleur6.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1129
+ log3(kleur7.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
912
1130
  } else {
913
- for (const line of result.text.split("\n")) log2(" " + line);
1131
+ for (const line of result.text.split("\n")) log3(" " + line);
914
1132
  }
915
1133
  }
916
- log2(kleur6.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1134
+ log3(kleur7.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
917
1135
  return;
918
1136
  }
919
- log2("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1137
+ log3("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
920
1138
  const hostName = os2.hostname().slice(0, 32);
921
1139
  const name = `cli-${hostName}`;
922
1140
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
923
1141
  const callback = `http://127.0.0.1:${port}/cb`;
924
1142
  const connectUrl = `${DEFAULT_WEB_BASE}/cli/connect?callback=${encodeURIComponent(callback)}&name=${encodeURIComponent(name)}`;
925
- log2(kleur6.dim(` ${connectUrl}`));
1143
+ log3(kleur7.dim(` ${connectUrl}`));
926
1144
  await open(connectUrl);
927
1145
  let handshake;
928
1146
  try {
929
1147
  handshake = await promise;
930
1148
  } catch (e) {
931
- log2(kleur6.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1149
+ log3(kleur7.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
932
1150
  process.exit(1);
933
1151
  }
934
- log2(kleur6.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1152
+ log3(kleur7.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
935
1153
  const cfg = {
936
1154
  token: handshake.token,
937
1155
  prefix: handshake.prefix,
@@ -940,30 +1158,30 @@ async function cmdInit() {
940
1158
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
941
1159
  };
942
1160
  await writeConfig(cfg);
943
- log2(kleur6.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
944
- log2("");
1161
+ log3(kleur7.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1162
+ log3("");
945
1163
  if (hints.length > 0) {
946
- log2("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
1164
+ log3("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
947
1165
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
948
1166
  const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
949
1167
  if (result.isError) {
950
- log2(kleur6.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1168
+ log3(kleur7.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
951
1169
  } else {
952
- for (const line of result.text.split("\n")) log2(" " + line);
1170
+ for (const line of result.text.split("\n")) log3(" " + line);
953
1171
  }
954
- log2("");
1172
+ log3("");
955
1173
  }
956
- log2(kleur6.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
957
- log2("");
958
- log2("Claude Code\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
959
- log2(kleur6.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
960
- log2(kleur6.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
961
- log2(kleur6.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
962
- log2("");
963
- log2(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur6.underline(DEFAULT_WEB_BASE + "/apps")}`);
964
- log2("");
965
- log2(
966
- kleur6.dim(
1174
+ log3(kleur7.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1175
+ log3("");
1176
+ log3("Claude Code\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1177
+ log3(kleur7.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1178
+ log3(kleur7.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1179
+ log3(kleur7.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1180
+ log3("");
1181
+ log3(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur7.underline(DEFAULT_WEB_BASE + "/apps")}`);
1182
+ log3("");
1183
+ log3(
1184
+ kleur7.dim(
967
1185
  `Claude Code MCP \uB4F1\uB85D:
968
1186
  claude mcp add --transport http mimi-seed ${DEFAULT_MCP_ENDPOINT} \\
969
1187
  --header "Authorization: Bearer ${cfg.prefix}..."`
@@ -973,40 +1191,41 @@ async function cmdInit() {
973
1191
  async function cmdStatus() {
974
1192
  const cfg = await getEffectiveConfig();
975
1193
  if (!cfg) {
976
- log2(kleur6.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1194
+ log3(kleur7.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
977
1195
  process.exit(1);
978
1196
  }
979
- log2(kleur6.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
980
- log2(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
981
- log2(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
982
- log2("");
983
- log2("\u{1F4CB} \uC571 \uBAA9\uB85D:");
1197
+ log3(kleur7.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1198
+ log3(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
1199
+ log3(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
1200
+ log3("");
1201
+ log3("\u{1F4CB} \uC571 \uBAA9\uB85D:");
984
1202
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
985
1203
  if (r.isError) {
986
- log2(kleur6.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1204
+ log3(kleur7.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
987
1205
  process.exit(1);
988
1206
  }
989
- for (const line of r.text.split("\n")) log2(" " + line);
1207
+ for (const line of r.text.split("\n")) log3(" " + line);
990
1208
  }
991
1209
  async function cmdLogout() {
992
1210
  await deleteConfig();
993
- log2(kleur6.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
994
- log2(kleur6.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1211
+ log3(kleur7.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1212
+ log3(kleur7.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
995
1213
  }
996
1214
  function printHelp() {
997
- log2(`${kleur6.bold("mimi-seed")} \u2014 Claude Code\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1215
+ log3(`${kleur7.bold("mimi-seed")} \u2014 Claude Code\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
998
1216
 
999
- ${kleur6.bold("\uBA85\uB839\uC5B4:")}
1000
- ${kleur6.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1001
- ${kleur6.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1002
- ${kleur6.cyan("mimi-seed auth")} Google OAuth \uC778\uC99D (Firebase/AdMob/Play). 'mimi-seed auth --help'
1003
- ${kleur6.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1004
- ${kleur6.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1005
- ${kleur6.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1006
- ${kleur6.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1007
- ${kleur6.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1217
+ ${kleur7.bold("\uBA85\uB839\uC5B4:")}
1218
+ ${kleur7.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1219
+ ${kleur7.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1220
+ ${kleur7.cyan("mimi-seed auth")} Google OAuth \uC778\uC99D (Firebase/AdMob/Play). 'mimi-seed auth --help'
1221
+ ${kleur7.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1222
+ ${kleur7.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1223
+ ${kleur7.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1224
+ ${kleur7.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1225
+ ${kleur7.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (Jenkins \u2192 Play Store/App Store)
1226
+ ${kleur7.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1008
1227
 
1009
- ${kleur6.bold("mimi-seed notes \uC635\uC158:")}
1228
+ ${kleur7.bold("mimi-seed notes \uC635\uC158:")}
1010
1229
  --from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
1011
1230
  --to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
1012
1231
  --locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
@@ -1014,11 +1233,11 @@ ${kleur6.bold("mimi-seed notes \uC635\uC158:")}
1014
1233
  --no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
1015
1234
  --limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)
1016
1235
 
1017
- ${kleur6.bold("mimi-seed check \uC635\uC158:")}
1236
+ ${kleur7.bold("mimi-seed check \uC635\uC158:")}
1018
1237
  --app <id> \uC571 ID \uC9C0\uC815
1019
1238
  --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)
1020
1239
 
1021
- ${kleur6.bold("mimi-seed review \uC635\uC158:")}
1240
+ ${kleur7.bold("mimi-seed review \uC635\uC158:")}
1022
1241
  --text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
1023
1242
  --rating <1-5> \uBCC4\uC810
1024
1243
  --tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
@@ -1029,7 +1248,18 @@ ${kleur6.bold("mimi-seed review \uC635\uC158:")}
1029
1248
  --package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
1030
1249
  --no-interactive CI \uBAA8\uB4DC
1031
1250
 
1032
- ${kleur6.bold("\uD658\uACBD\uBCC0\uC218:")}
1251
+ ${kleur7.bold("mimi-seed deploy \uC635\uC158:")}
1252
+ --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1253
+ --app <id> \uC571 ID \uC9C0\uC815
1254
+ --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
1255
+ --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
1256
+ --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
1257
+ --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
1258
+ --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
1259
+ --skip-build Jenkins \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
1260
+ setup-jenkins Jenkins \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1261
+
1262
+ ${kleur7.bold("\uD658\uACBD\uBCC0\uC218:")}
1033
1263
  MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
1034
1264
  MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
1035
1265
  ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
@@ -1061,6 +1291,9 @@ async function main() {
1061
1291
  case "auth":
1062
1292
  await cmdAuth(restArgs);
1063
1293
  break;
1294
+ case "deploy":
1295
+ await cmdDeploy(restArgs);
1296
+ break;
1064
1297
  case "logout":
1065
1298
  await cmdLogout();
1066
1299
  break;
@@ -1070,13 +1303,13 @@ async function main() {
1070
1303
  printHelp();
1071
1304
  break;
1072
1305
  default:
1073
- log2(kleur6.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
1306
+ log3(kleur7.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
1074
1307
  printHelp();
1075
1308
  process.exit(1);
1076
1309
  }
1077
1310
  } catch (e) {
1078
- log2(kleur6.red(`\uC624\uB958: ${e.message}`));
1079
- if (process.env.DEBUG) log2(e.stack ?? "");
1311
+ log3(kleur7.red(`\uC624\uB958: ${e.message}`));
1312
+ if (process.env.DEBUG) log3(e.stack ?? "");
1080
1313
  process.exit(1);
1081
1314
  }
1082
1315
  }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ mcpCall
4
+ } from "./chunk-FW4DVFZD.js";
5
+ export {
6
+ mcpCall
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Mimi Seed CLI — Claude Code에서 앱 출시 운영을 관리합니다.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"