@yoonion/mimi-seed-mcp 0.19.9 → 0.19.10

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/README.md CHANGED
@@ -8,6 +8,14 @@
8
8
 
9
9
  스토어 자격증명을 연결하기 전에 저장소에서 확정할 수 있는 출시 블로커를 검사합니다.
10
10
 
11
+ 일반 사용자는 전체 MCP 패키지를 먼저 설치하지 않는 경량 CLI 경로를 권장합니다.
12
+
13
+ ```bash
14
+ npx -y mimi-seed@latest check --local
15
+ ```
16
+
17
+ MCP 패키지를 이미 설치했다면 같은 검사기의 직접 bin도 사용할 수 있습니다.
18
+
11
19
  ```bash
12
20
  npx -y @yoonion/mimi-seed-mcp mimi-seed-release-doctor .
13
21
  ```
@@ -0,0 +1,2 @@
1
+ import { type BillingTransitiveResolver } from './billing.js';
2
+ export declare const resolveOpenIapBilling: BillingTransitiveResolver;
@@ -0,0 +1,9 @@
1
+ import { fetchWithTimeout } from '../lib/http.js';
2
+ import { billingVersionFromPom } from './billing.js';
3
+ export const resolveOpenIapBilling = async (openIapVersion) => {
4
+ const pomUrl = `https://repo.maven.apache.org/maven2/io/github/hyochan/openiap/openiap-google/${openIapVersion}/openiap-google-${openIapVersion}.pom`;
5
+ const response = await fetchWithTimeout(pomUrl, {}, { timeoutMs: 15_000, maxAttempts: 2 });
6
+ if (!response.ok)
7
+ return null;
8
+ return billingVersionFromPom(await response.text());
9
+ };
@@ -33,8 +33,14 @@ export interface BillingComplianceResult {
33
33
  automaticExecution: false;
34
34
  };
35
35
  }
36
+ export type BillingTransitiveResolver = (openIapVersion: string) => Promise<{
37
+ module: string;
38
+ version: string;
39
+ } | null>;
36
40
  export declare function billingVersionFromPom(pom: string): {
37
41
  module: string;
38
42
  version: string;
39
43
  } | null;
40
- export declare function checkBillingCompliance(projectPath: string, now?: Date): Promise<BillingComplianceResult>;
44
+ export declare function checkBillingCompliance(projectPath: string, now?: Date, options?: {
45
+ resolveTransitive?: BillingTransitiveResolver;
46
+ }): Promise<BillingComplianceResult>;
@@ -1,6 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { fetchWithTimeout } from '../lib/http.js';
4
3
  const BILLING_MODULE = /com\.android\.billingclient:billing(?:-ktx)?/;
5
4
  const LITERAL_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:([0-9]+(?:\.[0-9A-Za-z_-]+){0,3})/g;
6
5
  const VARIABLE_DEPENDENCY = /com\.android\.billingclient:billing(?:-ktx)?:\$\{?([A-Za-z_][A-Za-z0-9_.-]*)\}?/g;
@@ -145,7 +144,7 @@ async function nearestNodePackage(packageDir, root) {
145
144
  }
146
145
  return null;
147
146
  }
