@yoonion/mimi-seed-mcp 0.13.7 → 0.13.9
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/appstore/tools.d.ts
CHANGED
|
@@ -166,6 +166,7 @@ export declare function submitVersionForReview(versionId: string): Promise<{
|
|
|
166
166
|
versionId: string;
|
|
167
167
|
reusedSubmission: boolean;
|
|
168
168
|
itemAttached: boolean;
|
|
169
|
+
recoveredFromStaleSubmission: boolean;
|
|
169
170
|
state: any;
|
|
170
171
|
}>;
|
|
171
172
|
export declare function addProductToReviewSubmission(args: {
|
package/dist/appstore/tools.js
CHANGED
|
@@ -599,38 +599,73 @@ export async function buildSubmitForReviewPreview(versionId) {
|
|
|
599
599
|
whatsNewByLocale,
|
|
600
600
|
};
|
|
601
601
|
}
|
|
602
|
+
async function createReviewSubmission(appId, platform) {
|
|
603
|
+
const created = await apiPost('/reviewSubmissions', {
|
|
604
|
+
data: {
|
|
605
|
+
type: 'reviewSubmissions',
|
|
606
|
+
attributes: { platform },
|
|
607
|
+
relationships: {
|
|
608
|
+
app: { data: { type: 'apps', id: appId } },
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
const submissionId = created?.data?.id;
|
|
613
|
+
if (!submissionId) {
|
|
614
|
+
throw new Error(`reviewSubmission 생성 응답에 id가 없어: ${JSON.stringify(created)}`);
|
|
615
|
+
}
|
|
616
|
+
return submissionId;
|
|
617
|
+
}
|
|
618
|
+
function isItemAddRejected(error) {
|
|
619
|
+
const cause = error?.cause;
|
|
620
|
+
if (cause?.status !== 409)
|
|
621
|
+
return false;
|
|
622
|
+
return (cause.parsedErrors ?? []).some((e) => (e.code ?? '').startsWith('STATE_ERROR'));
|
|
623
|
+
}
|
|
602
624
|
export async function submitVersionForReview(versionId) {
|
|
603
625
|
const { appId, platform } = await getVersionAppAndPlatform(versionId);
|
|
604
|
-
// 1.
|
|
626
|
+
// 1. 열린 reviewSubmission이 있으면 재사용, 없으면 새로 생성
|
|
605
627
|
let submissionId = await findOpenReviewSubmission(appId, platform);
|
|
606
|
-
|
|
628
|
+
let reusedSubmission = Boolean(submissionId);
|
|
629
|
+
// findOpenReviewSubmission 은 WAITING_FOR_REVIEW 도 잡아온다. 그 상태의 실제 진행도는
|
|
630
|
+
// API 의 state 필드보다 앞서 있을 수 있어(실측: 이미 심사 큐를 탄 옛 제출), 항목 추가
|
|
631
|
+
// 자체를 거부당하는 경우가 있다 — 아래 recoveredFromStaleSubmission 이 그 케이스다.
|
|
632
|
+
let recoveredFromStaleSubmission = false;
|
|
607
633
|
if (!submissionId) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
type: 'reviewSubmissions',
|
|
611
|
-
attributes: { platform },
|
|
612
|
-
relationships: {
|
|
613
|
-
app: { data: { type: 'apps', id: appId } },
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
});
|
|
617
|
-
submissionId = created?.data?.id;
|
|
618
|
-
if (!submissionId) {
|
|
619
|
-
throw new Error(`reviewSubmission 생성 응답에 id가 없어: ${JSON.stringify(created)}`);
|
|
620
|
-
}
|
|
634
|
+
submissionId = await createReviewSubmission(appId, platform);
|
|
635
|
+
reusedSubmission = false;
|
|
621
636
|
}
|
|
622
637
|
// 2. 버전을 reviewSubmissionItems로 attach (이미 붙어있으면 skip)
|
|
623
|
-
|
|
638
|
+
let alreadyAttached = reusedSubmission ? await isVersionAttached(submissionId, versionId) : false;
|
|
624
639
|
if (!alreadyAttached) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
640
|
+
try {
|
|
641
|
+
await apiPost('/reviewSubmissionItems', {
|
|
642
|
+
data: {
|
|
643
|
+
type: 'reviewSubmissionItems',
|
|
644
|
+
relationships: {
|
|
645
|
+
reviewSubmission: { data: { type: 'reviewSubmissions', id: submissionId } },
|
|
646
|
+
appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } },
|
|
647
|
+
},
|
|
631
648
|
},
|
|
632
|
-
}
|
|
633
|
-
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
if (!reusedSubmission || !isItemAddRejected(error))
|
|
653
|
+
throw error;
|
|
654
|
+
// 재사용하려던 묶음이 실제로는 잠겨 있었다 — 새 묶음을 만들어 한 번만 재시도한다.
|
|
655
|
+
submissionId = await createReviewSubmission(appId, platform);
|
|
656
|
+
reusedSubmission = false;
|
|
657
|
+
recoveredFromStaleSubmission = true;
|
|
658
|
+
alreadyAttached = false;
|
|
659
|
+
await apiPost('/reviewSubmissionItems', {
|
|
660
|
+
data: {
|
|
661
|
+
type: 'reviewSubmissionItems',
|
|
662
|
+
relationships: {
|
|
663
|
+
reviewSubmission: { data: { type: 'reviewSubmissions', id: submissionId } },
|
|
664
|
+
appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } },
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
}
|
|
634
669
|
}
|
|
635
670
|
// 3. PATCH submitted=true → state: CREATED → WAITING_FOR_REVIEW
|
|
636
671
|
const submitted = await apiPatch(`/reviewSubmissions/${submissionId}`, {
|
|
@@ -647,6 +682,7 @@ export async function submitVersionForReview(versionId) {
|
|
|
647
682
|
versionId,
|
|
648
683
|
reusedSubmission,
|
|
649
684
|
itemAttached: !alreadyAttached,
|
|
685
|
+
recoveredFromStaleSubmission,
|
|
650
686
|
state: submitted?.data?.attributes?.state ?? 'WAITING_FOR_REVIEW',
|
|
651
687
|
};
|
|
652
688
|
}
|
package/dist/auth/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import readline from 'node:readline';
|
|
3
|
-
import open from 'open';
|
|
4
3
|
import { startAuth, getStoredTokens, ensureFreshAccessToken, } from './google-auth.js';
|
|
5
4
|
import { AuthError, classifyError } from './errors.js';
|
|
6
5
|
import { getMcpOAuthClient } from './constants.js';
|
|
7
6
|
import { AUTH_DOMAINS, DOMAIN_IDS, parseDomainList, summarizeGrantedDomains, } from './scopes.js';
|
|
7
|
+
import { openPrivateBrowser } from './browser.js';
|
|
8
8
|
import { resolveLang } from '../lib/lang.js';
|
|
9
9
|
// ko 가 원본이고 en 은 `typeof ko` 를 만족해야 한다 — 키를 빠뜨리면 컴파일이 깨진다.
|
|
10
10
|
// 여기 있는 건 전부 **터미널에 찍히는 사람용 문자열**이다. errors.ts 가 만드는
|
|
@@ -60,7 +60,7 @@ const ko = {
|
|
|
60
60
|
serverStart: ' 🌐 OAuth 콜백 서버 시작: http://localhost:9876/callback',
|
|
61
61
|
serverFail: ' ❌ 콜백 서버 시작 실패',
|
|
62
62
|
pasteUrl: ' 📋 아래 URL을 브라우저에 직접 붙여넣으세요:',
|
|
63
|
-
openingBrowser: ' 🌐
|
|
63
|
+
openingBrowser: ' 🌐 시크릿 브라우저 자동 열기...',
|
|
64
64
|
openingHint: ' (실패 시 --no-browser 로 URL 직접 받기)',
|
|
65
65
|
openFail: (msg) => ` ⚠️ 브라우저 자동 열기 실패: ${msg}`,
|
|
66
66
|
openManually: ' 📋 직접 열어주세요:',
|
|
@@ -124,7 +124,7 @@ const en = {
|
|
|
124
124
|
serverStart: ' 🌐 Starting the OAuth callback server: http://localhost:9876/callback',
|
|
125
125
|
serverFail: ' ❌ Failed to start the callback server',
|
|
126
126
|
pasteUrl: ' 📋 Paste this URL into your browser:',
|
|
127
|
-
openingBrowser: ' 🌐 Opening
|
|
127
|
+
openingBrowser: ' 🌐 Opening a private browser window...',
|
|
128
128
|
openingHint: ' (if that fails, use --no-browser to get the URL)',
|
|
129
129
|
openFail: (msg) => ` ⚠️ Could not open the browser: ${msg}`,
|
|
130
130
|
openManually: ' 📋 Please open it yourself:',
|
|
@@ -337,7 +337,7 @@ async function cmdLogin() {
|
|
|
337
337
|
else {
|
|
338
338
|
err(M.openingBrowser);
|
|
339
339
|
try {
|
|
340
|
-
await
|
|
340
|
+
await openPrivateBrowser(url);
|
|
341
341
|
err(M.openingHint);
|
|
342
342
|
}
|
|
343
343
|
catch (e) {
|
|
@@ -31,7 +31,7 @@ export declare function getAuthenticatedClient(): ReturnType<typeof createOAuth2
|
|
|
31
31
|
/**
|
|
32
32
|
* OAuth 플로우 시작.
|
|
33
33
|
* URL과 대기 Promise를 즉시 반환. localhost:9876 콜백 서버는 백그라운드로 실행.
|
|
34
|
-
* 호출자가 URL을 사용자에게 전달하거나
|
|
34
|
+
* 호출자가 URL을 사용자에게 전달하거나 private 브라우저를 직접 연다.
|
|
35
35
|
* `wait` Promise: 토큰 저장 시 resolve, 타임아웃/에러 시 reject.
|
|
36
36
|
* 재호출 시 기존 세션 자동 정리.
|
|
37
37
|
*
|
|
@@ -47,7 +47,7 @@ export declare function startAuth(clientId: string, clientSecret: string, option
|
|
|
47
47
|
wait: Promise<StoredTokens>;
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
|
-
* Interactive login — opens browser, waits for callback.
|
|
50
|
+
* Interactive login — opens a private browser window, waits for callback.
|
|
51
51
|
* startAuth() 래퍼 — CLI에서 사용.
|
|
52
52
|
*/
|
|
53
53
|
export declare function login(clientId: string, clientSecret: string, options?: {
|
package/dist/auth/google-auth.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { google } from 'googleapis';
|
|
2
2
|
import http from 'node:http';
|
|
3
|
-
import open from 'open';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import os from 'node:os';
|
|
7
6
|
import { getMcpOAuthClient } from './constants.js';
|
|
8
7
|
import { AuthError, classifyError } from './errors.js';
|
|
8
|
+
import { openPrivateBrowser } from './browser.js';
|
|
9
9
|
// 스코프 목록의 SSOT 는 scopes.ts (도메인 → 스코프 매핑). 여기서는 로그인 요청 조립만 한다.
|
|
10
10
|
import { scopesForDomains, mergeScopeStrings } from './scopes.js';
|
|
11
11
|
// Primary config dir. Legacy `~/.preseed` is read as a fallback during the
|
|
@@ -116,7 +116,7 @@ let activeAuthServer = null;
|
|
|
116
116
|
/**
|
|
117
117
|
* OAuth 플로우 시작.
|
|
118
118
|
* URL과 대기 Promise를 즉시 반환. localhost:9876 콜백 서버는 백그라운드로 실행.
|
|
119
|
-
* 호출자가 URL을 사용자에게 전달하거나
|
|
119
|
+
* 호출자가 URL을 사용자에게 전달하거나 private 브라우저를 직접 연다.
|
|
120
120
|
* `wait` Promise: 토큰 저장 시 resolve, 타임아웃/에러 시 reject.
|
|
121
121
|
* 재호출 시 기존 세션 자동 정리.
|
|
122
122
|
*
|
|
@@ -138,7 +138,10 @@ export function startAuth(clientId, clientSecret, options = {}) {
|
|
|
138
138
|
const authUrl = oauth2Client.generateAuthUrl({
|
|
139
139
|
access_type: 'offline',
|
|
140
140
|
scope: requestedScopes,
|
|
141
|
-
|
|
141
|
+
// Private windows can still share cookies with an already-running private session.
|
|
142
|
+
// Force Google to show the account chooser so an unrelated signed-in account is
|
|
143
|
+
// never selected implicitly.
|
|
144
|
+
prompt: 'consent select_account',
|
|
142
145
|
include_granted_scopes: true,
|
|
143
146
|
});
|
|
144
147
|
const wait = new Promise((resolve, reject) => {
|
|
@@ -268,13 +271,13 @@ export function startAuth(clientId, clientSecret, options = {}) {
|
|
|
268
271
|
return { url: authUrl, wait };
|
|
269
272
|
}
|
|
270
273
|
/**
|
|
271
|
-
* Interactive login — opens browser, waits for callback.
|
|
274
|
+
* Interactive login — opens a private browser window, waits for callback.
|
|
272
275
|
* startAuth() 래퍼 — CLI에서 사용.
|
|
273
276
|
*/
|
|
274
277
|
export async function login(clientId, clientSecret, options = {}) {
|
|
275
278
|
const { url, wait } = startAuth(clientId, clientSecret, options);
|
|
276
|
-
console.log('🔐 브라우저에서 Google
|
|
277
|
-
|
|
279
|
+
console.log('🔐 시크릿 브라우저에서 Google 계정 선택 중...');
|
|
280
|
+
await openPrivateBrowser(url);
|
|
278
281
|
return wait;
|
|
279
282
|
}
|
|
280
283
|
/**
|
package/package.json
CHANGED