pabal-resource-mcp 1.5.9 → 1.6.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.
@@ -11,7 +11,7 @@ import {
11
11
  getPushDataDir,
12
12
  loadAsoFromConfig,
13
13
  saveAsoToAsoDir
14
- } from "../chunk-YKUBCCJA.js";
14
+ } from "../chunk-7BCPWAMQ.js";
15
15
  import {
16
16
  DEFAULT_LOCALE,
17
17
  appStoreToUnified,
@@ -32,7 +32,7 @@ import {
32
32
  ListToolsRequestSchema
33
33
  } from "@modelcontextprotocol/sdk/types.js";
34
34
 
35
- // src/tools/aso/pull.ts
35
+ // src/tools/aso/aso-to-public.ts
36
36
  import { z } from "zod";
37
37
  import { zodToJsonSchema } from "zod-to-json-schema";
38
38
 
@@ -199,7 +199,7 @@ Copy screenshots from these directories to public/products/[slug]/screenshots/ a
199
199
  **Important:** Return only valid JSON, without any additional explanation.`;
200
200
  }
201
201
 
202
- // src/tools/aso/pull.ts
202
+ // src/tools/aso/aso-to-public.ts
203
203
  var toJsonSchema = zodToJsonSchema;
204
204
  var asoToPublicInputSchema = z.object({
205
205
  slug: z.string().describe("Product slug")
@@ -348,7 +348,7 @@ Next steps (manual):
348
348
  };
349
349
  }
350
350
 
351
- // src/tools/aso/push.ts
351
+ // src/tools/aso/public-to-aso.ts
352
352
  import { z as z2 } from "zod";
353
353
  import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
354
354
  import path4 from "path";
@@ -571,10 +571,8 @@ function prepareAsoDataForPush(slug, configData) {
571
571
  const { screenshots, ...rest } = localeData;
572
572
  cleanedLocales[locale] = {
573
573
  ...rest,
574
- featureGraphic: void 0,
574
+ featureGraphic: void 0
575
575
  // Images excluded
576
- contactWebsite: detailPageUrl
577
- // Set detail page URL
578
576
  };
579
577
  }
580
578
  const convertedLocales = {};
@@ -593,7 +591,11 @@ function prepareAsoDataForPush(slug, configData) {
593
591
  const convertedDefaultLocale = unifiedToGooglePlay(googleDefaultLocale) || googleDefaultLocale;
594
592
  storeData.googlePlay = {
595
593
  locales: convertedLocales,
596
- defaultLocale: convertedDefaultLocale
594
+ defaultLocale: convertedDefaultLocale,
595
+ // App-level contact information (from multilingual data)
596
+ contactEmail: isGooglePlayMultilingual(googlePlayData) ? googlePlayData.contactEmail : void 0,
597
+ contactWebsite: detailPageUrl
598
+ // Set to detail page URL
597
599
  };
598
600
  }
599
601
  if (configData.appStore) {
@@ -603,9 +605,7 @@ function prepareAsoDataForPush(slug, configData) {
603
605
  for (const [locale, localeData] of Object.entries(locales)) {
604
606
  const { screenshots, ...rest } = localeData;
605
607
  cleanedLocales[locale] = {
606
- ...rest,
607
- marketingUrl: detailPageUrl
608
- // Set detail page URL
608
+ ...rest
609
609
  };
610
610
  }
611
611
  const convertedLocales = {};
@@ -622,7 +622,14 @@ function prepareAsoDataForPush(slug, configData) {
622
622
  const convertedDefaultLocale = unifiedToAppStore(appStoreDefaultLocale) || appStoreDefaultLocale;
623
623
  storeData.appStore = {
624
624
  locales: convertedLocales,
625
- defaultLocale: convertedDefaultLocale
625
+ defaultLocale: convertedDefaultLocale,
626
+ // App-level contact information (from multilingual data)
627
+ contactEmail: isAppStoreMultilingual(appStoreData) ? appStoreData.contactEmail : void 0,
628
+ supportUrl: isAppStoreMultilingual(appStoreData) ? appStoreData.supportUrl : void 0,
629
+ marketingUrl: detailPageUrl,
630
+ // Set to detail page URL
631
+ privacyPolicyUrl: isAppStoreMultilingual(appStoreData) ? appStoreData.privacyPolicyUrl : void 0,
632
+ termsUrl: isAppStoreMultilingual(appStoreData) ? appStoreData.termsUrl : void 0
626
633
  };
627
634
  }
628
635
  return storeData;
@@ -718,7 +725,7 @@ function convertToMultilingual(data, locale) {
718
725
  };
719
726
  }
720
727
 
721
- // src/tools/aso/push.ts
728
+ // src/tools/aso/public-to-aso.ts
722
729
  import fs4 from "fs";
723
730
  var toJsonSchema2 = zodToJsonSchema2;
724
731
  var publicToAsoInputSchema = z2.object({
@@ -749,6 +756,10 @@ async function downloadScreenshotsToAsoDir(slug, asoData) {
749
756
  continue;
750
757
  }
751
758
  const localeData = googlePlayData.locales[unifiedLocale];
759
+ const hasAnyScreenshots = localeData.screenshots?.phone && localeData.screenshots.phone.length > 0 || localeData.screenshots?.tablet7 && localeData.screenshots.tablet7.length > 0 || localeData.screenshots?.tablet10 && localeData.screenshots.tablet10.length > 0 || localeData.screenshots?.tablet && localeData.screenshots.tablet.length > 0 || localeData.featureGraphic;
760
+ if (!hasAnyScreenshots) {
761
+ continue;
762
+ }
752
763
  const asoDir = path4.join(
753
764
  productStoreRoot,
754
765
  "google-play",
@@ -829,6 +840,10 @@ async function downloadScreenshotsToAsoDir(slug, asoData) {
829
840
  continue;
830
841
  }
831
842
  const localeData = appStoreData.locales[unifiedLocale];
843
+ const hasAnyScreenshots = localeData.screenshots?.iphone65 && localeData.screenshots.iphone65.length > 0 || localeData.screenshots?.ipadPro129 && localeData.screenshots.ipadPro129.length > 0;
844
+ if (!hasAnyScreenshots) {
845
+ continue;
846
+ }
832
847
  const asoDir = path4.join(
833
848
  productStoreRoot,
834
849
  "app-store",
@@ -1044,7 +1059,7 @@ Sanitized invalid characters:
1044
1059
  };
1045
1060
  }
1046
1061
 
1047
- // src/tools/aso/improve.ts
1062
+ // src/tools/aso/improve-public.ts
1048
1063
  import { z as z3 } from "zod";
1049
1064
  import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
1050
1065
 
@@ -2331,7 +2346,7 @@ function loadKeywordResearchForLocale(slug, locale) {
2331
2346
  return { entries: [], sections: [], researchDir, isFallback: false };
2332
2347
  }
2333
2348
 
2334
- // src/tools/aso/improve.ts
2349
+ // src/tools/aso/improve-public.ts
2335
2350
  var toJsonSchema3 = zodToJsonSchema3;
2336
2351
  var improvePublicInputSchema = z3.object({
2337
2352
  slug: z3.string().describe("Product slug"),
@@ -2527,7 +2542,7 @@ async function handleImprovePublic(input) {
2527
2542
  throw new Error(`Invalid stage: ${stage}. Must be "1", "2", or "both".`);
2528
2543
  }
2529
2544
 
2530
- // src/tools/aso/validate.ts
2545
+ // src/tools/aso/validate-aso.ts
2531
2546
  import { z as z4 } from "zod";
2532
2547
  import { zodToJsonSchema as zodToJsonSchema4 } from "zod-to-json-schema";
2533
2548
  var toJsonSchema4 = zodToJsonSchema4;
@@ -3675,8 +3690,9 @@ function prepareLocalesForTranslation(locales, primaryLocale) {
3675
3690
  }
3676
3691
  geminiLocalesNeeded.add(geminiLocale);
3677
3692
  const existing = localeMapping.get(geminiLocale) || [];
3678
- if (!existing.includes(locale)) {
3679
- existing.push(locale);
3693
+ const unifiedLocale = locale;
3694
+ if (!existing.includes(unifiedLocale)) {
3695
+ existing.push(unifiedLocale);
3680
3696
  }
3681
3697
  localeMapping.set(geminiLocale, existing);
3682
3698
  }
package/dist/browser.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as APP_STORE_TO_UNIFIED, F as AppMetaLinks, H as AppPageData, t as AppStoreAsoData, p as AppStoreInfoLocalization, d as AppStoreLocale, v as AppStoreMultilingualAsoData, o as AppStoreReleaseNote, r as AppStoreScreenshotDisplayType, s as AppStoreScreenshots, q as AppStoreVersionLocalization, w as AsoData, $ as AsoLocaleContent, _ as AsoTemplate, C as BlogArticle, z as BlogMeta, B as BlogMetaBlock, a4 as BlogMetaOutput, E as BlogSummary, a5 as CreateBlogHtmlInput, a7 as CreateBlogHtmlResult, D as DEFAULT_LOCALE, R as DeepPartial, M as FeatureItem, G as GOOGLE_PLAY_TO_UNIFIED, a6 as GeneratedBlogFile, m as GooglePlayAsoData, j as GooglePlayImageType, h as GooglePlayListing, e as GooglePlayLocale, u as GooglePlayMultilingualAsoData, n as GooglePlayReleaseNote, k as GooglePlayScreenshotType, l as GooglePlayScreenshots, I as ImageAsset, P as LandingCta, N as LandingFeatures, J as LandingHero, Q as LandingPage, V as LandingPageLocale, O as LandingReviews, K as LandingScreenshots, L as LayoutColors, a2 as LocaleDisplayInfo, Z as ProductConfig, Y as ProductContent, a0 as ProductLocale, X as ProductMetadata, W as ProductScreenshots, a1 as SiteConfig, a3 as SiteData, S as SupportedLocale, T as Testimonial, U as UNIFIED_LOCALES, a as UNIFIED_TO_APP_STORE, b as UNIFIED_TO_GOOGLE_PLAY, c as UnifiedLocale, ah as appStoreToGooglePlay, ab as appStoreToUnified, af as appStoreToUnifiedBatch, al as convertObjectFromAppStore, am as convertObjectFromGooglePlay, aj as convertObjectToAppStore, ak as convertObjectToGooglePlay, ai as googlePlayToAppStore, ac as googlePlayToUnified, ag as googlePlayToUnifiedBatch, f as isAppStoreLocale, y as isAppStoreMultilingual, g as isGooglePlayLocale, x as isGooglePlayMultilingual, i as isSupportedLocale, a8 as unifiedToAppStore, ad as unifiedToAppStoreBatch, aa as unifiedToBothPlatforms, a9 as unifiedToGooglePlay, ae as unifiedToGooglePlayBatch } from './locale-converter-CcA-f2gI.js';
1
+ export { A as APP_STORE_TO_UNIFIED, F as AppMetaLinks, H as AppPageData, t as AppStoreAsoData, p as AppStoreInfoLocalization, d as AppStoreLocale, v as AppStoreMultilingualAsoData, o as AppStoreReleaseNote, r as AppStoreScreenshotDisplayType, s as AppStoreScreenshots, q as AppStoreVersionLocalization, w as AsoData, $ as AsoLocaleContent, _ as AsoTemplate, C as BlogArticle, z as BlogMeta, B as BlogMetaBlock, a4 as BlogMetaOutput, E as BlogSummary, a5 as CreateBlogHtmlInput, a7 as CreateBlogHtmlResult, D as DEFAULT_LOCALE, R as DeepPartial, M as FeatureItem, G as GOOGLE_PLAY_TO_UNIFIED, a6 as GeneratedBlogFile, m as GooglePlayAsoData, j as GooglePlayImageType, h as GooglePlayListing, e as GooglePlayLocale, u as GooglePlayMultilingualAsoData, n as GooglePlayReleaseNote, k as GooglePlayScreenshotType, l as GooglePlayScreenshots, I as ImageAsset, P as LandingCta, N as LandingFeatures, J as LandingHero, Q as LandingPage, V as LandingPageLocale, O as LandingReviews, K as LandingScreenshots, L as LayoutColors, a2 as LocaleDisplayInfo, Z as ProductConfig, Y as ProductContent, a0 as ProductLocale, X as ProductMetadata, W as ProductScreenshots, a1 as SiteConfig, a3 as SiteData, S as SupportedLocale, T as Testimonial, U as UNIFIED_LOCALES, a as UNIFIED_TO_APP_STORE, b as UNIFIED_TO_GOOGLE_PLAY, c as UnifiedLocale, ah as appStoreToGooglePlay, ab as appStoreToUnified, af as appStoreToUnifiedBatch, al as convertObjectFromAppStore, am as convertObjectFromGooglePlay, aj as convertObjectToAppStore, ak as convertObjectToGooglePlay, ai as googlePlayToAppStore, ac as googlePlayToUnified, ag as googlePlayToUnifiedBatch, f as isAppStoreLocale, y as isAppStoreMultilingual, g as isGooglePlayLocale, x as isGooglePlayMultilingual, i as isSupportedLocale, a8 as unifiedToAppStore, ad as unifiedToAppStoreBatch, aa as unifiedToBothPlatforms, a9 as unifiedToGooglePlay, ae as unifiedToGooglePlayBatch } from './locale-converter-CHX8t4HG.js';
2
2
  import '@googleapis/androidpublisher';
3
3
  import 'appstore-connect-sdk/openapi';
4
4
 
@@ -0,0 +1,401 @@
1
+ import {
2
+ DEFAULT_LOCALE,
3
+ isAppStoreLocale,
4
+ isGooglePlayLocale,
5
+ isSupportedLocale
6
+ } from "./chunk-BOWRBVVV.js";
7
+
8
+ // src/utils/config.util.ts
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import os from "os";
12
+ function getAsoDataDir() {
13
+ const configPath = path.join(
14
+ os.homedir(),
15
+ ".config",
16
+ "pabal-mcp",
17
+ "config.json"
18
+ );
19
+ if (!fs.existsSync(configPath)) {
20
+ throw new Error(
21
+ `Config file not found at ${configPath}. Please create the config file and set the 'dataDir' property to specify the ASO data directory.`
22
+ );
23
+ }
24
+ try {
25
+ const configContent = fs.readFileSync(configPath, "utf-8");
26
+ const config = JSON.parse(configContent);
27
+ if (!config.dataDir) {
28
+ throw new Error(
29
+ `'dataDir' property is not set in ${configPath}. Please set 'dataDir' to specify the ASO data directory.`
30
+ );
31
+ }
32
+ if (path.isAbsolute(config.dataDir)) {
33
+ return config.dataDir;
34
+ }
35
+ return path.resolve(os.homedir(), config.dataDir);
36
+ } catch (error) {
37
+ if (error instanceof Error && error.message.includes("dataDir")) {
38
+ throw error;
39
+ }
40
+ throw new Error(
41
+ `Failed to read config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`
42
+ );
43
+ }
44
+ }
45
+ function getPullDataDir() {
46
+ return path.join(getAsoDataDir(), ".aso", "pullData");
47
+ }
48
+ function getPushDataDir() {
49
+ return path.join(getAsoDataDir(), ".aso", "pushData");
50
+ }
51
+ function getPublicDir() {
52
+ return path.join(getAsoDataDir(), "public");
53
+ }
54
+ function getKeywordResearchDir() {
55
+ return path.join(getAsoDataDir(), ".aso", "keywordResearch");
56
+ }
57
+ function getProductsDir() {
58
+ return path.join(getPublicDir(), "products");
59
+ }
60
+ function loadConfig() {
61
+ const configPath = path.join(
62
+ os.homedir(),
63
+ ".config",
64
+ "pabal-mcp",
65
+ "config.json"
66
+ );
67
+ if (!fs.existsSync(configPath)) {
68
+ return {};
69
+ }
70
+ try {
71
+ const configContent = fs.readFileSync(configPath, "utf-8");
72
+ return JSON.parse(configContent);
73
+ } catch {
74
+ return {};
75
+ }
76
+ }
77
+ function getGeminiApiKey() {
78
+ const config = loadConfig();
79
+ if (config.gemini?.apiKey) {
80
+ return config.gemini.apiKey;
81
+ }
82
+ const envKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
83
+ if (envKey) {
84
+ return envKey;
85
+ }
86
+ throw new Error(
87
+ `Gemini API key not found. Set it in ~/.config/pabal-mcp/config.json under "gemini.apiKey" or use GEMINI_API_KEY environment variable.`
88
+ );
89
+ }
90
+
91
+ // src/utils/aso-converter.ts
92
+ import fs2 from "fs";
93
+ import path2 from "path";
94
+ function generateFullDescription(localeData, metadata = {}) {
95
+ const { aso, landing } = localeData;
96
+ const template = aso?.template;
97
+ if (!template) {
98
+ return "";
99
+ }
100
+ const landingFeatures = landing?.features?.items || [];
101
+ const landingScreenshots = landing?.screenshots?.images || [];
102
+ const keyHeading = template.keyFeaturesHeading || "Key Features";
103
+ const featuresHeading = template.featuresHeading || "Additional Features";
104
+ const parts = [template.intro];
105
+ if (landingFeatures.length > 0) {
106
+ parts.push(
107
+ "",
108
+ keyHeading,
109
+ "",
110
+ ...landingFeatures.map(
111
+ (feature) => [`\u25B6\uFE0E ${feature.title}`, feature.body || ""].filter(Boolean).join("\n")
112
+ )
113
+ );
114
+ }
115
+ if (landingScreenshots.length > 0) {
116
+ parts.push("", featuresHeading, "");
117
+ parts.push(
118
+ ...landingScreenshots.map(
119
+ (screenshot) => [`\u25B6\uFE0E ${screenshot.title}`, screenshot.description || ""].filter(Boolean).join("\n")
120
+ )
121
+ );
122
+ }
123
+ parts.push("", template.outro);
124
+ const includeSupport = template.includeSupportLinks ?? true;
125
+ if (includeSupport) {
126
+ const contactLines = [
127
+ metadata.instagram ? `Instagram: ${metadata.instagram}` : null,
128
+ metadata.contactEmail ? `Email: ${metadata.contactEmail}` : null,
129
+ metadata.termsUrl ? `- Terms of Use: ${metadata.termsUrl}` : null,
130
+ metadata.privacyUrl ? `- Privacy Policy: ${metadata.privacyUrl}` : null
131
+ ].filter((line) => line !== null);
132
+ if (contactLines.length > 0) {
133
+ parts.push("", "[Contact & Support]", "", ...contactLines);
134
+ }
135
+ }
136
+ return parts.join("\n");
137
+ }
138
+ function loadAsoFromConfig(slug) {
139
+ const productsDir = getProductsDir();
140
+ const configPath = path2.join(productsDir, slug, "config.json");
141
+ console.debug(`[loadAsoFromConfig] Looking for ${slug}:`);
142
+ console.debug(` - productsDir: ${productsDir}`);
143
+ console.debug(` - configPath: ${configPath}`);
144
+ console.debug(` - configPath exists: ${fs2.existsSync(configPath)}`);
145
+ if (!fs2.existsSync(configPath)) {
146
+ console.warn(`[loadAsoFromConfig] Config file not found at ${configPath}`);
147
+ return {};
148
+ }
149
+ try {
150
+ const configContent = fs2.readFileSync(configPath, "utf-8");
151
+ const config = JSON.parse(configContent);
152
+ const localesDir = path2.join(productsDir, slug, "locales");
153
+ console.debug(` - localesDir: ${localesDir}`);
154
+ console.debug(` - localesDir exists: ${fs2.existsSync(localesDir)}`);
155
+ if (!fs2.existsSync(localesDir)) {
156
+ console.warn(
157
+ `[loadAsoFromConfig] Locales directory not found at ${localesDir}`
158
+ );
159
+ return {};
160
+ }
161
+ const localeFiles = fs2.readdirSync(localesDir).filter((f) => f.endsWith(".json"));
162
+ const locales = {};
163
+ for (const file of localeFiles) {
164
+ const localeCode = file.replace(".json", "");
165
+ const localePath = path2.join(localesDir, file);
166
+ const localeContent = fs2.readFileSync(localePath, "utf-8");
167
+ locales[localeCode] = JSON.parse(localeContent);
168
+ }
169
+ console.debug(
170
+ ` - Found ${Object.keys(locales).length} locale file(s): ${Object.keys(
171
+ locales
172
+ ).join(", ")}`
173
+ );
174
+ if (Object.keys(locales).length === 0) {
175
+ console.warn(
176
+ `[loadAsoFromConfig] No locale files found in ${localesDir}`
177
+ );
178
+ }
179
+ const defaultLocale = config.content?.defaultLocale || DEFAULT_LOCALE;
180
+ const asoData = {};
181
+ if (config.packageName) {
182
+ const googlePlayLocales = {};
183
+ const metadata = config.metadata || {};
184
+ const screenshots = metadata.screenshots || {};
185
+ for (const [locale, localeData] of Object.entries(locales)) {
186
+ if (!isSupportedLocale(locale)) {
187
+ console.debug(
188
+ `Skipping locale ${locale} - not a valid unified locale`
189
+ );
190
+ continue;
191
+ }
192
+ if (!isGooglePlayLocale(locale)) {
193
+ console.debug(
194
+ `Skipping locale ${locale} - not supported by Google Play`
195
+ );
196
+ continue;
197
+ }
198
+ const aso = localeData.aso || {};
199
+ if (!aso || !aso.title && !aso.shortDescription) {
200
+ console.warn(
201
+ `Locale ${locale} has no ASO data (title or shortDescription)`
202
+ );
203
+ }
204
+ const screenshotsDir = path2.join(productsDir, slug, "screenshots", locale);
205
+ const hasScreenshots = fs2.existsSync(screenshotsDir);
206
+ const localeScreenshots = hasScreenshots ? {
207
+ phone: screenshots.phone?.map(
208
+ (p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
209
+ ),
210
+ tablet: screenshots.tablet?.map(
211
+ (p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
212
+ )
213
+ } : {
214
+ phone: void 0,
215
+ tablet: void 0
216
+ };
217
+ googlePlayLocales[locale] = {
218
+ title: aso.title || "",
219
+ shortDescription: aso.shortDescription || "",
220
+ fullDescription: generateFullDescription(localeData, metadata),
221
+ packageName: config.packageName,
222
+ defaultLanguage: locale,
223
+ screenshots: {
224
+ phone: localeScreenshots.phone || [],
225
+ tablet: localeScreenshots.tablet
226
+ }
227
+ };
228
+ }
229
+ const googleLocaleKeys = Object.keys(googlePlayLocales);
230
+ if (googleLocaleKeys.length > 0) {
231
+ const hasConfigDefault = isGooglePlayLocale(defaultLocale) && Boolean(googlePlayLocales[defaultLocale]);
232
+ const resolvedDefault = hasConfigDefault ? defaultLocale : googlePlayLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : googleLocaleKeys[0];
233
+ asoData.googlePlay = {
234
+ locales: googlePlayLocales,
235
+ defaultLocale: resolvedDefault,
236
+ // App-level contact information
237
+ contactEmail: metadata.contactEmail,
238
+ contactWebsite: metadata.supportUrl
239
+ };
240
+ }
241
+ }
242
+ if (config.bundleId) {
243
+ const appStoreLocales = {};
244
+ const metadata = config.metadata || {};
245
+ const screenshots = metadata.screenshots || {};
246
+ for (const [locale, localeData] of Object.entries(locales)) {
247
+ if (!isSupportedLocale(locale)) {
248
+ console.debug(
249
+ `Skipping locale ${locale} - not a valid unified locale`
250
+ );
251
+ continue;
252
+ }
253
+ if (!isAppStoreLocale(locale)) {
254
+ console.debug(
255
+ `Skipping locale ${locale} - not supported by App Store`
256
+ );
257
+ continue;
258
+ }
259
+ const aso = localeData.aso || {};
260
+ if (!aso || !aso.title && !aso.shortDescription) {
261
+ console.warn(
262
+ `Locale ${locale} has no ASO data (title or shortDescription)`
263
+ );
264
+ }
265
+ const screenshotsDir = path2.join(productsDir, slug, "screenshots", locale);
266
+ const hasScreenshots = fs2.existsSync(screenshotsDir);
267
+ const localeScreenshots = hasScreenshots ? {
268
+ phone: screenshots.phone?.map(
269
+ (p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
270
+ ),
271
+ tablet: screenshots.tablet?.map(
272
+ (p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
273
+ )
274
+ } : {
275
+ phone: void 0,
276
+ tablet: void 0
277
+ };
278
+ appStoreLocales[locale] = {
279
+ name: aso.title || "",
280
+ subtitle: aso.subtitle,
281
+ description: generateFullDescription(localeData, metadata),
282
+ keywords: Array.isArray(aso.keywords) ? aso.keywords.join(", ") : aso.keywords,
283
+ promotionalText: void 0,
284
+ bundleId: config.bundleId,
285
+ locale,
286
+ screenshots: {
287
+ // 폰 스크린샷을 iphone65로 매핑
288
+ iphone65: localeScreenshots.phone || [],
289
+ // 태블릿 스크린샷을 ipadPro129로 매핑
290
+ ipadPro129: localeScreenshots.tablet
291
+ }
292
+ };
293
+ }
294
+ const appStoreLocaleKeys = Object.keys(appStoreLocales);
295
+ if (appStoreLocaleKeys.length > 0) {
296
+ const hasConfigDefault = isAppStoreLocale(defaultLocale) && Boolean(appStoreLocales[defaultLocale]);
297
+ const resolvedDefault = hasConfigDefault ? defaultLocale : appStoreLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : appStoreLocaleKeys[0];
298
+ asoData.appStore = {
299
+ locales: appStoreLocales,
300
+ defaultLocale: resolvedDefault,
301
+ // App-level contact information
302
+ contactEmail: metadata.contactEmail,
303
+ supportUrl: metadata.supportUrl,
304
+ marketingUrl: metadata.marketingUrl,
305
+ privacyPolicyUrl: metadata.privacyUrl,
306
+ termsUrl: metadata.termsUrl
307
+ };
308
+ }
309
+ }
310
+ const hasGooglePlay = !!asoData.googlePlay;
311
+ const hasAppStore = !!asoData.appStore;
312
+ console.debug(`[loadAsoFromConfig] Result for ${slug}:`);
313
+ console.debug(
314
+ ` - Google Play data: ${hasGooglePlay ? "found" : "not found"}`
315
+ );
316
+ console.debug(` - App Store data: ${hasAppStore ? "found" : "not found"}`);
317
+ if (!hasGooglePlay && !hasAppStore) {
318
+ console.warn(`[loadAsoFromConfig] No ASO data generated for ${slug}`);
319
+ }
320
+ return asoData;
321
+ } catch (error) {
322
+ console.error(
323
+ `[loadAsoFromConfig] Failed to load ASO data from config for ${slug}:`,
324
+ error
325
+ );
326
+ return {};
327
+ }
328
+ }
329
+ function saveAsoToConfig(slug, config) {
330
+ const productsDir = getProductsDir();
331
+ const configPath = path2.join(productsDir, slug, "config.json");
332
+ fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
333
+ }
334
+ function saveAsoToAsoDir(slug, asoData) {
335
+ const rootDir = getPushDataDir();
336
+ if (asoData.googlePlay) {
337
+ const asoPath = path2.join(
338
+ rootDir,
339
+ "products",
340
+ slug,
341
+ "store",
342
+ "google-play",
343
+ "aso-data.json"
344
+ );
345
+ const dir = path2.dirname(asoPath);
346
+ if (!fs2.existsSync(dir)) {
347
+ fs2.mkdirSync(dir, { recursive: true });
348
+ }
349
+ const googlePlayData = asoData.googlePlay;
350
+ const multilingualData = "locales" in googlePlayData ? googlePlayData : {
351
+ locales: {
352
+ [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData
353
+ },
354
+ defaultLocale: googlePlayData.defaultLanguage || DEFAULT_LOCALE
355
+ };
356
+ fs2.writeFileSync(
357
+ asoPath,
358
+ JSON.stringify({ googlePlay: multilingualData }, null, 2) + "\n",
359
+ "utf-8"
360
+ );
361
+ }
362
+ if (asoData.appStore) {
363
+ const asoPath = path2.join(
364
+ rootDir,
365
+ "products",
366
+ slug,
367
+ "store",
368
+ "app-store",
369
+ "aso-data.json"
370
+ );
371
+ const dir = path2.dirname(asoPath);
372
+ if (!fs2.existsSync(dir)) {
373
+ fs2.mkdirSync(dir, { recursive: true });
374
+ }
375
+ const appStoreData = asoData.appStore;
376
+ const multilingualData = "locales" in appStoreData ? appStoreData : {
377
+ locales: {
378
+ [appStoreData.locale || DEFAULT_LOCALE]: appStoreData
379
+ },
380
+ defaultLocale: appStoreData.locale || DEFAULT_LOCALE
381
+ };
382
+ fs2.writeFileSync(
383
+ asoPath,
384
+ JSON.stringify({ appStore: multilingualData }, null, 2) + "\n",
385
+ "utf-8"
386
+ );
387
+ }
388
+ }
389
+
390
+ export {
391
+ getAsoDataDir,
392
+ getPullDataDir,
393
+ getPushDataDir,
394
+ getPublicDir,
395
+ getKeywordResearchDir,
396
+ getProductsDir,
397
+ getGeminiApiKey,
398
+ loadAsoFromConfig,
399
+ saveAsoToConfig,
400
+ saveAsoToAsoDir
401
+ };