148
- async function reactNativeIapEvidence(root, manifestFile, manifestText) {
147
+ async function reactNativeIapEvidence(root, manifestFile, manifestText, resolveTransitive) {
149
148
  let manifest;
150
149
  try {
151
150
  manifest = JSON.parse(manifestText);
@@ -211,20 +210,24 @@ async function reactNativeIapEvidence(root, manifestFile, manifestText) {
211
210
  };
212
211
  }
213
212
  const coordinate = `io.github.hyochan.openiap:openiap-google:${openIapVersion}`;
213
+ if (!resolveTransitive) {
214
+ return {
215
+ file: relativeManifest,
216
+ module: 'com.android.billingclient:billing',
217
+ expression: `react-native-iap ${declaredVersion} -> ${coordinate}; transitive lookup unavailable in repository-only mode`,
218
+ source: 'unresolved',
219
+ };
220
+ }
214
221
  try {
215
- const pomUrl = `https://repo.maven.apache.org/maven2/io/github/hyochan/openiap/openiap-google/${openIapVersion}/openiap-google-${openIapVersion}.pom`;
216
- const response = await fetchWithTimeout(pomUrl, {}, { timeoutMs: 15_000, maxAttempts: 2 });
217
- if (response.ok) {
218
- const resolved = billingVersionFromPom(await response.text());
219
- if (resolved) {
220
- return {
221
- file: relativeManifest,
222
- module: resolved.module,
223
- version: resolved.version,
224
- expression: `react-native-iap ${declaredVersion} -> ${coordinate}`,
225
- source: 'transitive',
226
- };
227
- }
222
+ const resolved = await resolveTransitive(openIapVersion);
223
+ if (resolved) {
224
+ return {
225
+ file: relativeManifest,
226
+ module: resolved.module,
227
+ version: resolved.version,
228
+ expression: `react-native-iap ${declaredVersion} -> ${coordinate}`,
229
+ source: 'transitive',
230
+ };
228
231
  }
229
232
  }
230
233
  catch {
@@ -264,7 +267,7 @@ function isWithin(scope, file) {
264
267
  const relative = path.relative(scope, file);
265
268
  return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
266
269
  }
267
- export async function checkBillingCompliance(projectPath, now = new Date()) {
270
+ export async function checkBillingCompliance(projectPath, now = new Date(), options = {}) {
268
271
  const root = path.resolve(projectPath);
269
272
  let rootStat;
270
273
  try {
@@ -301,7 +304,7 @@ export async function checkBillingCompliance(projectPath, now = new Date()) {
301
304
  for (const [file, text] of texts) {
302
305
  if (path.basename(file) !== 'package.json')
303
306
  continue;
304
- const transitive = await reactNativeIapEvidence(root, file, text);
307
+ const transitive = await reactNativeIapEvidence(root, file, text, options.resolveTransitive);
305
308
  if (transitive && !evidence.some((row) => row.expression === transitive.expression))
306
309
  evidence.push(transitive);
307
310
  }
@@ -2,54 +2,7 @@
2
2
  import { resolveLang } from '../lib/lang.js';
3
3
  import { scanReleaseDoctor } from './release-doctor.js';
4
4
  import { parseReleaseDoctorArgs, RELEASE_DOCTOR_USAGE } from './release-doctor-cli-args.js';
5
- const COPY = {
6
- ko: {
7
- title: 'Mimi Seed Release Doctor — 로그인 없는 출시 점검',
8
- platforms: '감지 플랫폼',
9
- summary: (blockers, warnings) => `결과: 블로커 ${blockers} · 경고 ${warnings}`,
10
- labels: { blocker: '블로커', warning: '경고', info: '확인' },
11
- evidence: '근거',
12
- action: '조치',
13
- source: '출처',
14
- connected: '스토어 연결 후 추가 검사',
15
- connectedChecks: [
16
- '스토어 등록정보와 스크린샷',
17
- '업로드된 빌드의 존재 여부와 처리 상태',
18
- 'App Store 연령등급 응답과 Google Play 선언',
19
- '심사 제출과 단계적 출시 상태',
20
- ],
21
- ready: '로컬 검사에서 제출 차단 요인을 찾지 못했습니다.',
22
- partial: '이 결과는 저장소의 결정적 증거만 검사합니다. 스토어 상태는 연결 후 별도로 확인합니다.',
23
- },
24
- en: {
25
- title: 'Mimi Seed Release Doctor — no-login release check',
26
- platforms: 'Platforms',
27
- summary: (blockers, warnings) => `Result: ${blockers} blockers · ${warnings} warnings`,
28
- labels: { blocker: 'BLOCKER', warning: 'WARNING', info: 'INFO' },
29
- evidence: 'Evidence',
30
- action: 'Action',
31
- source: 'Source',
32
- connected: 'Checked after connecting stores',
33
- connectedChecks: undefined,
34
- ready: 'No submission blocker was found by the local checks.',
35
- partial: 'This report checks deterministic repository evidence only. Store state is checked separately after connection.',
36
- },
37
- };
38
- function renderFinding(finding, copy, lang) {
39
- const localized = lang === 'ko' ? finding.ko : undefined;
40
- const lines = [
41
- `[${copy.labels[finding.severity]}] ${localized?.title ?? finding.title}`,
42
- ` ${localized?.detail ?? finding.detail}`,
43
- ];
44
- if (finding.file)
45
- lines.push(` ${copy.evidence}: ${finding.file}`);
46
- const action = localized?.action ?? finding.action;
47
- if (action)
48
- lines.push(` ${copy.action}: ${action}`);
49
- if (finding.sourceUrl)
50
- lines.push(` ${copy.source}: ${finding.sourceUrl}`);
51
- return lines;
52
- }
5
+ import { renderReleaseDoctor } from './release-doctor-render.js';
53
6
  async function main() {
54
7
  const args = parseReleaseDoctorArgs(process.argv.slice(2));
55
8
  if (args.help) {
@@ -61,23 +14,7 @@ async function main() {
61
14
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
62
15
  }
63
16
  else {
64
- const lang = resolveLang();
65
- const copy = COPY[lang];
66
- const lines = [
67
- '',
68
- copy.title,
69
- report.projectPath,
70
- `${copy.platforms}: ${report.platforms.length ? report.platforms.join(' + ') : '-'}`,
71
- copy.summary(report.counts.blocker, report.counts.warning),
72
- '',
73
- ];
74
- for (const finding of report.findings)
75
- lines.push(...renderFinding(finding, copy, lang), '');
76
- if (report.counts.blocker === 0)
77
- lines.push(`✓ ${copy.ready}`, '');
78
- const connectedChecks = copy.connectedChecks ?? report.coverage.requiresStoreConnection;
79
- lines.push(copy.partial, '', `${copy.connected}:`, ...connectedChecks.map((item) => ` - ${item}`), '');
80
- process.stdout.write(lines.join('\n'));
17
+ process.stdout.write(renderReleaseDoctor(report, resolveLang()));
81
18
  }
82
19
  if (args.failOnBlocker && report.counts.blocker > 0)
83
20
  process.exitCode = 1;
@@ -0,0 +1,2 @@
1
+ import type { ReleaseDoctorReport } from './release-doctor.js';
2
+ export declare function renderReleaseDoctor(report: ReleaseDoctorReport, lang: 'ko' | 'en'): string;
@@ -0,0 +1,66 @@
1
+ const COPY = {
2
+ ko: {
3
+ title: 'Mimi Seed Release Doctor — 로그인 없는 출시 점검',
4
+ platforms: '감지 플랫폼',
5
+ summary: (blockers, warnings) => `결과: 블로커 ${blockers} · 경고 ${warnings}`,
6
+ labels: { blocker: '블로커', warning: '경고', info: '확인' },
7
+ evidence: '근거',
8
+ action: '조치',
9
+ source: '출처',
10
+ connected: '스토어 연결 후 추가 검사',
11
+ connectedChecks: [
12
+ '스토어 등록정보와 스크린샷',
13
+ '업로드된 빌드의 존재 여부와 처리 상태',
14
+ 'App Store 연령등급 응답과 Google Play 선언',
15
+ '심사 제출과 단계적 출시 상태',
16
+ ],
17
+ ready: '로컬 검사에서 제출 차단 요인을 찾지 못했습니다.',
18
+ partial: '이 결과는 저장소의 결정적 증거만 검사합니다. 스토어 상태는 연결 후 별도로 확인합니다.',
19
+ },
20
+ en: {
21
+ title: 'Mimi Seed Release Doctor — no-login release check',
22
+ platforms: 'Platforms',
23
+ summary: (blockers, warnings) => `Result: ${blockers} blockers · ${warnings} warnings`,
24
+ labels: { blocker: 'BLOCKER', warning: 'WARNING', info: 'INFO' },
25
+ evidence: 'Evidence',
26
+ action: 'Action',
27
+ source: 'Source',
28
+ connected: 'Checked after connecting stores',
29
+ connectedChecks: undefined,
30
+ ready: 'No submission blocker was found by the local checks.',
31
+ partial: 'This report checks deterministic repository evidence only. Store state is checked separately after connection.',
32
+ },
33
+ };
34
+ function renderFinding(finding, copy, lang) {
35
+ const localized = lang === 'ko' ? finding.ko : undefined;
36
+ const lines = [
37
+ `[${copy.labels[finding.severity]}] ${localized?.title ?? finding.title}`,
38
+ ` ${localized?.detail ?? finding.detail}`,
39
+ ];
40
+ if (finding.file)
41
+ lines.push(` ${copy.evidence}: ${finding.file}`);
42
+ const action = localized?.action ?? finding.action;
43
+ if (action)
44
+ lines.push(` ${copy.action}: ${action}`);
45
+ if (finding.sourceUrl)
46
+ lines.push(` ${copy.source}: ${finding.sourceUrl}`);
47
+ return lines;
48
+ }
49
+ export function renderReleaseDoctor(report, lang) {
50
+ const copy = COPY[lang];
51
+ const lines = [
52
+ '',
53
+ copy.title,
54
+ report.projectPath,
55
+ `${copy.platforms}: ${report.platforms.length ? report.platforms.join(' + ') : '-'}`,
56
+ copy.summary(report.counts.blocker, report.counts.warning),
57
+ '',
58
+ ];
59
+ for (const finding of report.findings)
60
+ lines.push(...renderFinding(finding, copy, lang), '');
61
+ if (report.counts.blocker === 0)
62
+ lines.push(`✓ ${copy.ready}`, '');
63
+ const connectedChecks = copy.connectedChecks ?? report.coverage.requiresStoreConnection;
64
+ lines.push(copy.partial, '', `${copy.connected}:`, ...connectedChecks.map((item) => ` - ${item}`), '');
65
+ return lines.join('\n');
66
+ }
@@ -6,6 +6,7 @@ import * as appstore from '../appstore/tools.js';
6
6
  import * as playstore from '../playstore/tools.js';
7
7
  import { jsonResult } from '../lib/mcp-response.js';
8
8
  import { checkBillingCompliance } from '../checks/billing.js';
9
+ import { resolveOpenIapBilling } from '../checks/billing-network.js';
9
10
  export function registerChecksTools(server) {
10
11
  server.tool('android_check_billing_compliance', [
11
12
  '로컬 Android 저장소의 Google Play Billing Library 버전을 읽기 전용으로 탐지하고 제출 정책 마감과 비교합니다.',
@@ -13,7 +14,7 @@ export function registerChecksTools(server) {
13
14
  '코드를 자동 수정하지 않고 공식 Android CLI Skill 설치 명령과 업그레이드 프롬프트만 반환합니다.',
14
15
  ].join(' '), {
15
16
  projectPath: z.string().optional().describe('검사할 프로젝트 절대경로 (기본: MCP 프로세스 현재 디렉터리)'),
16
- }, async ({ projectPath }) => jsonResult(await checkBillingCompliance(projectPath ?? process.cwd())));
17
+ }, async ({ projectPath }) => jsonResult(await checkBillingCompliance(projectPath ?? process.cwd(), new Date(), { resolveTransitive: resolveOpenIapBilling })));
17
18
  server.tool('playstore_check_submission_risks', [
18
19
  'Google Play 제출 전 위험 요소를 자동으로 점검합니다.',
19
20
  '블로커(반드시 수정)와 경고(권장 수정)로 분류하여 반환합니다.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoonion/mimi-seed-mcp",
3
- "version": "0.19.9",
3
+ "version": "0.19.10",
4
4
  "description": "Mimi Seed MCP server \u2014 Firebase + AdMob + Google Play + App Store management for Claude Code / Codex / Cursor / any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {