@yoonion/mimi-seed-mcp 0.19.18 → 0.20.0
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 +17 -10
- package/assets/agent-guide.md +108 -16
- package/dist/admob/tools.d.ts +16 -15
- package/dist/admob/tools.js +6 -5
- package/dist/android/keystore-store.d.ts +30 -0
- package/dist/android/keystore-store.js +85 -0
- package/dist/android/keystore.d.ts +3 -0
- package/dist/android/keystore.js +12 -3
- package/dist/android/messages.d.ts +35 -0
- package/dist/android/messages.js +145 -0
- package/dist/android/playstore-sa.d.ts +18 -0
- package/dist/android/playstore-sa.js +15 -0
- package/dist/appstore/analytics.js +5 -4
- package/dist/appstore/apps.d.ts +81 -0
- package/dist/appstore/apps.js +174 -0
- package/dist/appstore/client.d.ts +21 -0
- package/dist/appstore/client.js +130 -0
- package/dist/appstore/declarations.js +9 -8
- package/dist/appstore/messages.d.ts +15 -0
- package/dist/appstore/messages.js +104 -0
- package/dist/appstore/previews.js +5 -4
- package/dist/appstore/product-localization.js +5 -4
- package/dist/appstore/product-review.js +8 -7
- package/dist/appstore/products.d.ts +66 -0
- package/dist/appstore/products.js +305 -0
- package/dist/appstore/release.js +6 -5
- package/dist/appstore/review-submission.d.ts +109 -0
- package/dist/appstore/review-submission.js +468 -0
- package/dist/appstore/screenshots.d.ts +11 -1
- package/dist/appstore/screenshots.js +5 -5
- package/dist/appstore/testflight.js +14 -13
- package/dist/appstore/tools.d.ts +16 -323
- package/dist/appstore/tools.js +16 -1318
- package/dist/appstore/types.d.ts +35 -0
- package/dist/appstore/types.js +7 -0
- package/dist/appstore/versions.d.ts +113 -0
- package/dist/appstore/versions.js +263 -0
- package/dist/auth/google-auth.d.ts +7 -0
- package/dist/auth/google-auth.js +61 -8
- package/dist/auth/playstore-auth.d.ts +6 -0
- package/dist/auth/playstore-auth.js +22 -3
- package/dist/bigquery/ids.d.ts +4 -0
- package/dist/bigquery/ids.js +30 -0
- package/dist/bigquery/tools.d.ts +20 -2
- package/dist/bigquery/tools.js +134 -6
- package/dist/billing/tools.js +4 -4
- package/dist/checks/plan.js +1 -1
- package/dist/checks/release-doctor.d.ts +5 -1
- package/dist/checks/release-doctor.js +728 -17
- package/dist/ci/github.d.ts +4 -4
- package/dist/ci/github.js +7 -6
- package/dist/ci/gitlab.js +3 -2
- package/dist/facebook/api.js +7 -6
- package/dist/firebase/remote-config.js +5 -3
- package/dist/firebase/tools.d.ts +1 -1
- package/dist/firebase/tools.js +34 -25
- package/dist/ga4/tools.js +4 -6
- package/dist/googleads/tools.d.ts +14 -14
- package/dist/googleads/tools.js +2 -2
- package/dist/gsc/tools.js +9 -4
- package/dist/iam/key-files.d.ts +14 -0
- package/dist/iam/key-files.js +49 -0
- package/dist/iam/tools.d.ts +10 -0
- package/dist/iam/tools.js +31 -10
- package/dist/instagram/api.js +9 -8
- package/dist/jenkins/config.d.ts +5 -0
- package/dist/jenkins/config.js +40 -0
- package/dist/jenkins/credentials.d.ts +40 -2
- package/dist/jenkins/credentials.js +130 -24
- package/dist/jenkins/messages.d.ts +26 -0
- package/dist/jenkins/messages.js +105 -0
- package/dist/lib/atomic-write.d.ts +27 -3
- package/dist/lib/atomic-write.js +41 -2
- package/dist/lib/google-timeouts.d.ts +47 -0
- package/dist/lib/google-timeouts.js +55 -0
- package/dist/lib/googleapis-lite.d.ts +11 -0
- package/dist/lib/googleapis-lite.js +6 -0
- package/dist/lib/mcp-response.d.ts +5 -0
- package/dist/lib/mcp-response.js +9 -2
- package/dist/lib/package-name.d.ts +15 -0
- package/dist/lib/package-name.js +42 -0
- package/dist/lib/package-root.d.ts +33 -0
- package/dist/lib/paginate.d.ts +19 -0
- package/dist/lib/paginate.js +38 -0
- package/dist/lib/path-containment.d.ts +4 -0
- package/dist/lib/path-containment.js +39 -0
- package/dist/lib/resource-id.d.ts +18 -0
- package/dist/lib/resource-id.js +85 -0
- package/dist/lib/tool-registrar.d.ts +64 -0
- package/dist/lib/tool-registrar.js +163 -0
- package/dist/lib/toolsets.d.ts +39 -0
- package/dist/lib/toolsets.js +104 -0
- package/dist/lib/url-path.d.ts +1 -0
- package/dist/lib/url-path.js +16 -0
- package/dist/lib/wrap-domain.d.ts +2 -0
- package/dist/lib/wrap-domain.js +35 -0
- package/dist/playstore/data-safety.d.ts +16 -0
- package/dist/playstore/data-safety.js +38 -0
- package/dist/playstore/edits.d.ts +13 -0
- package/dist/playstore/edits.js +53 -0
- package/dist/playstore/images.d.ts +21 -0
- package/dist/playstore/images.js +73 -0
- package/dist/playstore/listing.d.ts +14 -0
- package/dist/playstore/listing.js +84 -0
- package/dist/playstore/messages.d.ts +48 -0
- package/dist/playstore/messages.js +173 -0
- package/dist/playstore/products.d.ts +52 -0
- package/dist/playstore/products.js +152 -0
- package/dist/playstore/recovery.d.ts +39 -0
- package/dist/playstore/recovery.js +76 -0
- package/dist/playstore/releases.d.ts +56 -0
- package/dist/playstore/releases.js +250 -0
- package/dist/playstore/reviews.d.ts +12 -0
- package/dist/playstore/reviews.js +27 -0
- package/dist/playstore/service-account.d.ts +11 -0
- package/dist/playstore/service-account.js +67 -0
- package/dist/playstore/statistics.d.ts +16 -0
- package/dist/playstore/statistics.js +66 -0
- package/dist/playstore/tools.d.ts +24 -223
- package/dist/playstore/tools.js +24 -820
- package/dist/prompts.js +1 -1
- package/dist/registers/admob.d.ts +2 -2
- package/dist/registers/admob.js +16 -26
- package/dist/registers/ai.d.ts +2 -2
- package/dist/registers/ai.js +3 -2
- package/dist/registers/android.d.ts +2 -2
- package/dist/registers/android.js +59 -158
- package/dist/registers/appstore.d.ts +2 -2
- package/dist/registers/appstore.js +218 -477
- package/dist/registers/auth.d.ts +2 -2
- package/dist/registers/auth.js +37 -55
- package/dist/registers/bigquery.d.ts +2 -2
- package/dist/registers/bigquery.js +27 -35
- package/dist/registers/billing.d.ts +2 -2
- package/dist/registers/billing.js +11 -18
- package/dist/registers/checks.d.ts +2 -2
- package/dist/registers/checks.js +8 -7
- package/dist/registers/ci.d.ts +2 -2
- package/dist/registers/ci.js +26 -41
- package/dist/registers/facebook.d.ts +2 -2
- package/dist/registers/facebook.js +26 -53
- package/dist/registers/firebase.d.ts +2 -2
- package/dist/registers/firebase.js +16 -41
- package/dist/registers/ga4.d.ts +2 -2
- package/dist/registers/ga4.js +5 -23
- package/dist/registers/googleads.d.ts +2 -2
- package/dist/registers/googleads.js +40 -60
- package/dist/registers/gsc.d.ts +2 -2
- package/dist/registers/gsc.js +6 -11
- package/dist/registers/iam.d.ts +2 -2
- package/dist/registers/iam.js +36 -56
- package/dist/registers/instagram.d.ts +2 -2
- package/dist/registers/instagram.js +18 -33
- package/dist/registers/jenkins.d.ts +2 -2
- package/dist/registers/jenkins.js +51 -53
- package/dist/registers/playstore.d.ts +2 -2
- package/dist/registers/playstore.js +130 -353
- package/dist/registers/threads.d.ts +2 -2
- package/dist/registers/threads.js +29 -54
- package/dist/registers/tiktok.d.ts +2 -2
- package/dist/registers/tiktok.js +0 -1
- package/dist/registers/video.d.ts +2 -2
- package/dist/registers/video.js +21 -23
- package/dist/registers/youtube.d.ts +2 -2
- package/dist/remote-sync.d.ts +17 -0
- package/dist/remote-sync.js +53 -3
- package/dist/resources.d.ts +44 -1
- package/dist/resources.js +68 -17
- package/dist/server.d.ts +3 -1
- package/dist/server.js +35 -24
- package/dist/threads/api.js +4 -3
- package/dist/video/render.d.ts +15 -0
- package/dist/video/render.js +58 -6
- package/dist/video/schemas.d.ts +10 -10
- package/dist/video/youtube-publish.js +5 -3
- package/dist/youtube/tools.js +2 -1
- package/package.json +15 -14
- package/tool-manifest.json +442 -7
package/README.md
CHANGED
|
@@ -8,7 +8,8 @@ Start with a lightweight, account-free check in your app repository (Node 20+):
|
|
|
8
8
|
npx -y mimi-seed@latest check --local
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Review identifiers, Target API
|
|
11
|
+
Review identifiers, Target API, detectable Play Billing, pinned Xcode / App Store SDK minimum and legacy FCM API
|
|
12
|
+
evidence first. Then connect stores for
|
|
12
13
|
metadata, screenshot and uploaded-build checks. Local results do not guarantee store approval.
|
|
13
14
|
|
|
14
15
|
[Install the plugin for Claude Code or Codex](https://github.com/jeonghwanko/mimi-seed-sdk#30-second-setup)
|
|
@@ -40,7 +41,8 @@ MCP 패키지를 이미 설치했다면 같은 검사기의 직접 bin도 사용
|
|
|
40
41
|
npx -y @yoonion/mimi-seed-mcp mimi-seed-release-doctor .
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
앱 식별자, 일반 Android 앱의 Target API 제출 기준, Google Play Billing 지원
|
|
44
|
+
앱 식별자, 일반 Android 앱의 Target API 제출 기준, Google Play Billing 지원 마감, 고정된 Xcode의 App Store
|
|
45
|
+
SDK 최소 요건, 레거시 FCM API 사용을 확인하며 정책
|
|
44
46
|
결과에는 근거 파일과 공식 출처를 함께 표시합니다. 모노레포에서는 `.` 대신 앱 경로를 전달하세요.
|
|
45
47
|
CI에서는 `--fail-on-blocker`, 기계가 읽는 결과에는 `--json`을 추가할 수 있습니다. Wear OS, Android
|
|
46
48
|
TV, Android Automotive OS, Android XR은 Target API 예외가 있으므로 카테고리 확인 경고를 냅니다.
|
|
@@ -125,32 +127,36 @@ export ANTHROPIC_API_KEY=sk-ant-...
|
|
|
125
127
|
|
|
126
128
|
---
|
|
127
129
|
|
|
130
|
+
<!-- generated:readme-tools-heading:start — edit scripts/docs-spec.mjs, then npm run plugin:sync -->
|
|
128
131
|
## 제공 도구 (150+ 개 · 22개 영역)
|
|
132
|
+
<!-- generated:readme-tools-heading:end -->
|
|
129
133
|
|
|
134
|
+
<!-- generated:readme-tools-table:start — edit scripts/docs-spec.mjs, then npm run plugin:sync -->
|
|
130
135
|
| 영역 | 도구 수 | 주요 도구 |
|
|
131
136
|
|------|---------|-----------|
|
|
132
137
|
| App Store Connect | 64 | `appstore_submit_for_review` / `appstore_get_weekly_insight` / `appstore_update_product_review_note` / `appstore_upload_product_review_screenshot` |
|
|
133
138
|
| Google Play | 39 | `playstore_submit_release` / `playstore_promote_release` / `playstore_replace_images` / `playstore_reply_review` / `playstore_verify_service_account` |
|
|
134
139
|
| Firebase | 21 | `firebase_create_project` / `firebase_get_remote_config_overview` / `firebase_get_android_config` / `firebase_create_ios_app` |
|
|
135
140
|
| AdMob | 7 | `admob_list_apps` / `admob_create_ad_unit` / `admob_get_today_earnings` / `admob_get_report` |
|
|
136
|
-
| CI
|
|
141
|
+
| CI (GitHub Actions / GitLab) | 6 | `ci_trigger_build` / `ci_get_build_status` / `ci_list_workflows` / `ci_cancel_build` |
|
|
137
142
|
| Jenkins (크리덴셜 + 잡) | 10 | `jenkins_create_credential` / `jenkins_upload_keystore` / `jenkins_create_job` / `jenkins_update_job` |
|
|
138
|
-
| GA4 | 8 | `ga4_create_property` / `ga4_create_data_stream` / `ga4_plan_bigquery_link` / `ga4_create_bigquery_link` / `ga4_run_report` |
|
|
143
|
+
| GA4 (Google Analytics 4) | 8 | `ga4_create_property` / `ga4_create_data_stream` / `ga4_plan_bigquery_link` / `ga4_create_bigquery_link` / `ga4_run_report` |
|
|
139
144
|
| Search Console | 6 | `gsc_inspect_url` / `gsc_search_analytics` / `gsc_submit_sitemap` |
|
|
140
145
|
| Google Ads | 6 | `googleads_list_campaigns` / `googleads_get_uac_report` / `googleads_get_campaign_report` |
|
|
141
146
|
| Facebook | 6 | `facebook_post_photo` / `facebook_post_multi_photo` / `facebook_list_pages` |
|
|
142
147
|
| Google Cloud IAM | 5 | `iam_create_service_account` / `iam_create_key` / `iam_add_iam_policy_binding` |
|
|
143
|
-
| GCP Billing | 4 | `gcp_get_billing_info` / `gcp_list_billing_projects` / `gcp_list_budgets` / `gcp_create_budget` |
|
|
144
148
|
| BigQuery | 5 | `bigquery_run_query` / `bigquery_list_datasets` / `bigquery_get_table_schema` |
|
|
149
|
+
| GCP Billing | 4 | `gcp_get_billing_info` / `gcp_list_billing_projects` / `gcp_list_budgets` / `gcp_create_budget` |
|
|
145
150
|
| Threads | 7 | `threads_post` / `threads_post_video` / `threads_post_carousel` / `threads_refresh_token` |
|
|
146
151
|
| TikTok Business | 7 | `tiktok_business_plan_video_post` / `tiktok_business_publish_video` / `tiktok_business_get_publish_status` |
|
|
147
|
-
| 점검
|
|
152
|
+
| 출시 점검 | 5 | `playstore_check_submission_risks` / `appstore_check_submission_risks` / `android_check_billing_compliance` / `screenshot_validate` / `release_status` |
|
|
148
153
|
| Instagram | 4 | `instagram_post_image` / `instagram_post_carousel` / `instagram_save_config` |
|
|
149
154
|
| Android 서명 | 3 | `android_signing_setup` / `android_generate_keystore` / `jenkins_upload_playstore_sa` |
|
|
150
|
-
| 인증 | 4 | `mimi_seed_status` / `mimi_seed_auth_start` / `mimi_seed_auth_status` / `mimi_seed_remote_sync_credentials` |
|
|
155
|
+
| 인증 / 연결 진단 | 4 | `mimi_seed_status` / `mimi_seed_auth_start` / `mimi_seed_auth_status` / `mimi_seed_remote_sync_credentials` |
|
|
151
156
|
| AI (Claude) | 2 | `generate_release_notes_from_commits` / `generate_review_reply` |
|
|
152
|
-
| 영상
|
|
157
|
+
| 영상 제작 · YouTube 업로드 | 15 | `youtube_upload_video` / `youtube_get_video_status` / `youtube_update_video_privacy` / `video_plan_from_story` / `video_research_youtube` / `video_render` |
|
|
153
158
|
| YouTube | 10 | `youtube_get_channel` / `youtube_list_videos` / `youtube_get_analytics_report` / `youtube_get_content_insights` / `youtube_update_video_metadata` / `youtube_set_thumbnail` / `youtube_schedule_video` / `youtube_list_comments` / `youtube_list_comment_replies` / `youtube_reply_comment` |
|
|
159
|
+
<!-- generated:readme-tools-table:end -->
|
|
154
160
|
|
|
155
161
|
> 인앱 결제(IAP·구독) 도구는 위 Play Store·App Store 카운트에 포함됩니다 — `appstore_create_inapp_purchase` · `appstore_update_product_review_note` · `appstore_upload_product_review_screenshot` 등.
|
|
156
162
|
> 전체 카탈로그(항상 최신): [`docs/domain/tool-catalog.md`](../../docs/domain/tool-catalog.md)
|
|
@@ -245,7 +251,7 @@ git 커밋 내역을 Claude가 사용자 친화적인 릴리즈 노트로 변환
|
|
|
245
251
|
tone 옵션: `friendly`(친근) / `professional`(정중) / `empathetic`(공감) / `brief`(간결)
|
|
246
252
|
|
|
247
253
|
> ⚠ AI 생성 답변은 초안입니다. 게시 전 반드시 검토하세요.
|
|
248
|
-
> 답변 게시는 `playstore_reply_review` 도구를
|
|
254
|
+
> 답변 게시는 `playstore_reply_review` 도구를 사용하세요 — `confirm` 없이 부르면 dry-run preview 만 돌아오고, `confirm: true` 로 다시 불러야 게시됩니다.
|
|
249
255
|
|
|
250
256
|
---
|
|
251
257
|
|
|
@@ -260,7 +266,7 @@ tone 옵션: `friendly`(친근) / `professional`(정중) / `empathetic`(공감)
|
|
|
260
266
|
Claude가 연쇄 호출:
|
|
261
267
|
|
|
262
268
|
- `iam_create_service_account("my-project", "onesub-play-verifier", "onesub Play verifier")`
|
|
263
|
-
- `iam_create_key("onesub-play-verifier@my-project.iam.gserviceaccount.com")` → JSON 반환
|
|
269
|
+
- `iam_create_key("onesub-play-verifier@my-project.iam.gserviceaccount.com")` → 먼저 dry-run preview, 사용자 승인 후 `confirm: true` 로 재호출하면 JSON 반환
|
|
264
270
|
- `playstore_verify_service_account(<json>, "com.yourapp.id")` → 아직 Play Console 권한이 없어서 403 반환 기대
|
|
265
271
|
|
|
266
272
|
그 다음 **Play Console에서 수동으로** (또는 별도 androidpublisher.users API 호출):
|
|
@@ -284,6 +290,7 @@ Claude가 연쇄 호출:
|
|
|
284
290
|
| `PEXELS_API_KEY` | Pexels 스톡 영상 검색 (선택) |
|
|
285
291
|
| `OPENAI_API_KEY` | 장면 이미지 생성 (선택) |
|
|
286
292
|
| `MIMI_SEED_FFMPEG_PATH` / `MIMI_SEED_FFPROBE_PATH` | FFmpeg/ffprobe가 PATH에 없을 때 실행 파일 경로 (선택) |
|
|
293
|
+
| `MIMI_SEED_TOOLSETS` / `MIMI_SEED_TOOLSETS_EXCLUDE` | 노출할 도구 영역만 고른다 — 도메인 키(`playstore,appstore` …) 또는 그룹 `store` · `google` · `social` · `media` · `build` · `all` 쉼표 구분. 미지정 = 전체. `auth`·`checks` 는 항상 켜짐, 모르는 키는 stderr 경고 후 무시 (선택) |
|
|
287
294
|
|
|
288
295
|
---
|
|
289
296
|
|
package/assets/agent-guide.md
CHANGED
|
@@ -42,19 +42,21 @@ tools directly — but the *call order* and *safety rules* below still apply.
|
|
|
42
42
|
|
|
43
43
|
### Ready-made `select:` batches
|
|
44
44
|
|
|
45
|
-
Every registered tool appears in at least one batch below (
|
|
46
|
-
|
|
45
|
+
Every registered tool appears in at least one batch below (the table is generated from the tool manifest, and a
|
|
46
|
+
new tool lands in its domain's batch automatically), so a task you can name is a batch you can paste. Pick the
|
|
47
|
+
row for the job; batching two rows in one `select:` call is fine.
|
|
47
48
|
|
|
49
|
+
<!-- generated:select-batches:start — edit scripts/docs-spec.mjs, then npm run plugin:sync -->
|
|
48
50
|
| Goal | `ToolSearch` query |
|
|
49
51
|
|------|--------------------|
|
|
50
52
|
| First contact / "what's connected?" | `select:mimi_seed_status,mimi_seed_auth_status,mimi_seed_auth_start,mimi_seed_remote_sync_credentials` |
|
|
51
53
|
| Release readiness (either store) | `select:release_status,playstore_check_submission_risks,appstore_check_submission_risks,screenshot_validate` |
|
|
52
54
|
| Android Billing compliance | `select:android_check_billing_compliance,playstore_check_submission_risks` |
|
|
53
|
-
| Play Store release | `select:playstore_get_app,playstore_list_tracks,playstore_update_release_notes,
|
|
55
|
+
| Play Store release | `select:playstore_get_app,playstore_list_tracks,playstore_update_release_notes,playstore_promote_release,playstore_submit_release,playstore_check_submission_risks,playstore_plan_release` |
|
|
54
56
|
| Play Store listing + images | `select:playstore_get_listing,playstore_update_listing,playstore_update_details,playstore_upload_image,playstore_list_images,playstore_replace_images,playstore_delete_all_images` |
|
|
55
57
|
| Play Store reviews + stats | `select:playstore_list_reviews,playstore_reply_review,playstore_get_statistics,generate_review_reply` |
|
|
56
58
|
| Play Store IAP / subscriptions | `select:playstore_list_products,playstore_list_inapp_products,playstore_list_subscriptions,playstore_create_onetime_product,playstore_create_subscription,playstore_update_product,playstore_update_product_listing,playstore_update_subscription_listing,playstore_update_product_state,playstore_delete_product` |
|
|
57
|
-
| App Store / TestFlight | `select:appstore_list_apps,appstore_verify_credentials,appstore_get_app,appstore_list_versions,appstore_create_version,appstore_get_metadata,appstore_update_whats_new,appstore_list_builds,appstore_attach_build,
|
|
59
|
+
| App Store / TestFlight | `select:appstore_list_apps,appstore_verify_credentials,appstore_get_app,appstore_list_versions,appstore_create_version,appstore_get_metadata,appstore_update_whats_new,appstore_list_builds,appstore_attach_build,appstore_list_beta_groups,appstore_submit_for_review,appstore_check_submission_risks,appstore_plan_release` |
|
|
58
60
|
| App Store release control (after approval) | `select:appstore_release_status,appstore_release_version,appstore_update_release_type,appstore_phased_release,appstore_list_versions` |
|
|
59
61
|
| Pre-submission declarations (both stores) | `select:appstore_get_age_rating,appstore_update_age_rating,appstore_declare_encryption,appstore_get_availability,appstore_set_territory_availability,playstore_upload_data_safety` |
|
|
60
62
|
| TestFlight external testing | `select:appstore_beta_status,appstore_update_beta_review_detail,appstore_update_beta_test_info,appstore_update_whats_to_test,appstore_submit_beta_review,appstore_set_beta_group_build,appstore_add_beta_testers,appstore_notify_beta_testers,appstore_list_beta_groups,appstore_list_builds` |
|
|
@@ -81,13 +83,14 @@ you can paste. Pick the row for the job; batching two rows in one `select:` call
|
|
|
81
83
|
| TikTok Business video publish | `select:tiktok_business_auth_status,tiktok_business_get_account,tiktok_business_get_video_settings,tiktok_business_plan_video_post,tiktok_business_publish_video,tiktok_business_get_publish_status,tiktok_business_list_publish_audits` |
|
|
82
84
|
| Jenkins credentials + jobs | `select:jenkins_status,jenkins_save_config,jenkins_list_credentials,jenkins_create_credential,jenkins_delete_credential,jenkins_upload_keystore,jenkins_upload_playstore_sa,jenkins_list_jobs,jenkins_get_job_config,jenkins_create_job,jenkins_update_job` |
|
|
83
85
|
| CI (GitHub/GitLab) | `select:ci_save_config,ci_list_workflows,ci_trigger_build,ci_get_build_status,ci_list_recent_builds,ci_cancel_build` |
|
|
84
|
-
| Android signing / keystore | `select:android_signing_setup,android_generate_keystore,jenkins_upload_keystore,jenkins_upload_playstore_sa` |
|
|
86
|
+
| Android signing / keystore | `select:android_signing_setup,android_generate_keystore,jenkins_upload_keystore,jenkins_create_credential,jenkins_upload_playstore_sa` |
|
|
85
87
|
| Service account end-to-end | `select:iam_list_service_accounts,iam_create_service_account,iam_list_keys,iam_create_key,iam_add_iam_policy_binding,setup_playstore_connection,playstore_register_service_account,playstore_verify_service_account,playstore_list_service_accounts,playstore_delete_service_account` |
|
|
86
88
|
| Story → researched video | `select:video_save_plan,video_plan_from_story,video_research_youtube,video_search_stock_assets,video_synthesize_research,video_download_stock_assets,video_generate_image,video_add_local_asset,video_build_timeline,video_render,video_job_status,video_validate` |
|
|
87
89
|
| YouTube upload / publish | `select:youtube_upload_video,youtube_get_video_status,youtube_update_video_privacy,youtube_update_video_metadata,youtube_set_thumbnail,youtube_schedule_video,mimi_seed_auth_start,mimi_seed_auth_status` |
|
|
88
90
|
| YouTube channel + analytics | `select:youtube_get_channel,youtube_list_videos,youtube_get_analytics_report,mimi_seed_auth_start,mimi_seed_auth_status` |
|
|
89
91
|
| YouTube comments | `select:youtube_list_comments,youtube_list_comment_replies,youtube_reply_comment,mimi_seed_auth_start,mimi_seed_auth_status` |
|
|
90
92
|
| YouTube content insights | `select:youtube_get_content_insights,youtube_get_channel,youtube_list_videos,video_save_plan,mimi_seed_auth_start,mimi_seed_auth_status` |
|
|
93
|
+
<!-- generated:select-batches:end -->
|
|
91
94
|
|
|
92
95
|
---
|
|
93
96
|
|
|
@@ -161,6 +164,8 @@ Credentials live under `~/.mimi-seed/` (legacy `~/.preseed/` is still read):
|
|
|
161
164
|
| `social-profiles/<profile>.json` | Named Facebook/Instagram/Threads tokens selected by the current project's `.mimi-seed.json` |
|
|
162
165
|
| `tiktok-business.json` | TikTok API for Business app credentials + short-term/refresh tokens (0600) |
|
|
163
166
|
| `tiktok-business/plans`, `tiktok-business/audit`, `tiktok-business/locks` | Expiring post plans, deduplication/audit records, and atomic publish reservations; signed URL query strings are not written to audit records |
|
|
167
|
+
| `keys/<sa>-<keyId>.json` | Service-account keys issued by `iam_create_key` (0600). The tool returns only this path |
|
|
168
|
+
| `keystores/<app>-<timestamp>/upload.jks` + `signing.json` | Upload keystore and its alias/passwords from `android_generate_keystore` (0600). The tool returns only these paths |
|
|
164
169
|
|
|
165
170
|
Notes that matter in practice:
|
|
166
171
|
|
|
@@ -174,6 +179,31 @@ Notes that matter in practice:
|
|
|
174
179
|
`YOUTUBE_API_KEY` (reference research), `PEXELS_API_KEY` (licensed stock search), and
|
|
175
180
|
`OPENAI_API_KEY` (generated images). Rendering needs FFmpeg on `PATH` or
|
|
176
181
|
`MIMI_SEED_FFMPEG_PATH`.
|
|
182
|
+
- Google API calls time out after 60 s by default. Media uploads (YouTube video/thumbnail, Play images) get
|
|
183
|
+
**3 hours** per call; set `MIMI_SEED_UPLOAD_TIMEOUT_MS` (milliseconds, positive integer) in the MCP server's
|
|
184
|
+
environment to change it for very large files or slow links.
|
|
185
|
+
|
|
186
|
+
### Server configuration — limiting the tool surface (`MIMI_SEED_TOOLSETS`)
|
|
187
|
+
|
|
188
|
+
Clients without lazy tool loading pay for every tool schema in context, and some cap the number of tools.
|
|
189
|
+
The server can expose only the domains a project needs — set these in the MCP server's `env`:
|
|
190
|
+
|
|
191
|
+
| Variable | Meaning |
|
|
192
|
+
|----------|---------|
|
|
193
|
+
| `MIMI_SEED_TOOLSETS` | Comma-separated domain keys from `mimi-seed://tools/catalog` (`playstore`, `appstore`, `firebase`, …) and/or groups: `store` (playstore · appstore · android · ai), `google` (firebase · admob · iam · billing · bigquery · ga4 · gsc · googleads), `social` (facebook · instagram · threads · tiktok · youtube), `media` (video · youtube), `build` (ci · jenkins · android), `all`. Unset = everything (default). |
|
|
194
|
+
| `MIMI_SEED_TOOLSETS_EXCLUDE` | Same syntax; removed from the include set. |
|
|
195
|
+
|
|
196
|
+
A few tools also belong to a second toolset: `youtube_upload_video` / `youtube_get_video_status` /
|
|
197
|
+
`youtube_update_video_privacy` (`video` domain) are also in `youtube`, and `jenkins_upload_playstore_sa` (`android`)
|
|
198
|
+
is also in `jenkins` — a tool is on when any of its toolsets is included and off when any is excluded.
|
|
199
|
+
`auth` and `checks` are always on. Unknown keys are ignored with a warning on stderr (and if no include key is
|
|
200
|
+
valid, everything stays on). `mimi_seed_status` prints the active toolsets on its second line — if a tool you
|
|
201
|
+
expect is missing from the deferred list, check that line before concluding the tool doesn't exist.
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{ "mcpServers": { "mimi-seed": { "command": "npx", "args": ["-y", "@yoonion/mimi-seed-mcp"],
|
|
205
|
+
"env": { "MIMI_SEED_TOOLSETS": "store,build" } } } }
|
|
206
|
+
```
|
|
177
207
|
|
|
178
208
|
---
|
|
179
209
|
|
|
@@ -184,8 +214,8 @@ per-domain inventory is [`docs/domain/tool-catalog.md`](domain/tool-catalog.md).
|
|
|
184
214
|
|
|
185
215
|
| Domain | Representative tools |
|
|
186
216
|
|--------|----------------------|
|
|
187
|
-
| **Google Play** | `playstore_get_app` · `playstore_get_listing` · `playstore_update_listing` · `playstore_list_tracks` · `
|
|
188
|
-
| **App Store Connect** | `appstore_list_apps` · `appstore_list_versions` · `appstore_create_version` · `appstore_update_whats_new` · `appstore_update_localization` · `appstore_list_builds` · `
|
|
217
|
+
| **Google Play** | `playstore_get_app` · `playstore_get_listing` · `playstore_update_listing` · `playstore_list_tracks` · `playstore_update_release_notes` · `playstore_promote_release` · `playstore_submit_release` · `playstore_upload_image` · `playstore_replace_images` · `playstore_check_submission_risks` · `playstore_get_statistics` · `playstore_reply_review` · `playstore_register_service_account` |
|
|
218
|
+
| **App Store Connect** | `appstore_list_apps` · `appstore_list_versions` · `appstore_create_version` · `appstore_update_whats_new` · `appstore_update_localization` · `appstore_list_builds` · `appstore_attach_build` · `appstore_upload_screenshot` · `appstore_submit_for_review` · `appstore_cancel_review` · `appstore_list_beta_groups` · `appstore_reply_review` · `appstore_get_weekly_insight` |
|
|
189
219
|
| **Firebase** | `firebase_create_project` · `firebase_create_android_app` · `firebase_create_ios_app` · `firebase_get_android_config` · `firebase_enable_service` · `firebase_enable_common_services` · `firebase_get_remote_config_overview` · `firebase_list_*_apps` |
|
|
190
220
|
| **AdMob** | `admob_create_app` · `admob_create_ad_unit` · `admob_list_ad_units` · `admob_get_today_earnings` · `admob_get_report` |
|
|
191
221
|
| **CI/CD** | `ci_trigger_build` · `ci_get_build_status` · `ci_list_workflows` (**GitHub Actions / GitLab only**) |
|
|
@@ -209,14 +239,33 @@ per-domain inventory is [`docs/domain/tool-catalog.md`](domain/tool-catalog.md).
|
|
|
209
239
|
### Play Store release / promote
|
|
210
240
|
1. `playstore_list_tracks` — see current track/version state.
|
|
211
241
|
2. `playstore_check_submission_risks` (or `playstore_plan_release`) — surface blockers.
|
|
212
|
-
3. Write missing listing/notes (`playstore_update_listing`, `
|
|
213
|
-
|
|
242
|
+
3. Write missing listing/notes (`playstore_update_listing`, `playstore_update_release_notes` — omit `versionCode` for the latest
|
|
243
|
+
release; `syncTracks` only works without `versionCode` — `playstore_upload_image`).
|
|
244
|
+
4. `playstore_promote_release` / `playstore_submit_release` **without** `confirm` → show the returned dry-run preview to the user →
|
|
245
|
+
after an explicit go-ahead, call again with the same arguments plus `confirm: true` (see §5). This applies to every status, including `draft`.
|
|
214
246
|
|
|
215
247
|
### App Store TestFlight → review
|
|
216
248
|
1. `appstore_list_apps` → `appstore_list_versions` (or `appstore_create_version`).
|
|
217
|
-
2. `appstore_list_builds` → `
|
|
249
|
+
2. `appstore_list_builds` → `appstore_attach_build` (omit `buildId` to attach the latest `processingState=VALID` build).
|
|
218
250
|
3. `appstore_update_whats_new`, screenshots if needed.
|
|
219
|
-
4. `appstore_check_submission_risks` → `appstore_submit_for_review`
|
|
251
|
+
4. `appstore_check_submission_risks` → `appstore_submit_for_review` without `confirm` (dry-run preview) → user approves →
|
|
252
|
+
same call with `confirm: true`.
|
|
253
|
+
|
|
254
|
+
### Service account key → Play / Jenkins (secrets stay on disk)
|
|
255
|
+
1. `iam_create_service_account` (or reuse one) → `iam_create_key`. The key is written to
|
|
256
|
+
`~/.mimi-seed/keys/…json` and **only its path** comes back — the private key never enters the conversation.
|
|
257
|
+
2. Pass that path, not the JSON: `playstore_verify_service_account(serviceAccountJsonPath=…)` →
|
|
258
|
+
`playstore_register_service_account(packageName, serviceAccountJsonPath=…)`, and/or
|
|
259
|
+
`jenkins_upload_playstore_sa(package_name, service_account_json_path=…)`.
|
|
260
|
+
Path params accept files **inside `~/.mimi-seed/keys/` only**. (`setup_playstore_connection` does
|
|
261
|
+
steps 1–2 in one call without surfacing the key.)
|
|
262
|
+
|
|
263
|
+
### Android upload keystore → Jenkins
|
|
264
|
+
1. `android_generate_keystore` → returns the paths of `upload.jks` and `signing.json` under
|
|
265
|
+
`~/.mimi-seed/keystores/`; passwords are not printed.
|
|
266
|
+
2. `jenkins_upload_keystore(keystore_path=…)` and `jenkins_create_credential(secret_file=<signing.json>,
|
|
267
|
+
secret_field=storePassword|keyAlias|keyPassword)` — path params accept files **inside
|
|
268
|
+
`~/.mimi-seed/keystores/` only**. Never read `signing.json` into the chat to copy a value.
|
|
220
269
|
|
|
221
270
|
### Release notes from git
|
|
222
271
|
`generate_release_notes_from_commits` (pass commit array + locales) → review with user →
|
|
@@ -265,20 +314,54 @@ contact sheet; codec validation alone is not a quality pass.
|
|
|
265
314
|
|
|
266
315
|
## 5. Safety — irreversible actions need explicit confirmation
|
|
267
316
|
|
|
268
|
-
|
|
317
|
+
Every tool marked **D** in the [tool catalog](domain/tool-catalog.md) — submit/promote/release, deletes,
|
|
318
|
+
public posts and review replies, IAM keys and bindings, Jenkins job/credential overwrites, Play service-account
|
|
319
|
+
setup, beta invites — is
|
|
320
|
+
**confirm-gated by the server**, and its MCP annotations say `destructiveHint: true`. Call order:
|
|
321
|
+
|
|
322
|
+
1. Call the tool **without** `confirm`. Nothing changes; it returns a dry-run preview (the generic one starts
|
|
323
|
+
with `🛑 DRY-RUN` and lists the arguments; some tools show the current remote state instead).
|
|
324
|
+
2. Show that preview to the user and get an explicit go-ahead **in the same turn**.
|
|
325
|
+
3. Call again with the **same arguments** plus `confirm: true`.
|
|
326
|
+
|
|
327
|
+
Some **D** tools gate themselves (`ownGate` in the manifest) and ask for confirmation only on the dangerous
|
|
328
|
+
path: `tiktok_business_publish_video` uses `confirmPublish`; `appstore_phased_release` runs
|
|
329
|
+
`pause`/`resume`/`enable`/`status` directly; `jenkins_create_credential` / `jenkins_upload_keystore` /
|
|
330
|
+
`jenkins_upload_playstore_sa` create a **new** id directly but return a dry-run when the id already exists
|
|
331
|
+
(replacing it needs `confirm: true`).
|
|
332
|
+
There is no argument-based exemption otherwise — `playstore_submit_release` / `playstore_promote_release` need
|
|
333
|
+
`confirm: true` even with `status="draft"`. `youtube_upload_video` / `youtube_update_video_privacy` are writes,
|
|
334
|
+
not **D** (private is reversible), but public/unlisted still needs `confirmVisible: true`.
|
|
335
|
+
|
|
336
|
+
Never pass `confirm: true` on the first call, and never retry an uncertain public write automatically.
|
|
269
337
|
|
|
270
338
|
| Action | Why |
|
|
271
339
|
|--------|-----|
|
|
272
340
|
| `playstore_submit_release` / `playstore_promote_release` with `status=completed` | Starts Google review / full rollout. Near-irreversible. |
|
|
273
|
-
| `appstore_submit_for_review` | Submits to Apple review. |
|
|
274
|
-
| `appstore_delete_screenshot_set`, `playstore_delete_all_images` | Deletes assets. |
|
|
341
|
+
| `appstore_submit_for_review`, `appstore_release_version` | Submits to Apple review / publishes immediately. |
|
|
342
|
+
| `appstore_delete_screenshot_set`, `playstore_delete_all_images`, `playstore_replace_images` | Deletes assets. |
|
|
275
343
|
| `playstore_delete_product`, `jenkins_delete_credential`, `firebase_delete_*_app` | Destructive. |
|
|
344
|
+
| `facebook_post_*`, `instagram_post_*`, `threads_post*`, `*_reply_review`, `youtube_reply_comment` | Public, outward-facing. |
|
|
345
|
+
| `iam_create_key`, `iam_add_iam_policy_binding` | Issues a permanent credential / changes project IAM. |
|
|
276
346
|
|
|
277
347
|
General rules:
|
|
278
348
|
- Run `*_check_submission_risks` / `*_plan_release` **before** submitting, and show
|
|
279
349
|
blockers to the user as a checklist first.
|
|
280
350
|
- Pass file paths as **absolute paths**; never load image bytes into the conversation.
|
|
281
351
|
- Prefer `status=draft` while iterating; flip to `completed` only on explicit request.
|
|
352
|
+
- **Never copy a secret through the chat.** Tools that mint secrets (`iam_create_key`,
|
|
353
|
+
`android_generate_keystore`) write them to `~/.mimi-seed/` and return paths; hand those paths to the
|
|
354
|
+
consuming tool's `*_path` / `secret_file` parameter instead of reading the file.
|
|
355
|
+
- `bigquery_run_query` is **read-only**: it dry-runs the SQL first and refuses anything that is not a
|
|
356
|
+
single `SELECT` (DML, DDL, scripts). "Read-only" means no data change, not no side effects: a `SELECT`
|
|
357
|
+
still bills scanned bytes, and one that calls remote functions, `ML.GENERATE_TEXT`-style model calls, or
|
|
358
|
+
`EXTERNAL_QUERY` still makes those external calls and costs. If it returns `jobComplete=false` with a
|
|
359
|
+
`jobId`, the rows are empty because the job did not finish — not because the result is empty.
|
|
360
|
+
- `mimi_seed_remote_sync_credentials` only sends Play service accounts that are **registered**
|
|
361
|
+
(`playstore_list_service_accounts`), or — when the legacy single `play-service-account.json` exists — any
|
|
362
|
+
well-formed package name (it falls back to that SA). Other names are **skipped and listed**; App Store and the
|
|
363
|
+
allowed packages still sync. The preview shows the destination host, and the endpoint must be https (http only
|
|
364
|
+
for localhost).
|
|
282
365
|
|
|
283
366
|
---
|
|
284
367
|
|
|
@@ -302,6 +385,14 @@ General rules:
|
|
|
302
385
|
against editing the same app with both tools at once. Do all listing writes via the
|
|
303
386
|
API, or finish & publish your Console edits first — never interleave them.
|
|
304
387
|
- **`ci_*` is GitHub/GitLab only.** It does not trigger Jenkins builds.
|
|
388
|
+
- **Identifiers are validated at the schema.** `packageName` / `package_name(s)` must look like an
|
|
389
|
+
Android application id (`com.example.app`) and `bundleId` like an iOS bundle id; anything else
|
|
390
|
+
(`../x`, slashes, empty segments) is rejected with `Input validation error` before the tool runs.
|
|
391
|
+
Google resource ids (project, app, service-account email, AdMob/GA4/billing account) must be a single segment
|
|
392
|
+
(`A-Z a-z 0-9 - _ . : @`) — `../` is refused before any request. BigQuery ids follow BigQuery's own naming
|
|
393
|
+
rules (table names may contain Unicode and spaces), and Play ids may not be exactly `.` or `..`.
|
|
394
|
+
- **FFmpeg location is configuration, not a tool argument.** The video/TikTok tools no longer take
|
|
395
|
+
`ffmpegPath`; set `MIMI_SEED_FFMPEG_PATH` / `MIMI_SEED_FFPROBE_PATH` or put FFmpeg on `PATH`.
|
|
305
396
|
- **Reward/cash-out apps** are a sensitive Play category — flag policy implications to
|
|
306
397
|
the user, but it does not block test-track distribution.
|
|
307
398
|
|
|
@@ -317,8 +408,9 @@ Available in any MCP client as native slash commands:
|
|
|
317
408
|
- `/mimi-seed:review-inbox` — fetch unanswered reviews → draft replies
|
|
318
409
|
|
|
319
410
|
MCP resources: `mimi-seed://auth/status` (live token state) · `mimi-seed://agent/guide`
|
|
320
|
-
(this guide, served over MCP) · `mimi-seed://tools/catalog` (the
|
|
321
|
-
domain
|
|
411
|
+
(this guide, served over MCP) · `mimi-seed://tools/catalog` (the tools this server registered,
|
|
412
|
+
by domain — filtered by `MIMI_SEED_TOOLSETS` — with write / destructive markers, deprecated aliases, and the
|
|
413
|
+
credential each domain needs; read it to answer "what can Mimi Seed do?").
|
|
322
414
|
|
|
323
415
|
---
|
|
324
416
|
|
package/dist/admob/tools.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuth2Client } from 'google-auth-library';
|
|
2
|
+
import type { admob_v1, admob_v1beta } from 'googleapis/build/src/apis/admob/index.js';
|
|
2
3
|
/**
|
|
3
4
|
* AdMob API 래퍼
|
|
4
5
|
* v1: 조회 (stable)
|
|
@@ -11,19 +12,19 @@ export declare function listAccounts(auth: OAuth2Client): Promise<{
|
|
|
11
12
|
currencyCode: string | null | undefined;
|
|
12
13
|
}[]>;
|
|
13
14
|
export declare function listApps(auth: OAuth2Client, accountId: string): Promise<{
|
|
14
|
-
name:
|
|
15
|
-
appId:
|
|
16
|
-
platform:
|
|
17
|
-
linkedAppInfo:
|
|
18
|
-
manualAppInfo:
|
|
15
|
+
name: string | null | undefined;
|
|
16
|
+
appId: string | null | undefined;
|
|
17
|
+
platform: string | null | undefined;
|
|
18
|
+
linkedAppInfo: admob_v1.Schema$AppLinkedAppInfo | undefined;
|
|
19
|
+
manualAppInfo: admob_v1.Schema$AppManualAppInfo | undefined;
|
|
19
20
|
}[]>;
|
|
20
21
|
export declare function listAdUnits(auth: OAuth2Client, accountId: string): Promise<{
|
|
21
|
-
name:
|
|
22
|
-
adUnitId:
|
|
23
|
-
adFormat:
|
|
24
|
-
adTypes:
|
|
25
|
-
displayName:
|
|
26
|
-
appId:
|
|
22
|
+
name: string | null | undefined;
|
|
23
|
+
adUnitId: string | null | undefined;
|
|
24
|
+
adFormat: string | null | undefined;
|
|
25
|
+
adTypes: string[] | null | undefined;
|
|
26
|
+
displayName: string | null | undefined;
|
|
27
|
+
appId: string | null | undefined;
|
|
27
28
|
}[]>;
|
|
28
29
|
export declare function getNetworkReport(auth: OAuth2Client, accountId: string, startDate: {
|
|
29
30
|
year: number;
|
|
@@ -33,8 +34,8 @@ export declare function getNetworkReport(auth: OAuth2Client, accountId: string,
|
|
|
33
34
|
year: number;
|
|
34
35
|
month: number;
|
|
35
36
|
day: number;
|
|
36
|
-
}): Promise<
|
|
37
|
-
export declare function getTodayEarnings(auth: OAuth2Client, accountId: string): Promise<
|
|
37
|
+
}): Promise<admob_v1.Schema$GenerateNetworkReportResponse>;
|
|
38
|
+
export declare function getTodayEarnings(auth: OAuth2Client, accountId: string): Promise<admob_v1.Schema$GenerateNetworkReportResponse>;
|
|
38
39
|
export type AdFormat = 'BANNER' | 'INTERSTITIAL' | 'REWARDED' | 'REWARDED_INTERSTITIAL' | 'APP_OPEN' | 'NATIVE';
|
|
39
40
|
/**
|
|
40
41
|
* adFormat → adTypes 파생 (순수 함수 — 테스트 대상).
|
|
@@ -45,5 +46,5 @@ export type AdFormat = 'BANNER' | 'INTERSTITIAL' | 'REWARDED' | 'REWARDED_INTERS
|
|
|
45
46
|
export declare function adTypesForFormat(adFormat: AdFormat): string[];
|
|
46
47
|
export declare function createApp(auth: OAuth2Client, accountId: string, platform: 'ANDROID' | 'IOS', displayName: string,
|
|
47
48
|
/** 스토어에 게시된 앱이면 store ID 로 링크 (Android=패키지명, iOS=App Store 숫자 ID). 없으면 manual(unlinked) 앱. */
|
|
48
|
-
appStoreId?: string): Promise<
|
|
49
|
-
export declare function createAdUnit(auth: OAuth2Client, accountId: string, appId: string, displayName: string, adFormat: AdFormat): Promise<
|
|
49
|
+
appStoreId?: string): Promise<admob_v1beta.Schema$App>;
|
|
50
|
+
export declare function createAdUnit(auth: OAuth2Client, accountId: string, appId: string, displayName: string, adFormat: AdFormat): Promise<admob_v1beta.Schema$AdUnit>;
|
package/dist/admob/tools.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { google } from '../lib/googleapis-lite.js';
|
|
2
|
+
import { resourceName } from '../lib/resource-id.js';
|
|
2
3
|
/**
|
|
3
4
|
* AdMob API 래퍼
|
|
4
5
|
* v1: 조회 (stable)
|
|
@@ -23,7 +24,7 @@ export async function listApps(auth, accountId) {
|
|
|
23
24
|
do {
|
|
24
25
|
const res = await admobApi.accounts.apps.list({
|
|
25
26
|
auth,
|
|
26
|
-
parent: accountId,
|
|
27
|
+
parent: resourceName(accountId, 'accounts', 'AdMob 계정 ID'),
|
|
27
28
|
pageSize: 100,
|
|
28
29
|
pageToken,
|
|
29
30
|
});
|
|
@@ -46,7 +47,7 @@ export async function listAdUnits(auth, accountId) {
|
|
|
46
47
|
do {
|
|
47
48
|
const res = await admobApi.accounts.adUnits.list({
|
|
48
49
|
auth,
|
|
49
|
-
parent: accountId,
|
|
50
|
+
parent: resourceName(accountId, 'accounts', 'AdMob 계정 ID'),
|
|
50
51
|
pageSize: 100,
|
|
51
52
|
pageToken,
|
|
52
53
|
});
|
|
@@ -67,7 +68,7 @@ export async function getNetworkReport(auth, accountId, startDate, endDate) {
|
|
|
67
68
|
const admob = google.admob('v1');
|
|
68
69
|
const res = await admob.accounts.networkReport.generate({
|
|
69
70
|
auth,
|
|
70
|
-
parent: accountId,
|
|
71
|
+
parent: resourceName(accountId, 'accounts', 'AdMob 계정 ID'),
|
|
71
72
|
requestBody: {
|
|
72
73
|
reportSpec: {
|
|
73
74
|
dateRange: { startDate, endDate },
|
|
@@ -125,7 +126,7 @@ appStoreId) {
|
|
|
125
126
|
}
|
|
126
127
|
const res = await admobBeta.accounts.apps.create({
|
|
127
128
|
auth,
|
|
128
|
-
parent: accountId,
|
|
129
|
+
parent: resourceName(accountId, 'accounts', 'AdMob 계정 ID'),
|
|
129
130
|
requestBody,
|
|
130
131
|
});
|
|
131
132
|
return res.data;
|
|
@@ -135,7 +136,7 @@ export async function createAdUnit(auth, accountId, appId, displayName, adFormat
|
|
|
135
136
|
const admobBeta = google.admob('v1beta');
|
|
136
137
|
const res = await admobBeta.accounts.adUnits.create({
|
|
137
138
|
auth,
|
|
138
|
-
parent: accountId,
|
|
139
|
+
parent: resourceName(accountId, 'accounts', 'AdMob 계정 ID'),
|
|
139
140
|
requestBody: {
|
|
140
141
|
appId,
|
|
141
142
|
displayName,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { GeneratedKeystore } from './keystore.js';
|
|
2
|
+
export declare const KEYSTORE_FILE = "upload.jks";
|
|
3
|
+
export declare const SIGNING_SECRETS_FILE = "signing.json";
|
|
4
|
+
export declare const SIGNING_SECRET_FIELDS: readonly ["storePassword", "keyPassword", "keyAlias"];
|
|
5
|
+
export type SigningSecretField = (typeof SIGNING_SECRET_FIELDS)[number];
|
|
6
|
+
/** 호출 시점에 계산한다 — 테스트가 홈 디렉터리를 바꿀 수 있어야 한다. */
|
|
7
|
+
export declare function keystoresDir(): string;
|
|
8
|
+
export interface PersistedKeystore {
|
|
9
|
+
dir: string;
|
|
10
|
+
keystorePath: string;
|
|
11
|
+
secretsPath: string;
|
|
12
|
+
keyAlias: string;
|
|
13
|
+
}
|
|
14
|
+
/** keystore 바이너리와 비밀번호 파일을 0600 으로 저장한다. label 은 영숫자·하이픈만 남긴다. */
|
|
15
|
+
export declare function persistGeneratedKeystore(ks: GeneratedKeystore, label: string): PersistedKeystore;
|
|
16
|
+
/** ~/.mimi-seed/keystores/ 안의 keystore 파일만 base64 로 읽는다. */
|
|
17
|
+
export declare function readKeystoreBase64(filePath: string): string;
|
|
18
|
+
/** jenkins_upload_keystore 입력 → base64. 직접 값(기존 호환)과 경로 중 정확히 하나. */
|
|
19
|
+
export declare function resolveKeystoreInput(input: {
|
|
20
|
+
base64?: string;
|
|
21
|
+
path?: string;
|
|
22
|
+
}): string;
|
|
23
|
+
/** jenkins_create_credential 입력 → 비밀값. 직접 값(기존 호환)과 파일+필드 중 정확히 하나. */
|
|
24
|
+
export declare function resolveSecretInput(input: {
|
|
25
|
+
secret?: string;
|
|
26
|
+
secretFile?: string;
|
|
27
|
+
secretField?: SigningSecretField;
|
|
28
|
+
}): string;
|
|
29
|
+
/** ~/.mimi-seed/keystores/ 안의 signing.json 에서 한 필드만 꺼낸다. */
|
|
30
|
+
export declare function readSigningSecret(filePath: string, field: SigningSecretField): string;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// 생성한 upload keystore 보관소 — ~/.mimi-seed/keystores/<앱>-<시각>/.
|
|
2
|
+
//
|
|
3
|
+
// android_generate_keystore 는 예전에 store/key 비밀번호와 keystore base64 전체를 도구 응답에
|
|
4
|
+
// 실었다. 그 값들은 대화 기록·클라이언트 로그에 평문으로 남고, 잃어버리면 앱 서명을 영구히
|
|
5
|
+
// 잃는 비밀이다. 이제 keystore 와 비밀번호는 0600 파일로만 존재하고, Jenkins 등록 도구는
|
|
6
|
+
// 이 디렉터리 안의 **경로**를 받는다 (jenkins_upload_keystore.keystore_path,
|
|
7
|
+
// jenkins_create_credential.secret_file).
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { writeCredentialFile, writeCredentialJson } from '../lib/atomic-write.js';
|
|
12
|
+
import { resolveInsideDir } from '../lib/path-containment.js';
|
|
13
|
+
export const KEYSTORE_FILE = 'upload.jks';
|
|
14
|
+
export const SIGNING_SECRETS_FILE = 'signing.json';
|
|
15
|
+
export const SIGNING_SECRET_FIELDS = ['storePassword', 'keyPassword', 'keyAlias'];
|
|
16
|
+
/** 호출 시점에 계산한다 — 테스트가 홈 디렉터리를 바꿀 수 있어야 한다. */
|
|
17
|
+
export function keystoresDir() {
|
|
18
|
+
return path.join(os.homedir(), '.mimi-seed', 'keystores');
|
|
19
|
+
}
|
|
20
|
+
/** keystore 바이너리와 비밀번호 파일을 0600 으로 저장한다. label 은 영숫자·하이픈만 남긴다. */
|
|
21
|
+
export function persistGeneratedKeystore(ks, label) {
|
|
22
|
+
const safeLabel = label.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '') || 'app';
|
|
23
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
24
|
+
const dir = path.join(keystoresDir(), `${safeLabel}-${stamp}`);
|
|
25
|
+
const keystorePath = path.join(dir, KEYSTORE_FILE);
|
|
26
|
+
const secretsPath = path.join(dir, SIGNING_SECRETS_FILE);
|
|
27
|
+
writeCredentialFile(keystorePath, Buffer.from(ks.keystoreBase64, 'base64'));
|
|
28
|
+
writeCredentialJson(secretsPath, {
|
|
29
|
+
keystore: KEYSTORE_FILE,
|
|
30
|
+
keyAlias: ks.keyAlias,
|
|
31
|
+
storePassword: ks.storePassword,
|
|
32
|
+
keyPassword: ks.keyPassword,
|
|
33
|
+
createdAt: new Date().toISOString(),
|
|
34
|
+
});
|
|
35
|
+
return { dir, keystorePath, secretsPath, keyAlias: ks.keyAlias };
|
|
36
|
+
}
|
|
37
|
+
/** ~/.mimi-seed/keystores/ 안의 keystore 파일만 base64 로 읽는다. */
|
|
38
|
+
export function readKeystoreBase64(filePath) {
|
|
39
|
+
const real = resolveInsideDir(keystoresDir(), filePath, {
|
|
40
|
+
label: 'keystore',
|
|
41
|
+
extensions: ['.jks', '.keystore', '.p12'],
|
|
42
|
+
});
|
|
43
|
+
return fs.readFileSync(real).toString('base64');
|
|
44
|
+
}
|
|
45
|
+
/** jenkins_upload_keystore 입력 → base64. 직접 값(기존 호환)과 경로 중 정확히 하나. */
|
|
46
|
+
export function resolveKeystoreInput(input) {
|
|
47
|
+
if (input.base64 && input.path)
|
|
48
|
+
throw new Error('keystore_base64 와 keystore_path 중 하나만 주세요.');
|
|
49
|
+
if (input.path)
|
|
50
|
+
return readKeystoreBase64(input.path);
|
|
51
|
+
if (input.base64)
|
|
52
|
+
return input.base64;
|
|
53
|
+
throw new Error('keystore_path(권장) 또는 keystore_base64 가 필요합니다.');
|
|
54
|
+
}
|
|
55
|
+
/** jenkins_create_credential 입력 → 비밀값. 직접 값(기존 호환)과 파일+필드 중 정확히 하나. */
|
|
56
|
+
export function resolveSecretInput(input) {
|
|
57
|
+
if (input.secret !== undefined && input.secretFile)
|
|
58
|
+
throw new Error('secret 과 secret_file 중 하나만 주세요.');
|
|
59
|
+
if (input.secretFile) {
|
|
60
|
+
if (!input.secretField)
|
|
61
|
+
throw new Error('secret_file 을 쓰면 secret_field 도 필요합니다.');
|
|
62
|
+
return readSigningSecret(input.secretFile, input.secretField);
|
|
63
|
+
}
|
|
64
|
+
// 빈 문자열도 값이다 — main 의 `secret: z.string()` 은 ""(예: 비워 둔 플레이스홀더)를 그대로
|
|
65
|
+
// 저장했다. 없음(undefined)과 빈 값을 구분한다.
|
|
66
|
+
if (input.secret !== undefined)
|
|
67
|
+
return input.secret;
|
|
68
|
+
throw new Error('secret_file + secret_field(권장) 또는 secret 이 필요합니다.');
|
|
69
|
+
}
|
|
70
|
+
/** ~/.mimi-seed/keystores/ 안의 signing.json 에서 한 필드만 꺼낸다. */
|
|
71
|
+
export function readSigningSecret(filePath, field) {
|
|
72
|
+
const real = resolveInsideDir(keystoresDir(), filePath, { label: '서명 비밀 파일', extensions: ['.json'] });
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(fs.readFileSync(real, 'utf8'));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
throw new Error(`서명 비밀 파일을 읽지 못했습니다: ${filePath}`);
|
|
79
|
+
}
|
|
80
|
+
const value = parsed[field];
|
|
81
|
+
if (typeof value !== 'string' || !value) {
|
|
82
|
+
throw new Error(`서명 비밀 파일에 ${field} 값이 없습니다: ${filePath}`);
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
@@ -4,6 +4,9 @@ export interface GeneratedKeystore {
|
|
|
4
4
|
keyAlias: string;
|
|
5
5
|
keyPassword: string;
|
|
6
6
|
}
|
|
7
|
+
/** keytool `-storepass:env` / `-keypass:env` 로 넘기는 환경변수 이름. */
|
|
8
|
+
export declare const STOREPASS_ENV = "MIMI_SEED_KEYTOOL_STOREPASS";
|
|
9
|
+
export declare const KEYPASS_ENV = "MIMI_SEED_KEYTOOL_KEYPASS";
|
|
7
10
|
export declare function isKeytoolAvailable(): boolean;
|
|
8
11
|
export declare function generateKeystore(opts: {
|
|
9
12
|
appName: string;
|
package/dist/android/keystore.js
CHANGED
|
@@ -3,6 +3,9 @@ import { randomBytes } from 'node:crypto';
|
|
|
3
3
|
import { readFileSync, unlinkSync, existsSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
+
/** keytool `-storepass:env` / `-keypass:env` 로 넘기는 환경변수 이름. */
|
|
7
|
+
export const STOREPASS_ENV = 'MIMI_SEED_KEYTOOL_STOREPASS';
|
|
8
|
+
export const KEYPASS_ENV = 'MIMI_SEED_KEYTOOL_KEYPASS';
|
|
6
9
|
function randomPassword(len = 20) {
|
|
7
10
|
// alphanumeric — avoid quoting/escape issues in shell / Gradle properties
|
|
8
11
|
return randomBytes(32).toString('base64url').replace(/[^a-zA-Z0-9]/g, '').slice(0, len);
|
|
@@ -31,11 +34,17 @@ export function generateKeystore(opts) {
|
|
|
31
34
|
'-keysize', '2048',
|
|
32
35
|
'-validity', '10000',
|
|
33
36
|
'-alias', keyAlias,
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
// 비밀번호를 argv 에 실으면 같은 머신의 다른 사용자가 `ps` 로 볼 수 있다.
|
|
38
|
+
// keytool 의 `:env` 형식은 환경변수 이름만 인자로 받는다.
|
|
39
|
+
'-storepass:env', STOREPASS_ENV,
|
|
40
|
+
'-keypass:env', KEYPASS_ENV,
|
|
36
41
|
'-dname', dname,
|
|
37
42
|
'-storetype', 'JKS',
|
|
38
|
-
], {
|
|
43
|
+
], {
|
|
44
|
+
stdio: 'pipe',
|
|
45
|
+
encoding: 'buffer',
|
|
46
|
+
env: { ...process.env, [STOREPASS_ENV]: storePassword, [KEYPASS_ENV]: keyPassword },
|
|
47
|
+
});
|
|
39
48
|
if (result.status !== 0) {
|
|
40
49
|
const stderr = result.stderr?.toString('utf-8') ?? '';
|
|
41
50
|
throw new Error(`keytool 실패:\n${stderr}`);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type PersistedKeystore } from './keystore-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* android_signing_setup — Play Console 에 이미 있는 앱. 기존 keystore 를 받아 등록하는 순서.
|
|
4
|
+
*
|
|
5
|
+
* 비밀값(keystore 바이트·비밀번호)을 대화에 싣지 않는다 — 사용자가 keystore 를 `keystoreDir` 로 복사하고
|
|
6
|
+
* 비밀번호를 그 폴더의 signing.json 에 적게 한 뒤, Jenkins 도구에는 경로만 넘긴다
|
|
7
|
+
* (jenkins_upload_keystore.keystore_path, jenkins_create_credential.secret_file + secret_field).
|
|
8
|
+
*/
|
|
9
|
+
export declare function existingAppSigningPlanLines(packageName: string, prefix: string, jenkinsStatus: string, keystoreDir: string): string[];
|
|
10
|
+
/** android_signing_setup — 신규 앱(또는 Play Console 확인 불가). keystore 생성부터의 순서. */
|
|
11
|
+
export declare function newAppSigningPlanLines(opts: {
|
|
12
|
+
packageName: string;
|
|
13
|
+
prefix: string;
|
|
14
|
+
appStatus: 'new' | 'unknown';
|
|
15
|
+
playNote: string;
|
|
16
|
+
jenkinsStatus: string;
|
|
17
|
+
jenkinsConfigured: boolean;
|
|
18
|
+
keytoolOk: boolean;
|
|
19
|
+
projectId?: string;
|
|
20
|
+
/** keytool 이 없어 사용자가 직접 만든 keystore·비밀번호 파일을 둘 폴더 (~/.mimi-seed/keystores/ 안). */
|
|
21
|
+
keystoreDir: string;
|
|
22
|
+
}): string[];
|
|
23
|
+
/** android_generate_keystore — keytool 이 없을 때 설치 안내. */
|
|
24
|
+
export declare function keytoolMissingLines(): string[];
|
|
25
|
+
/** android_generate_keystore — 생성 완료. 비밀값은 싣지 않고 경로만 넘긴다. */
|
|
26
|
+
export declare function keystoreGeneratedLines(prefix: string, saved: PersistedKeystore): string[];
|
|
27
|
+
/** jenkins_upload_playstore_sa — 패키지별 SA 파일이 아직 없을 때. */
|
|
28
|
+
export declare function playServiceAccountMissingLines(packageName: string, saPath: string): string[];
|
|
29
|
+
/** jenkins_upload_playstore_sa — 업로드 완료 + Play Console 권한 부여(수동 1회) 안내. */
|
|
30
|
+
export declare function playServiceAccountUploadedLines(opts: {
|
|
31
|
+
result: string;
|
|
32
|
+
credentialId: string;
|
|
33
|
+
clientEmail: string;
|
|
34
|
+
rawLength: number;
|
|
35
|
+
}): string[];
|