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.
- package/dist/chunk-FW4DVFZD.js +57 -0
- package/dist/index.js +330 -97
- package/dist/mcp-client-U3CS5OZN.js +7 -0
- package/package.json +1 -1
|
@@ -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
|
|
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)
|
|
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
|
|
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
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1105
|
+
log3(kleur7.bold("Mimi Seed CLI \u2014 init"));
|
|
1106
|
+
log3(kleur7.dim(cwd));
|
|
1107
|
+
log3("");
|
|
890
1108
|
if (!await hasAnyProjectSignal(cwd)) {
|
|
891
|
-
|
|
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
|
-
|
|
1111
|
+
log3("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
|
|
894
1112
|
const hints = await detectHints(cwd);
|
|
895
1113
|
if (hints.length === 0) {
|
|
896
|
-
|
|
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
|
-
|
|
1118
|
+
log3(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur7.dim(tag)}`);
|
|
901
1119
|
}
|
|
902
1120
|
}
|
|
903
|
-
|
|
1121
|
+
log3("");
|
|
904
1122
|
if (process.env.MIMI_SEED_TOKEN) {
|
|
905
1123
|
const cfg2 = await getEffectiveConfig();
|
|
906
1124
|
if (cfg2 && hints.length > 0) {
|
|
907
|
-
|
|
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
|
-
|
|
1129
|
+
log3(kleur7.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
|
|
912
1130
|
} else {
|
|
913
|
-
for (const line of result.text.split("\n"))
|
|
1131
|
+
for (const line of result.text.split("\n")) log3(" " + line);
|
|
914
1132
|
}
|
|
915
1133
|
}
|
|
916
|
-
|
|
1134
|
+
log3(kleur7.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
|
|
917
1135
|
return;
|
|
918
1136
|
}
|
|
919
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1149
|
+
log3(kleur7.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
|
|
932
1150
|
process.exit(1);
|
|
933
1151
|
}
|
|
934
|
-
|
|
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
|
-
|
|
944
|
-
|
|
1161
|
+
log3(kleur7.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
|
|
1162
|
+
log3("");
|
|
945
1163
|
if (hints.length > 0) {
|
|
946
|
-
|
|
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
|
-
|
|
1168
|
+
log3(kleur7.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
|
|
951
1169
|
} else {
|
|
952
|
-
for (const line of result.text.split("\n"))
|
|
1170
|
+
for (const line of result.text.split("\n")) log3(" " + line);
|
|
953
1171
|
}
|
|
954
|
-
|
|
1172
|
+
log3("");
|
|
955
1173
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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
|
-
|
|
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
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
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
|
-
|
|
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"))
|
|
1207
|
+
for (const line of r.text.split("\n")) log3(" " + line);
|
|
990
1208
|
}
|
|
991
1209
|
async function cmdLogout() {
|
|
992
1210
|
await deleteConfig();
|
|
993
|
-
|
|
994
|
-
|
|
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
|
-
|
|
1215
|
+
log3(`${kleur7.bold("mimi-seed")} \u2014 Claude Code\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
|
|
998
1216
|
|
|
999
|
-
${
|
|
1000
|
-
${
|
|
1001
|
-
${
|
|
1002
|
-
${
|
|
1003
|
-
${
|
|
1004
|
-
${
|
|
1005
|
-
${
|
|
1006
|
-
${
|
|
1007
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
-
|
|
1079
|
-
if (process.env.DEBUG)
|
|
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
|
}
|