mimi-seed 0.19.10 → 0.19.11
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-I3C5DIUA.js → chunk-2FKWHED2.js} +1 -1
- package/dist/{chunk-ZGMPOJRY.js → chunk-TQHLWSFR.js} +4 -2
- package/dist/{chunk-KY6LI3CK.js → chunk-UKNCSE4V.js} +81 -4
- package/dist/index.js +165 -35
- package/dist/{mcp-client-DZNDKAGQ.js → mcp-client-L5BQ5VG4.js} +2 -2
- package/dist/{setup-RI2NDOZR.js → setup-VO5WN32U.js} +2 -2
- package/package.json +1 -1
|
@@ -118,6 +118,7 @@ var ko = {
|
|
|
118
118
|
serverOk: "Mimi Seed \uC11C\uBC84 \uC5F0\uACB0\uB428",
|
|
119
119
|
appCount: (n) => `\uC571 ${n}\uAC1C`,
|
|
120
120
|
unknownService: (id) => `${id} (\uC54C \uC218 \uC5C6\uB294 \uC11C\uBE44\uC2A4)`,
|
|
121
|
+
credentialMismatch: (field, expected, actual) => `${field} \uBD88\uC77C\uCE58 \u2014 \uC694\uAD6C\uAC12 ${expected}, \uD604\uC7AC\uAC12 ${actual ?? "\uC5C6\uC74C"}`,
|
|
121
122
|
credsHint: " \uC804\uBD80 \uC5F0\uACB0\uD558\uAE30: mimi-seed setup \xB7 OAuth \uC2E0\uC120\uB3C4: mimi-seed auth status\n",
|
|
122
123
|
nodeTooOld: (v) => `${v} \u2014 v20 \uC774\uC0C1 \uD544\uC694 (.nvmrc \uCC38\uACE0)`,
|
|
123
124
|
gitRepo: "Git \uC800\uC7A5\uC18C",
|
|
@@ -126,7 +127,7 @@ var ko = {
|
|
|
126
127
|
noGit: "Git \uC800\uC7A5\uC18C \uC5C6\uC74C",
|
|
127
128
|
noGitDetail: "mimi-seed notes \uC0AC\uC6A9 \uBD88\uAC00",
|
|
128
129
|
noApp: "\uC571 \uAC10\uC9C0 \uC5C6\uC74C",
|
|
129
|
-
noAppDetail: "
|
|
130
|
+
noAppDetail: "Expo / Gradle / Xcode / Unity \uC571 \uC124\uC815 \uC5C6\uC74C",
|
|
130
131
|
unnamed: "(\uC774\uB984 \uBBF8\uC0C1)",
|
|
131
132
|
requirements: (proj) => `${proj} \uC694\uAD6C\uC0AC\uD56D (.mimi-seed.json)`,
|
|
132
133
|
thisProject: "\uC774 \uD504\uB85C\uC81D\uD2B8"
|
|
@@ -220,6 +221,7 @@ MIMI_SEED_LANG takes precedence when set.`,
|
|
|
220
221
|
serverOk: "Connected to Mimi Seed",
|
|
221
222
|
appCount: (n) => `${n} app(s)`,
|
|
222
223
|
unknownService: (id) => `${id} (unknown service)`,
|
|
224
|
+
credentialMismatch: (field, expected, actual) => `${field} mismatch \u2014 expected ${expected}, current ${actual ?? "missing"}`,
|
|
223
225
|
credsHint: " Connect everything: mimi-seed setup \xB7 OAuth freshness: mimi-seed auth status\n",
|
|
224
226
|
nodeTooOld: (v) => `${v} \u2014 v20+ required (see .nvmrc)`,
|
|
225
227
|
gitRepo: "Git repository",
|
|
@@ -228,7 +230,7 @@ MIMI_SEED_LANG takes precedence when set.`,
|
|
|
228
230
|
noGit: "Not a git repository",
|
|
229
231
|
noGitDetail: "mimi-seed notes is unavailable",
|
|
230
232
|
noApp: "No app detected",
|
|
231
|
-
noAppDetail: "no
|
|
233
|
+
noAppDetail: "no Expo / Gradle / Xcode / Unity app configuration",
|
|
232
234
|
unnamed: "(unnamed)",
|
|
233
235
|
requirements: (proj) => `${proj} requirements (.mimi-seed.json)`,
|
|
234
236
|
thisProject: "This project"
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
resolveLang,
|
|
6
6
|
t,
|
|
7
7
|
writeSettings
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TQHLWSFR.js";
|
|
9
9
|
|
|
10
10
|
// src/setup.ts
|
|
11
11
|
import kleur2 from "kleur";
|
|
@@ -242,7 +242,14 @@ var CREDENTIALS = [
|
|
|
242
242
|
docsAnchor: "app-store-connect",
|
|
243
243
|
detect: (home) => {
|
|
244
244
|
const cfg = readJson(home, "appstore.json");
|
|
245
|
-
return cfg ? {
|
|
245
|
+
return cfg ? {
|
|
246
|
+
present: true,
|
|
247
|
+
detail: cfg.keyId ? `keyId ${cfg.keyId}` : void 0,
|
|
248
|
+
identity: Object.fromEntries([
|
|
249
|
+
["keyId", cfg.keyId],
|
|
250
|
+
["issuerId", cfg.issuerId]
|
|
251
|
+
].filter((entry) => typeof entry[1] === "string"))
|
|
252
|
+
} : { present: false };
|
|
246
253
|
}
|
|
247
254
|
},
|
|
248
255
|
{
|
|
@@ -769,6 +776,37 @@ async function pathExists(p) {
|
|
|
769
776
|
return false;
|
|
770
777
|
}
|
|
771
778
|
}
|
|
779
|
+
async function importedJsonObjects(configPath, text, root) {
|
|
780
|
+
const result = /* @__PURE__ */ new Map();
|
|
781
|
+
const imports = [
|
|
782
|
+
...text.matchAll(/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+["']([^"']+\.json)["']/g),
|
|
783
|
+
...text.matchAll(/\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*require\(\s*["']([^"']+\.json)["']\s*\)/g)
|
|
784
|
+
];
|
|
785
|
+
for (const match of imports) {
|
|
786
|
+
if (!match[2].startsWith(".")) continue;
|
|
787
|
+
const candidate = path5.resolve(path5.dirname(configPath), match[2]);
|
|
788
|
+
const relative = path5.relative(root, candidate);
|
|
789
|
+
if (relative.startsWith(`..${path5.sep}`) || relative === ".." || path5.isAbsolute(relative)) continue;
|
|
790
|
+
const jsonText = await readIfExists(candidate);
|
|
791
|
+
if (!jsonText) continue;
|
|
792
|
+
try {
|
|
793
|
+
const json = JSON.parse(jsonText);
|
|
794
|
+
if (json && typeof json === "object" && !Array.isArray(json)) {
|
|
795
|
+
result.set(match[1], json);
|
|
796
|
+
}
|
|
797
|
+
} catch {
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
function importedMember(text, block, field, imports) {
|
|
803
|
+
const match = new RegExp(
|
|
804
|
+
`\\b${block}\\s*:\\s*\\{[\\s\\S]{0,5000}?\\b${field}\\s*:\\s*([A-Za-z_$][\\w$]*)\\.([A-Za-z_$][\\w$]*)`
|
|
805
|
+
).exec(text);
|
|
806
|
+
if (!match) return void 0;
|
|
807
|
+
const value = imports.get(match[1])?.[match[2]];
|
|
808
|
+
return typeof value === "string" ? value : void 0;
|
|
809
|
+
}
|
|
772
810
|
async function walk(root, match, maxDepth = 5) {
|
|
773
811
|
const found = [];
|
|
774
812
|
const skipDirs = /* @__PURE__ */ new Set([
|
|
@@ -823,6 +861,17 @@ async function detectHints(cwd) {
|
|
|
823
861
|
} catch {
|
|
824
862
|
}
|
|
825
863
|
}
|
|
864
|
+
for (const fname of ["app.config.js", "app.config.cjs", "app.config.mjs", "app.config.ts"]) {
|
|
865
|
+
const configPath = path5.join(cwd, fname);
|
|
866
|
+
const txt = await readIfExists(configPath);
|
|
867
|
+
if (!txt) continue;
|
|
868
|
+
const imports = await importedJsonObjects(configPath, txt, cwd);
|
|
869
|
+
const pkg = txt.match(/\bandroid\s*:\s*\{[\s\S]{0,5000}?\bpackage\s*:\s*["']([^"']+)["']/)?.[1] ?? importedMember(txt, "android", "package", imports);
|
|
870
|
+
const bid = txt.match(/\bios\s*:\s*\{[\s\S]{0,5000}?\bbundleIdentifier\s*:\s*["']([^"']+)["']/)?.[1] ?? importedMember(txt, "ios", "bundleIdentifier", imports);
|
|
871
|
+
if (pkg || bid) {
|
|
872
|
+
hints.push({ packageName: pkg, bundleId: bid, source: [fname] });
|
|
873
|
+
}
|
|
874
|
+
}
|
|
826
875
|
const gradleFiles = await walk(
|
|
827
876
|
cwd,
|
|
828
877
|
(n) => n === "build.gradle" || n === "build.gradle.kts",
|
|
@@ -867,6 +916,34 @@ async function detectHints(cwd) {
|
|
|
867
916
|
}
|
|
868
917
|
}
|
|
869
918
|
}
|
|
919
|
+
const unitySettings = path5.join(cwd, "ProjectSettings", "ProjectSettings.asset");
|
|
920
|
+
const unityText = await readIfExists(unitySettings);
|
|
921
|
+
if (unityText) {
|
|
922
|
+
const lines = unityText.split(/\r?\n/);
|
|
923
|
+
const identifierStart = lines.findIndex((line) => /^\s*applicationIdentifier:\s*$/.test(line));
|
|
924
|
+
const baseIndent = identifierStart >= 0 ? lines[identifierStart].match(/^\s*/)?.[0].length ?? 0 : 0;
|
|
925
|
+
let packageName;
|
|
926
|
+
let bundleId;
|
|
927
|
+
if (identifierStart >= 0) {
|
|
928
|
+
for (const line of lines.slice(identifierStart + 1)) {
|
|
929
|
+
if (!line.trim()) continue;
|
|
930
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
931
|
+
if (indent <= baseIndent) break;
|
|
932
|
+
const entry = line.match(/^\s+(Android|iPhone|iOS):\s*([^\s#]+)\s*$/);
|
|
933
|
+
if (entry?.[1] === "Android") packageName = entry[2];
|
|
934
|
+
if (entry && entry[1] !== "Android") bundleId = entry[2];
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
const name = unityText.match(/^\s*productName:\s*(.+?)\s*$/m)?.[1];
|
|
938
|
+
if (packageName || bundleId) {
|
|
939
|
+
hints.push({
|
|
940
|
+
name,
|
|
941
|
+
packageName,
|
|
942
|
+
bundleId,
|
|
943
|
+
source: [path5.relative(cwd, unitySettings)]
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
870
947
|
const pkgJson = await readIfExists(path5.join(cwd, "package.json"));
|
|
871
948
|
if (pkgJson) {
|
|
872
949
|
try {
|
|
@@ -903,7 +980,7 @@ async function detectHints(cwd) {
|
|
|
903
980
|
return merged.filter((h) => h.packageName || h.bundleId);
|
|
904
981
|
}
|
|
905
982
|
async function hasAnyProjectSignal(cwd) {
|
|
906
|
-
return await pathExists(path5.join(cwd, "package.json")) || await pathExists(path5.join(cwd, "app.json")) || await pathExists(path5.join(cwd, "android")) || await pathExists(path5.join(cwd, "ios"));
|
|
983
|
+
return await pathExists(path5.join(cwd, "package.json")) || await pathExists(path5.join(cwd, "app.json")) || await pathExists(path5.join(cwd, "android")) || await pathExists(path5.join(cwd, "ios")) || await pathExists(path5.join(cwd, "ProjectSettings", "ProjectSettings.asset"));
|
|
907
984
|
}
|
|
908
985
|
|
|
909
986
|
// src/deploy.ts
|
|
@@ -1595,7 +1672,7 @@ async function cmdDeploy(argv) {
|
|
|
1595
1672
|
}
|
|
1596
1673
|
let appId = args.appId;
|
|
1597
1674
|
if (!appId) {
|
|
1598
|
-
const { mcpCall } = await import("./mcp-client-
|
|
1675
|
+
const { mcpCall } = await import("./mcp-client-L5BQ5VG4.js");
|
|
1599
1676
|
const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
1600
1677
|
if (!r.isError) {
|
|
1601
1678
|
try {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
mcpCall
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2FKWHED2.js";
|
|
5
5
|
import {
|
|
6
6
|
CONFIG_LOCATION,
|
|
7
7
|
CREDENTIALS,
|
|
@@ -22,14 +22,14 @@ import {
|
|
|
22
22
|
runMcpBin,
|
|
23
23
|
tryCredById,
|
|
24
24
|
writeConfig
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-UKNCSE4V.js";
|
|
26
26
|
import {
|
|
27
27
|
catalog,
|
|
28
28
|
isLang,
|
|
29
29
|
resolveLang,
|
|
30
30
|
t,
|
|
31
31
|
writeSettings
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-TQHLWSFR.js";
|
|
33
33
|
|
|
34
34
|
// src/index.ts
|
|
35
35
|
import os3 from "os";
|
|
@@ -176,6 +176,16 @@ function manifestDetail(id, svc) {
|
|
|
176
176
|
}
|
|
177
177
|
return parts.join(" / ");
|
|
178
178
|
}
|
|
179
|
+
function manifestCredentialMismatch(id, svc, identity) {
|
|
180
|
+
if (id !== "appstore") return null;
|
|
181
|
+
for (const field of ["keyId", "issuerId"]) {
|
|
182
|
+
const expected = svc[field];
|
|
183
|
+
if (expected && identity?.[field] !== expected) {
|
|
184
|
+
return { field, expected, actual: identity?.[field] };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
179
189
|
async function cmdDoctor() {
|
|
180
190
|
const cwd = process.cwd();
|
|
181
191
|
const m = t().doctor;
|
|
@@ -225,7 +235,10 @@ async function cmdDoctor() {
|
|
|
225
235
|
continue;
|
|
226
236
|
}
|
|
227
237
|
const connected = isSatisfied(spec, detected);
|
|
228
|
-
|
|
238
|
+
const mismatch = manifestCredentialMismatch(id, svc, detected.get(spec.id)?.identity);
|
|
239
|
+
if (connected && mismatch) {
|
|
240
|
+
fail(id, `${m.credentialMismatch(mismatch.field, mismatch.expected, mismatch.actual)} \u2192 ${spec.fix}`);
|
|
241
|
+
} else if (connected) ok(id, detail);
|
|
229
242
|
else if (!required) warn(`${id} (${t().common.optional})`, svc.note ?? detail);
|
|
230
243
|
else fail(id, `\u2192 ${spec.fix}${detail ? " " + detail : ""}`);
|
|
231
244
|
}
|
|
@@ -335,6 +348,10 @@ var BILLING_MODULE = /com\.android\.billingclient:billing(?:-ktx)?/;
|
|
|
335
348
|
var LITERAL_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:([0-9]+(?:\.[0-9A-Za-z_-]+){0,3})/g;
|
|
336
349
|
var VARIABLE_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:\$\{?([A-Za-z_][A-Za-z0-9_.-]*)\}?/g;
|
|
337
350
|
var VERSION_ASSIGNMENT = /(?:^|\s)([A-Za-z_][A-Za-z0-9_.-]*)\s*(?:=|:)\s*["']([0-9]+(?:\.[0-9A-Za-z_-]+){0,3})["']/gm;
|
|
351
|
+
var KNOWN_OPENIAP_BILLING = /* @__PURE__ */ new Map([
|
|
352
|
+
["2.1.0", { module: "com.android.billingclient:billing-ktx", version: "8.3.0" }],
|
|
353
|
+
["2.4.1", { module: "com.android.billingclient:billing", version: "9.1.0" }]
|
|
354
|
+
]);
|
|
338
355
|
var BILLING_SUPPORT_SCHEDULE = [
|
|
339
356
|
{ major: 5, submissionDeadline: "2024-08-31", extensionDeadline: "2024-11-01" },
|
|
340
357
|
{ major: 6, submissionDeadline: "2025-08-31", extensionDeadline: "2025-11-01" },
|
|
@@ -365,7 +382,7 @@ async function walk(root, maxDepth = 7) {
|
|
|
365
382
|
for (const entry of entries) {
|
|
366
383
|
if (entry.isDirectory()) {
|
|
367
384
|
if (!SKIP_DIRS.has(entry.name)) await visit(path.join(dir, entry.name), depth + 1);
|
|
368
|
-
} else if (entry.isFile() && (entry.name === "build.gradle" || entry.name === "build.gradle.kts" || entry.name === "libs.versions.toml" || entry.name === "package.json")) {
|
|
385
|
+
} else if (entry.isFile() && (entry.name === "build.gradle" || entry.name === "build.gradle.kts" || entry.name === "mainTemplate.gradle" || entry.name === "baseProjectTemplate.gradle" || entry.name === "launcherTemplate.gradle" || entry.name === "libs.versions.toml" || entry.name === "package.json")) {
|
|
369
386
|
result.push(path.join(dir, entry.name));
|
|
370
387
|
}
|
|
371
388
|
}
|
|
@@ -466,6 +483,12 @@ async function reactNativeIapEvidence(root, manifestFile, manifestText, resolveT
|
|
|
466
483
|
source: "unresolved"
|
|
467
484
|
};
|
|
468
485
|
}
|
|
486
|
+
let installedVersion = declaredVersion;
|
|
487
|
+
try {
|
|
488
|
+
const installedManifest = JSON.parse(await fs.readFile(path.join(installedDir, "package.json"), "utf8"));
|
|
489
|
+
if (typeof installedManifest.version === "string") installedVersion = installedManifest.version;
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
469
492
|
const directCandidates = [
|
|
470
493
|
path.join(installedDir, "android", "build.gradle"),
|
|
471
494
|
path.join(installedDir, "android", "build.gradle.kts")
|
|
@@ -479,7 +502,7 @@ async function reactNativeIapEvidence(root, manifestFile, manifestText, resolveT
|
|
|
479
502
|
file: relativeManifest,
|
|
480
503
|
module: direct[0].slice(0, direct[0].lastIndexOf(":")),
|
|
481
504
|
version: direct[1],
|
|
482
|
-
expression: `react-native-iap ${
|
|
505
|
+
expression: `react-native-iap ${installedVersion} native dependency`,
|
|
483
506
|
source: "transitive"
|
|
484
507
|
};
|
|
485
508
|
}
|
|
@@ -496,16 +519,26 @@ async function reactNativeIapEvidence(root, manifestFile, manifestText, resolveT
|
|
|
496
519
|
return {
|
|
497
520
|
file: relativeManifest,
|
|
498
521
|
module: "com.android.billingclient:billing",
|
|
499
|
-
expression: `react-native-iap ${
|
|
522
|
+
expression: `react-native-iap ${installedVersion} detected; transitive Billing version is unresolved`,
|
|
500
523
|
source: "unresolved"
|
|
501
524
|
};
|
|
502
525
|
}
|
|
503
526
|
const coordinate = `io.github.hyochan.openiap:openiap-google:${openIapVersion}`;
|
|
527
|
+
const known = KNOWN_OPENIAP_BILLING.get(openIapVersion);
|
|
504
528
|
if (!resolveTransitive) {
|
|
529
|
+
if (known) {
|
|
530
|
+
return {
|
|
531
|
+
file: relativeManifest,
|
|
532
|
+
module: known.module,
|
|
533
|
+
version: known.version,
|
|
534
|
+
expression: `react-native-iap ${installedVersion} -> ${coordinate} (embedded Maven metadata)`,
|
|
535
|
+
source: "transitive"
|
|
536
|
+
};
|
|
537
|
+
}
|
|
505
538
|
return {
|
|
506
539
|
file: relativeManifest,
|
|
507
540
|
module: "com.android.billingclient:billing",
|
|
508
|
-
expression: `react-native-iap ${
|
|
541
|
+
expression: `react-native-iap ${installedVersion} -> ${coordinate}; transitive lookup unavailable in repository-only mode`,
|
|
509
542
|
source: "unresolved"
|
|
510
543
|
};
|
|
511
544
|
}
|
|
@@ -516,16 +549,25 @@ async function reactNativeIapEvidence(root, manifestFile, manifestText, resolveT
|
|
|
516
549
|
file: relativeManifest,
|
|
517
550
|
module: resolved.module,
|
|
518
551
|
version: resolved.version,
|
|
519
|
-
expression: `react-native-iap ${
|
|
552
|
+
expression: `react-native-iap ${installedVersion} -> ${coordinate}`,
|
|
520
553
|
source: "transitive"
|
|
521
554
|
};
|
|
522
555
|
}
|
|
523
556
|
} catch {
|
|
524
557
|
}
|
|
558
|
+
if (known) {
|
|
559
|
+
return {
|
|
560
|
+
file: relativeManifest,
|
|
561
|
+
module: known.module,
|
|
562
|
+
version: known.version,
|
|
563
|
+
expression: `react-native-iap ${installedVersion} -> ${coordinate} (embedded Maven metadata fallback)`,
|
|
564
|
+
source: "transitive"
|
|
565
|
+
};
|
|
566
|
+
}
|
|
525
567
|
return {
|
|
526
568
|
file: relativeManifest,
|
|
527
569
|
module: "com.android.billingclient:billing",
|
|
528
|
-
expression: `react-native-iap ${
|
|
570
|
+
expression: `react-native-iap ${installedVersion} -> ${coordinate}; Maven Billing version lookup failed`,
|
|
529
571
|
source: "unresolved"
|
|
530
572
|
};
|
|
531
573
|
}
|
|
@@ -754,12 +796,55 @@ async function walk2(root, maxDepth = 7) {
|
|
|
754
796
|
return files;
|
|
755
797
|
}
|
|
756
798
|
function isRelevantFile(name) {
|
|
757
|
-
return name === "app.json" || name === "app.config.json" || /^app\.config\.(?:js|cjs|mjs|ts)$/.test(name) || name === "build.gradle" || name === "build.gradle.kts" || name === "libs.versions.toml" || name === "gradle.properties" || name === "AndroidManifest.xml" || name === "Info.plist" || name === "project.pbxproj" || name === "package.json";
|
|
799
|
+
return name === "app.json" || name === "app.config.json" || /^app\.config\.(?:js|cjs|mjs|ts)$/.test(name) || name === "build.gradle" || name === "build.gradle.kts" || name === "libs.versions.toml" || name === "gradle.properties" || name === "AndroidManifest.xml" || name === "Info.plist" || name === "project.pbxproj" || name === "ProjectSettings.asset" || name === "package.json";
|
|
758
800
|
}
|
|
759
801
|
function unique(values) {
|
|
760
802
|
return [...new Set(values.filter(Boolean))].sort();
|
|
761
803
|
}
|
|
762
|
-
function
|
|
804
|
+
function unityApplicationIdentifiers(text) {
|
|
805
|
+
const lines = text.split(/\r?\n/);
|
|
806
|
+
const start = lines.findIndex((line) => /^\s*applicationIdentifier:\s*$/.test(line));
|
|
807
|
+
if (start < 0) return {};
|
|
808
|
+
const baseIndent = lines[start].match(/^\s*/)?.[0].length ?? 0;
|
|
809
|
+
const result = {};
|
|
810
|
+
for (const line of lines.slice(start + 1)) {
|
|
811
|
+
if (!line.trim()) continue;
|
|
812
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
813
|
+
if (indent <= baseIndent) break;
|
|
814
|
+
const entry = line.match(/^\s+(Android|iPhone|iOS):\s*([^\s#]+)\s*$/);
|
|
815
|
+
if (entry?.[1] === "Android") result.android = entry[2];
|
|
816
|
+
if (entry && entry[1] !== "Android") result.ios = entry[2];
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
async function readStaticJsonImports(file, root) {
|
|
821
|
+
const result = /* @__PURE__ */ new Map();
|
|
822
|
+
const imports = [
|
|
823
|
+
...file.text.matchAll(/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+\.json)['"]/g),
|
|
824
|
+
...file.text.matchAll(/\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*require\(\s*['"]([^'"]+\.json)['"]\s*\)/g)
|
|
825
|
+
];
|
|
826
|
+
for (const match of imports) {
|
|
827
|
+
if (!match[2].startsWith(".")) continue;
|
|
828
|
+
const candidate = path2.resolve(path2.dirname(file.absolute), match[2]);
|
|
829
|
+
const relative = path2.relative(root, candidate);
|
|
830
|
+
if (relative.startsWith(`..${path2.sep}`) || relative === ".." || path2.isAbsolute(relative)) continue;
|
|
831
|
+
try {
|
|
832
|
+
const json = JSON.parse(await fs2.readFile(candidate, "utf8"));
|
|
833
|
+
if (json && typeof json === "object" && !Array.isArray(json)) {
|
|
834
|
+
result.set(match[1], json);
|
|
835
|
+
}
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
function resolveJsonMember(text, block, field, imports) {
|
|
842
|
+
const expression = new RegExp(`\\b${block}\\s*:\\s*\\{[\\s\\S]{0,5000}?\\b${field}\\s*:\\s*([A-Za-z_$][\\w$]*)\\.([A-Za-z_$][\\w$]*)`).exec(text);
|
|
843
|
+
if (!expression) return void 0;
|
|
844
|
+
const value = imports.get(expression[1])?.[expression[2]];
|
|
845
|
+
return typeof value === "string" ? value : void 0;
|
|
846
|
+
}
|
|
847
|
+
async function parseExpo(files, root) {
|
|
763
848
|
const androidPackageNames = [];
|
|
764
849
|
const iosBundleIds = [];
|
|
765
850
|
const platforms = /* @__PURE__ */ new Set();
|
|
@@ -778,9 +863,12 @@ function parseExpo(files) {
|
|
|
778
863
|
} catch {
|
|
779
864
|
const android = file.text.match(/\bandroid\s*:\s*\{[\s\S]{0,3000}?\bpackage\s*:\s*['"]([^'"]+)['"]/);
|
|
780
865
|
const ios = file.text.match(/\bios\s*:\s*\{[\s\S]{0,3000}?\bbundleIdentifier\s*:\s*['"]([^'"]+)['"]/);
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
866
|
+
const imports = await readStaticJsonImports(file, root);
|
|
867
|
+
const importedAndroid = resolveJsonMember(file.text, "android", "package", imports);
|
|
868
|
+
const importedIos = resolveJsonMember(file.text, "ios", "bundleIdentifier", imports);
|
|
869
|
+
if (android?.[1] || ios?.[1] || importedAndroid || importedIos || /\bexpo\s*:/.test(file.text)) detected = true;
|
|
870
|
+
if (android?.[1] || importedAndroid) androidPackageNames.push(android?.[1] ?? importedAndroid);
|
|
871
|
+
if (ios?.[1] || importedIos) iosBundleIds.push(ios?.[1] ?? importedIos);
|
|
784
872
|
}
|
|
785
873
|
}
|
|
786
874
|
for (const file of files.filter((candidate) => candidate.relative.endsWith("package.json"))) {
|
|
@@ -793,8 +881,8 @@ function parseExpo(files) {
|
|
|
793
881
|
}
|
|
794
882
|
return { androidPackageNames, iosBundleIds, platforms, detected };
|
|
795
883
|
}
|
|
796
|
-
function detectProject(files) {
|
|
797
|
-
const expo = parseExpo(files);
|
|
884
|
+
async function detectProject(files, root) {
|
|
885
|
+
const expo = await parseExpo(files, root);
|
|
798
886
|
const gradleFiles = files.filter((file) => /build\.gradle(?:\.kts)?$/.test(file.relative));
|
|
799
887
|
const pbxFiles = files.filter((file) => file.relative.endsWith("project.pbxproj"));
|
|
800
888
|
const plistFiles = files.filter((file) => file.relative.endsWith("Info.plist"));
|
|
@@ -803,13 +891,28 @@ function detectProject(files) {
|
|
|
803
891
|
const versionCatalogFiles = files.filter((file) => file.relative.endsWith("libs.versions.toml"));
|
|
804
892
|
const iosPbxFiles = pbxFiles.filter((file) => /(?:^|\/)ios\//.test(file.relative) || /\b(?:SDKROOT\s*=\s*iphoneos|IPHONEOS_DEPLOYMENT_TARGET|TARGETED_DEVICE_FAMILY)\b/.test(file.text));
|
|
805
893
|
const iosPlistFiles = plistFiles.filter((file) => /(?:^|\/)ios\//.test(file.relative) || iosPbxFiles.length > 0);
|
|
806
|
-
const
|
|
894
|
+
const unitySettingsFiles = files.filter((file) => /(?:^|\/)ProjectSettings\/ProjectSettings\.asset$/.test(file.relative));
|
|
895
|
+
const unityAndroidPackageNames = [];
|
|
896
|
+
const unityIosBundleIds = [];
|
|
897
|
+
const unityTargetSdkEvidence = [];
|
|
898
|
+
for (const file of unitySettingsFiles) {
|
|
899
|
+
const identifiers = unityApplicationIdentifiers(file.text);
|
|
900
|
+
const androidId = identifiers.android;
|
|
901
|
+
const iosId = identifiers.ios;
|
|
902
|
+
const targetSdk = file.text.match(/^\s*AndroidTargetSdkVersion:\s*(-?\d+)\s*$/m)?.[1];
|
|
903
|
+
if (androidId) unityAndroidPackageNames.push(androidId);
|
|
904
|
+
if (iosId) unityIosBundleIds.push(iosId);
|
|
905
|
+
if (targetSdk && Number.parseInt(targetSdk, 10) > 0) {
|
|
906
|
+
unityTargetSdkEvidence.push({ file: file.relative, value: Number.parseInt(targetSdk, 10) });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const androidPackageNames = [...expo.androidPackageNames, ...unityAndroidPackageNames];
|
|
807
910
|
for (const file of androidAppGradleFiles) {
|
|
808
911
|
for (const match of file.text.matchAll(/\bapplicationId\s*(?:=\s*)?["']([^"']+)["']/g)) {
|
|
809
912
|
androidPackageNames.push(match[1]);
|
|
810
913
|
}
|
|
811
914
|
}
|
|
812
|
-
const iosBundleIds = [...expo.iosBundleIds];
|
|
915
|
+
const iosBundleIds = [...expo.iosBundleIds, ...unityIosBundleIds];
|
|
813
916
|
for (const file of iosPbxFiles) {
|
|
814
917
|
for (const match of file.text.matchAll(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*([^;]+);/g)) {
|
|
815
918
|
const value = match[1].trim().replace(/^["']|["']$/g, "");
|
|
@@ -824,15 +927,16 @@ function detectProject(files) {
|
|
|
824
927
|
}
|
|
825
928
|
const expoTargetsAndroid = expo.detected && (expo.platforms.size === 0 || expo.platforms.has("android"));
|
|
826
929
|
const expoTargetsIos = expo.detected && (expo.platforms.size === 0 || expo.platforms.has("ios"));
|
|
827
|
-
const android = androidAppGradleFiles.length > 0 || expo.androidPackageNames.length > 0 || expoTargetsAndroid || androidAppManifestFiles.length > 0;
|
|
828
|
-
const ios = iosPbxFiles.length > 0 || iosPlistFiles.some((file) => /(?:^|\/)ios\//.test(file.relative)) || expo.iosBundleIds.length > 0 || expoTargetsIos;
|
|
930
|
+
const android = androidAppGradleFiles.length > 0 || expo.androidPackageNames.length > 0 || expoTargetsAndroid || androidAppManifestFiles.length > 0 || unityAndroidPackageNames.length > 0 || unityTargetSdkEvidence.length > 0;
|
|
931
|
+
const ios = iosPbxFiles.length > 0 || iosPlistFiles.some((file) => /(?:^|\/)ios\//.test(file.relative)) || expo.iosBundleIds.length > 0 || expoTargetsIos || unityIosBundleIds.length > 0;
|
|
829
932
|
const androidGradleFiles = gradleFiles.filter((file) => androidAppGradleFiles.includes(file) || /(?:^|\/)android\//.test(file.relative));
|
|
830
933
|
return {
|
|
831
934
|
android,
|
|
832
935
|
ios,
|
|
833
936
|
androidPackageNames: unique(androidPackageNames),
|
|
834
937
|
iosBundleIds: unique(iosBundleIds),
|
|
835
|
-
gradleFiles: [...androidGradleFiles, ...versionCatalogFiles]
|
|
938
|
+
gradleFiles: [...androidGradleFiles, ...versionCatalogFiles],
|
|
939
|
+
targetSdkEvidence: unityTargetSdkEvidence
|
|
836
940
|
};
|
|
837
941
|
}
|
|
838
942
|
function targetPolicy(now) {
|
|
@@ -845,10 +949,11 @@ function targetPolicy(now) {
|
|
|
845
949
|
effectiveDate: current?.effectiveDate
|
|
846
950
|
};
|
|
847
951
|
}
|
|
848
|
-
function targetSdkFindings(gradleFiles, now) {
|
|
849
|
-
const evidence = [];
|
|
952
|
+
function targetSdkFindings(gradleFiles, now, supplementalEvidence = []) {
|
|
953
|
+
const evidence = [...supplementalEvidence];
|
|
850
954
|
let hasUnresolvedExpression = false;
|
|
851
955
|
const catalogs = /* @__PURE__ */ new Map();
|
|
956
|
+
let reactNativeTargetSdk;
|
|
852
957
|
for (const file of gradleFiles.filter((candidate) => candidate.relative.endsWith("libs.versions.toml"))) {
|
|
853
958
|
let section2 = "";
|
|
854
959
|
for (const rawLine of file.text.split(/\r?\n/)) {
|
|
@@ -860,21 +965,36 @@ function targetSdkFindings(gradleFiles, now) {
|
|
|
860
965
|
}
|
|
861
966
|
if (section2 !== "versions") continue;
|
|
862
967
|
const version = line.match(/^([A-Za-z0-9_.-]+)\s*=\s*["'](\d+)["']/);
|
|
863
|
-
if (version)
|
|
968
|
+
if (version) {
|
|
969
|
+
const parsed = { value: Number.parseInt(version[2], 10), file: file.relative };
|
|
970
|
+
if (file.relative === "node_modules/react-native/gradle/libs.versions.toml" && version[1] === "targetSdk") {
|
|
971
|
+
reactNativeTargetSdk = parsed;
|
|
972
|
+
} else {
|
|
973
|
+
catalogs.set(version[1], parsed);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
864
976
|
}
|
|
865
977
|
}
|
|
866
978
|
for (const file of gradleFiles.filter((candidate) => /build\.gradle(?:\.kts)?$/.test(candidate.relative))) {
|
|
979
|
+
let resolvedIndirectly = false;
|
|
867
980
|
for (const match of file.text.matchAll(/\btargetSdk(?:Version)?\s*(?:=\s*)?(\d+)/g)) {
|
|
868
981
|
evidence.push({ file: file.relative, value: Number.parseInt(match[1], 10) });
|
|
869
982
|
}
|
|
870
983
|
for (const match of file.text.matchAll(/\btargetSdk(?:Version)?\s*(?:=\s*)?libs\.versions\.([A-Za-z0-9_.-]+?)(?=\.get\(\)|\s|$)/g)) {
|
|
871
984
|
const resolved = catalogs.get(match[1]) ?? catalogs.get(match[1].replace(/\./g, "-"));
|
|
872
|
-
if (resolved)
|
|
985
|
+
if (resolved) {
|
|
986
|
+
evidence.push({ file: resolved.file, value: resolved.value });
|
|
987
|
+
resolvedIndirectly = true;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (/\btargetSdkVersion\s+rootProject\.ext\.targetSdkVersion\b/.test(file.text) && reactNativeTargetSdk) {
|
|
991
|
+
evidence.push({ file: reactNativeTargetSdk.file, value: reactNativeTargetSdk.value });
|
|
992
|
+
resolvedIndirectly = true;
|
|
873
993
|
}
|
|
874
994
|
if (/\btargetSdk(?:Version)?\b/.test(file.text) && !/\btargetSdk(?:Version)?\s*(?:=\s*)?\d+/.test(file.text)) {
|
|
875
995
|
const catalogExpression = /\btargetSdk(?:Version)?\s*(?:=\s*)?libs\.versions\.([A-Za-z0-9_.-]+?)(?=\.get\(\)|\s|$)/.exec(file.text);
|
|
876
996
|
const resolved = catalogExpression ? catalogs.get(catalogExpression[1]) ?? catalogs.get(catalogExpression[1].replace(/\./g, "-")) : void 0;
|
|
877
|
-
if (!resolved) hasUnresolvedExpression = true;
|
|
997
|
+
if (!resolved && !resolvedIndirectly) hasUnresolvedExpression = true;
|
|
878
998
|
}
|
|
879
999
|
}
|
|
880
1000
|
const policy = targetPolicy(now);
|
|
@@ -956,7 +1076,16 @@ async function scanReleaseDoctor(projectPath, now = /* @__PURE__ */ new Date())
|
|
|
956
1076
|
}
|
|
957
1077
|
if (!stat.isDirectory()) throw new Error(`Project path is not a directory: ${root}`);
|
|
958
1078
|
const files = await walk2(root);
|
|
959
|
-
const
|
|
1079
|
+
const reactNativeCatalog = path2.join(root, "node_modules", "react-native", "gradle", "libs.versions.toml");
|
|
1080
|
+
try {
|
|
1081
|
+
files.push({
|
|
1082
|
+
absolute: reactNativeCatalog,
|
|
1083
|
+
relative: "node_modules/react-native/gradle/libs.versions.toml",
|
|
1084
|
+
text: await fs2.readFile(reactNativeCatalog, "utf8")
|
|
1085
|
+
});
|
|
1086
|
+
} catch {
|
|
1087
|
+
}
|
|
1088
|
+
const detected = await detectProject(files, root);
|
|
960
1089
|
const platforms = [];
|
|
961
1090
|
if (detected.android) platforms.push("android");
|
|
962
1091
|
if (detected.ios) platforms.push("ios");
|
|
@@ -1030,7 +1159,7 @@ async function scanReleaseDoctor(projectPath, now = /* @__PURE__ */ new Date())
|
|
|
1030
1159
|
}
|
|
1031
1160
|
});
|
|
1032
1161
|
} else {
|
|
1033
|
-
findings.push(...targetSdkFindings(detected.gradleFiles, now));
|
|
1162
|
+
findings.push(...targetSdkFindings(detected.gradleFiles, now, detected.targetSdkEvidence));
|
|
1034
1163
|
}
|
|
1035
1164
|
const billing = await checkBillingCompliance(root, now);
|
|
1036
1165
|
if (billing.status !== "not_used") {
|
|
@@ -1988,7 +2117,7 @@ async function cmdAuth(args) {
|
|
|
1988
2117
|
if (sub === "threads") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["threads", ...rest]));
|
|
1989
2118
|
if (sub === "tiktok") return void exitWith(await runMcpBin("mimi-seed-tiktok-business-auth", rest));
|
|
1990
2119
|
if (sub === "ci") {
|
|
1991
|
-
const { cmdSetup: cmdSetup2 } = await import("./setup-
|
|
2120
|
+
const { cmdSetup: cmdSetup2 } = await import("./setup-VO5WN32U.js");
|
|
1992
2121
|
await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
|
|
1993
2122
|
return;
|
|
1994
2123
|
}
|
|
@@ -2134,7 +2263,7 @@ function readJson(filePath) {
|
|
|
2134
2263
|
}
|
|
2135
2264
|
var BUILTIN_MIMI_SEED_SERVER = {
|
|
2136
2265
|
command: "npx",
|
|
2137
|
-
args: ["-y", "@yoonion/mimi-seed-mcp"]
|
|
2266
|
+
args: ["-y", "@yoonion/mimi-seed-mcp@latest"]
|
|
2138
2267
|
};
|
|
2139
2268
|
function detectMcpClient(env = process.env) {
|
|
2140
2269
|
if (env.CODEX_THREAD_ID || env.CODEX_CI || env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE) return "codex";
|
|
@@ -2188,7 +2317,8 @@ function candidateMarkers(cfg) {
|
|
|
2188
2317
|
const primary = findProcessMarker(cfg);
|
|
2189
2318
|
if (!primary) return [];
|
|
2190
2319
|
const base = primary.split("/").pop();
|
|
2191
|
-
|
|
2320
|
+
const executableBase = base?.replace(/@(?:latest|\d+(?:\.\d+){1,3}(?:[-+][\w.-]+)?)$/, "");
|
|
2321
|
+
return [...new Set([primary, base, executableBase].filter((value) => Boolean(value)))];
|
|
2192
2322
|
}
|
|
2193
2323
|
function findPids(markers) {
|
|
2194
2324
|
let out;
|
|
@@ -2463,7 +2593,7 @@ var M8 = catalog(
|
|
|
2463
2593
|
localIntro: "\uC6D0\uACA9 MCP(PAT, \uC77D\uAE30\xB7\uC9C4\uB2E8)\uB294 \uC704\uC5D0\uC11C \uB05D. \uB85C\uCEEC MCP\uB294 Google OAuth\uB85C \uC2A4\uD1A0\uC5B4 \uC4F0\uAE30 \uB3C4\uAD6C \uC804\uCCB4\uB97C \uC9C1\uC811 \uC2E4\uD589\uD569\uB2C8\uB2E4 (Node 20+).",
|
|
2464
2594
|
localStep1: "1) Google \uB85C\uADF8\uC778 (Firebase / AdMob / Play / Ads):",
|
|
2465
2595
|
localStep2: "2) \uB85C\uCEEC MCP \uC11C\uBC84 \uB4F1\uB85D (\uC6D0\uACA9 'mimi-seed' \uC640 \uBCC4\uAC1C):",
|
|
2466
|
-
localCodexHint: ' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"]',
|
|
2596
|
+
localCodexHint: ' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp@latest"]',
|
|
2467
2597
|
localStep3: "3) \uB098\uBA38\uC9C0 \uACC4\uC815 \uC5F0\uACB0 (App Store / Play / Jenkins / CI / \uC18C\uC15C \u2026):",
|
|
2468
2598
|
localSetupHint: " \uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 \uD1A0\uD070 \uBC1C\uAE09 \uBC29\uBC95\uC744 \uC54C\uB824\uC90D\uB2C8\uB2E4.",
|
|
2469
2599
|
notConnected: "\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589.",
|
|
@@ -2633,7 +2763,7 @@ ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
|
|
|
2633
2763
|
localIntro: "The remote MCP (PAT, read + diagnostics) is done above. The local MCP runs every store write tool directly via Google OAuth (Node 20+).",
|
|
2634
2764
|
localStep1: "1) Sign in with Google (Firebase / AdMob / Play / Ads):",
|
|
2635
2765
|
localStep2: "2) Register the local MCP server (separate from the remote 'mimi-seed'):",
|
|
2636
|
-
localCodexHint: ' Codex: add [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"] to ~/.codex/config.toml',
|
|
2766
|
+
localCodexHint: ' Codex: add [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp@latest"] to ~/.codex/config.toml',
|
|
2637
2767
|
localStep3: "3) Connect the remaining accounts (App Store / Play / Jenkins / CI / social \u2026):",
|
|
2638
2768
|
localSetupHint: " Press [?] on any item to see how to obtain that token.",
|
|
2639
2769
|
notConnected: "No Mimi Seed account connected. Run `mimi-seed init`.",
|
|
@@ -2933,7 +3063,7 @@ async function cmdInit(args) {
|
|
|
2933
3063
|
await cmdAuth(["login"]);
|
|
2934
3064
|
log4("");
|
|
2935
3065
|
log4(M8().localStep2);
|
|
2936
|
-
log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp"));
|
|
3066
|
+
log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp@latest"));
|
|
2937
3067
|
log4(kleur9.dim(M8().localCodexHint));
|
|
2938
3068
|
log4("");
|
|
2939
3069
|
log4(M8().localStep3);
|
package/package.json
CHANGED