@yoonion/mimi-seed-mcp 0.3.4 → 0.3.5

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/index.js CHANGED
@@ -718,6 +718,39 @@ server.tool('playstore_submit_release', [
718
718
  const result = await playstore.submitRelease(auth, packageName, track, versionCode, status);
719
719
  return { content: [{ type: 'text', text: `✅ ${packageName} ${track} v${versionCode}: ${result.previousStatus} → ${result.newStatus}\n\n${JSON.stringify(result, null, 2)}` }] };
720
720
  });
721
+ // --- Play Store 트랙 간 promote (internal → production 등) ---
722
+ server.tool('playstore_promote_release', [
723
+ 'Google Play 트랙 간 release promote — fromTrack(예: internal)의 versionCode를 toTrack(예: production)에 새 release로 추가.',
724
+ '단일 edit session에서 source 조회 → target 업데이트 → commit 까지 한 번에 처리.',
725
+ 'source의 releaseNotes를 자동 복사 (copyReleaseNotes=false 또는 releaseNotes로 덮어쓰기 가능).',
726
+ 'status="completed" + production 으로 전체 출시, status="inProgress" + userFraction 으로 단계 출시(예: 0.1 = 10%).',
727
+ 'target에 같은 versionCode가 이미 있으면 해당 항목을 교체. status="completed"면 target 활성 release를 통째로 새 것으로 대체.',
728
+ '⚠️ status="completed"는 비가역에 가까움. playstore_check_submission_risks로 사전 점검 권장.',
729
+ ].join(' '), {
730
+ packageName: z.string().describe('패키지명 (예: gg.pryzm.coffee)'),
731
+ fromTrack: z.enum(['production', 'beta', 'alpha', 'internal']).describe('출처 트랙 (예: internal)'),
732
+ toTrack: z.enum(['production', 'beta', 'alpha', 'internal']).describe('대상 트랙 (예: production)'),
733
+ versionCode: z.string().describe('promote할 versionCode (문자열)'),
734
+ status: z.enum(['completed', 'draft', 'inProgress', 'halted']).optional().describe('대상 트랙에서의 status (기본 completed)'),
735
+ userFraction: z.number().min(0).max(1).optional().describe('status="inProgress"일 때 단계 출시 비율 (0~1, 예: 0.1)'),
736
+ releaseName: z.string().optional().describe('release 이름 (미지정 시 source 이름 그대로)'),
737
+ copyReleaseNotes: z.boolean().optional().describe('source releaseNotes 복사 여부 (기본 true)'),
738
+ releaseNotes: z.array(z.object({
739
+ language: z.string().describe('언어 코드 (예: ko-KR, en-US)'),
740
+ text: z.string().describe('릴리스 노트 본문 (500자 이내)'),
741
+ })).optional().describe('대상 트랙용 릴리스 노트 (미지정 + copyReleaseNotes=true면 source 그대로)'),
742
+ }, async ({ packageName, fromTrack, toTrack, versionCode, status, userFraction, releaseName, copyReleaseNotes, releaseNotes }) => {
743
+ const auth = requireAuth();
744
+ const result = await playstore.promoteRelease(auth, packageName, fromTrack, toTrack, versionCode, {
745
+ status,
746
+ userFraction,
747
+ releaseName,
748
+ copyReleaseNotes,
749
+ releaseNotes,
750
+ });
751
+ const summary = `✅ ${packageName} ${fromTrack} → ${toTrack} v${versionCode} (status: ${result.newStatus}${result.userFraction != null ? `, userFraction: ${result.userFraction}` : ''})`;
752
+ return { content: [{ type: 'text', text: `${summary}\n\n${JSON.stringify(result, null, 2)}` }] };
753
+ });
721
754
  // --- 인증 상태 ---
722
755
  server.tool('mimi_seed_auth_start', [
723
756
  'Google OAuth 로그인 링크를 발급하고 백그라운드 콜백 서버를 시작.',
@@ -62,6 +62,28 @@ export declare function submitRelease(auth: OAuth2Client, packageName: string, t
62
62
  committed: boolean;
63
63
  result: import("googleapis").androidpublisher_v3.Schema$Track;
64
64
  }>;
65
+ export interface PromoteReleaseOptions {
66
+ status?: 'completed' | 'draft' | 'inProgress' | 'halted';
67
+ userFraction?: number;
68
+ releaseName?: string;
69
+ releaseNotes?: Array<{
70
+ language: string;
71
+ text: string;
72
+ }>;
73
+ copyReleaseNotes?: boolean;
74
+ }
75
+ export declare function promoteRelease(auth: OAuth2Client, packageName: string, fromTrack: string, toTrack: string, versionCode: string, options?: PromoteReleaseOptions): Promise<{
76
+ packageName: string;
77
+ fromTrack: string;
78
+ toTrack: string;
79
+ versionCode: string;
80
+ newStatus: "completed" | "draft" | "inProgress" | "halted";
81
+ userFraction: number | undefined;
82
+ releaseName: string | null | undefined;
83
+ releaseNotesLanguages: (string | null | undefined)[];
84
+ committed: boolean;
85
+ result: import("googleapis").androidpublisher_v3.Schema$Track;
86
+ }>;
65
87
  export declare function listReviews(auth: OAuth2Client, packageName: string): Promise<{
66
88
  reviewId: string | null | undefined;
67
89
  authorName: string | null | undefined;
@@ -262,6 +262,79 @@ export async function submitRelease(auth, packageName, track, versionCode, statu
262
262
  };
263
263
  }, true);
264
264
  }
265
+ export async function promoteRelease(auth, packageName, fromTrack, toTrack, versionCode, options = {}) {
266
+ const { status = 'completed', userFraction, releaseName, releaseNotes, copyReleaseNotes = true, } = options;
267
+ if (fromTrack === toTrack) {
268
+ throw new Error('fromTrack과 toTrack이 같아. 다른 트랙으로 promote 해야 의미 있어.');
269
+ }
270
+ if (status === 'inProgress' && (userFraction == null || userFraction <= 0 || userFraction >= 1)) {
271
+ throw new Error('status="inProgress"일 때 userFraction은 0과 1 사이 필수 (예: 0.1 → 10%).');
272
+ }
273
+ return withEdit(auth, packageName, async (editId) => {
274
+ // 1. source 트랙에서 versionCode 매칭 release 찾기
275
+ const fromData = await publisher().edits.tracks.get({
276
+ auth, packageName, editId, track: fromTrack,
277
+ });
278
+ const fromReleases = fromData.data.releases ?? [];
279
+ const sourceRelease = fromReleases.find((r) => (r.versionCodes ?? []).some((v) => String(v) === String(versionCode)));
280
+ if (!sourceRelease) {
281
+ const available = fromReleases.map((r) => ({
282
+ name: r.name,
283
+ versionCodes: r.versionCodes,
284
+ status: r.status,
285
+ }));
286
+ throw new Error(`versionCode "${versionCode}"를 ${fromTrack} 트랙에서 찾을 수 없어. 가능한 릴리스: ${JSON.stringify(available)}`);
287
+ }
288
+ // 2. 새 release 객체 구성
289
+ const newRelease = {
290
+ name: releaseName ?? sourceRelease.name ?? versionCode,
291
+ versionCodes: [String(versionCode)],
292
+ status,
293
+ releaseNotes: releaseNotes ??
294
+ (copyReleaseNotes ? sourceRelease.releaseNotes ?? [] : []),
295
+ };
296
+ if (status === 'inProgress' && userFraction != null) {
297
+ newRelease.userFraction = userFraction;
298
+ }
299
+ // 3. target 트랙 현재 상태 조회 후 merge
300
+ // - 같은 versionCode가 이미 target에 있으면 그 항목을 새 release로 교체
301
+ // - status='completed'면 활성 release가 1개여야 하므로 [newRelease]로 통째 교체
302
+ // - 그 외(draft/inProgress)는 기존 release에 append
303
+ const toData = await publisher().edits.tracks.get({
304
+ auth, packageName, editId, track: toTrack,
305
+ });
306
+ const toReleases = toData.data.releases ?? [];
307
+ const existingIdx = toReleases.findIndex((r) => (r.versionCodes ?? []).some((v) => String(v) === String(versionCode)));
308
+ let mergedReleases;
309
+ if (existingIdx >= 0) {
310
+ mergedReleases = [...toReleases];
311
+ mergedReleases[existingIdx] = newRelease;
312
+ }
313
+ else if (status === 'completed') {
314
+ mergedReleases = [newRelease];
315
+ }
316
+ else {
317
+ mergedReleases = [...toReleases, newRelease];
318
+ }
319
+ // 4. target 트랙 업데이트 (commit은 withEdit 마지막에)
320
+ const updated = await publisher().edits.tracks.update({
321
+ auth, packageName, editId, track: toTrack,
322
+ requestBody: { track: toTrack, releases: mergedReleases },
323
+ });
324
+ return {
325
+ packageName,
326
+ fromTrack,
327
+ toTrack,
328
+ versionCode: String(versionCode),
329
+ newStatus: status,
330
+ userFraction: status === 'inProgress' ? userFraction : undefined,
331
+ releaseName: newRelease.name,
332
+ releaseNotesLanguages: (newRelease.releaseNotes ?? []).map((n) => n.language),
333
+ committed: true,
334
+ result: updated.data,
335
+ };
336
+ }, true);
337
+ }
265
338
  export async function listReviews(auth, packageName) {
266
339
  const res = await publisher().reviews.list({ auth, packageName });
267
340
  return (res.data.reviews ?? []).map((r) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoonion/mimi-seed-mcp",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Mimi Seed MCP server — Firebase + AdMob + Google Play + App Store management for Claude Code / Cursor / any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {