pabal-resource-mcp 1.10.3 → 1.10.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/bin/mcp-server.js +20 -10
- package/dist/chunk-4JFBWKY4.js +418 -0
- package/dist/index.js +1 -1
- package/package.json +2 -1
package/dist/bin/mcp-server.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getPushDataDir,
|
|
15
15
|
loadAsoFromConfig,
|
|
16
16
|
saveAsoToAsoDir
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-4JFBWKY4.js";
|
|
18
18
|
import {
|
|
19
19
|
DEFAULT_LOCALE,
|
|
20
20
|
appStoreToUnified,
|
|
@@ -597,6 +597,8 @@ function prepareAsoDataForPush(configData) {
|
|
|
597
597
|
const locales = isAppStoreMultilingual(appStoreData) ? appStoreData.locales : { [appStoreData.locale || DEFAULT_LOCALE]: appStoreData };
|
|
598
598
|
const appLevelSupportUrl = isAppStoreMultilingual(appStoreData) ? appStoreData.supportUrl : void 0;
|
|
599
599
|
const appLevelMarketingUrl = isAppStoreMultilingual(appStoreData) ? appStoreData.marketingUrl : void 0;
|
|
600
|
+
const appLevelPrivacyPolicyUrl = isAppStoreMultilingual(appStoreData) ? appStoreData.privacyPolicyUrl : void 0;
|
|
601
|
+
const appLevelTermsUrl = isAppStoreMultilingual(appStoreData) ? appStoreData.termsUrl : void 0;
|
|
600
602
|
const convertedLocales = {};
|
|
601
603
|
for (const [unifiedLocale, localeData] of Object.entries(locales)) {
|
|
602
604
|
const appStoreLocale = unifiedToAppStore(unifiedLocale);
|
|
@@ -605,7 +607,9 @@ function prepareAsoDataForPush(configData) {
|
|
|
605
607
|
...localeData,
|
|
606
608
|
locale: appStoreLocale,
|
|
607
609
|
supportUrl: localeData.supportUrl || appLevelSupportUrl,
|
|
608
|
-
marketingUrl: localeData.marketingUrl || appLevelMarketingUrl
|
|
610
|
+
marketingUrl: localeData.marketingUrl || appLevelMarketingUrl,
|
|
611
|
+
privacyPolicyUrl: localeData.privacyPolicyUrl || appLevelPrivacyPolicyUrl,
|
|
612
|
+
termsUrl: localeData.termsUrl || appLevelTermsUrl
|
|
609
613
|
};
|
|
610
614
|
}
|
|
611
615
|
}
|
|
@@ -753,7 +757,11 @@ async function downloadScreenshotArray(screenshots, asoDir, relativeDir, prefix)
|
|
|
753
757
|
const filename = `${prefix}-${i + 1}.png`;
|
|
754
758
|
const outputPath = path4.join(asoDir, filename);
|
|
755
759
|
const relativePath = `${relativeDir}/${filename}`;
|
|
756
|
-
const result = await downloadAndVerifyScreenshot(
|
|
760
|
+
const result = await downloadAndVerifyScreenshot(
|
|
761
|
+
url,
|
|
762
|
+
outputPath,
|
|
763
|
+
relativePath
|
|
764
|
+
);
|
|
757
765
|
if (result) {
|
|
758
766
|
relativePaths.push(result);
|
|
759
767
|
}
|
|
@@ -798,8 +806,8 @@ async function downloadScreenshotsToAsoDir(slug, asoData) {
|
|
|
798
806
|
relativeDir,
|
|
799
807
|
"tablet7"
|
|
800
808
|
);
|
|
801
|
-
const
|
|
802
|
-
localeData.screenshots?.tablet10,
|
|
809
|
+
const tabletPaths = await downloadScreenshotArray(
|
|
810
|
+
localeData.screenshots?.tablet ?? localeData.screenshots?.tablet10,
|
|
803
811
|
asoDir,
|
|
804
812
|
relativeDir,
|
|
805
813
|
"tablet10"
|
|
@@ -817,12 +825,13 @@ async function downloadScreenshotsToAsoDir(slug, asoData) {
|
|
|
817
825
|
featureGraphicPath = result;
|
|
818
826
|
}
|
|
819
827
|
}
|
|
820
|
-
const hasDownloadedScreenshots = phonePaths.length > 0 || tablet7Paths.length > 0 ||
|
|
828
|
+
const hasDownloadedScreenshots = phonePaths.length > 0 || tablet7Paths.length > 0 || tabletPaths.length > 0;
|
|
821
829
|
if (hasDownloadedScreenshots || featureGraphicPath) {
|
|
822
830
|
localeData.screenshots = {
|
|
823
831
|
phone: phonePaths,
|
|
832
|
+
tablet: tabletPaths,
|
|
824
833
|
tablet7: tablet7Paths,
|
|
825
|
-
tablet10:
|
|
834
|
+
tablet10: tabletPaths
|
|
826
835
|
};
|
|
827
836
|
if (featureGraphicPath) {
|
|
828
837
|
localeData.featureGraphic = featureGraphicPath;
|
|
@@ -973,7 +982,7 @@ Possible causes:
|
|
|
973
982
|
4. Locale files don't contain valid ASO data`
|
|
974
983
|
);
|
|
975
984
|
}
|
|
976
|
-
const
|
|
985
|
+
const previewStoreData = prepareAsoDataForPush(sanitizedData);
|
|
977
986
|
const validationIssues = validateFieldLimits(sanitizedData);
|
|
978
987
|
const validationMessage = formatValidationIssues(validationIssues);
|
|
979
988
|
const pushDataRoot = getPushDataDir();
|
|
@@ -985,7 +994,7 @@ Possible causes:
|
|
|
985
994
|
text: `Preview mode - Data that would be saved to ${pushDataRoot}:
|
|
986
995
|
|
|
987
996
|
${JSON.stringify(
|
|
988
|
-
|
|
997
|
+
previewStoreData,
|
|
989
998
|
null,
|
|
990
999
|
2
|
|
991
1000
|
)}
|
|
@@ -1005,8 +1014,9 @@ Sanitized invalid characters:
|
|
|
1005
1014
|
${validationMessage}`
|
|
1006
1015
|
);
|
|
1007
1016
|
}
|
|
1017
|
+
await downloadScreenshotsToAsoDir(slug, sanitizedData);
|
|
1018
|
+
const storeData = prepareAsoDataForPush(sanitizedData);
|
|
1008
1019
|
saveRawAsoData(slug, storeData);
|
|
1009
|
-
await downloadScreenshotsToAsoDir(slug, configData);
|
|
1010
1020
|
const localeCounts = {};
|
|
1011
1021
|
if (storeData.googlePlay) {
|
|
1012
1022
|
const googlePlayData = storeData.googlePlay;
|
|
@@ -0,0 +1,418 @@
|
|
|
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(
|
|
205
|
+
productsDir,
|
|
206
|
+
slug,
|
|
207
|
+
"screenshots",
|
|
208
|
+
locale
|
|
209
|
+
);
|
|
210
|
+
const phoneDir = path2.join(screenshotsDir, "phone");
|
|
211
|
+
const tabletDir = path2.join(screenshotsDir, "tablet");
|
|
212
|
+
const featureGraphicPath = path2.join(
|
|
213
|
+
screenshotsDir,
|
|
214
|
+
"feature-graphic.png"
|
|
215
|
+
);
|
|
216
|
+
const hasPhoneScreenshots = fs2.existsSync(phoneDir);
|
|
217
|
+
const hasTabletScreenshots = fs2.existsSync(tabletDir);
|
|
218
|
+
const hasFeatureGraphic = fs2.existsSync(featureGraphicPath);
|
|
219
|
+
const localeScreenshots = {
|
|
220
|
+
phone: hasPhoneScreenshots ? screenshots.phone?.map(
|
|
221
|
+
(p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
|
|
222
|
+
) : void 0,
|
|
223
|
+
tablet: hasTabletScreenshots ? screenshots.tablet?.map(
|
|
224
|
+
(p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
|
|
225
|
+
) : void 0
|
|
226
|
+
};
|
|
227
|
+
googlePlayLocales[locale] = {
|
|
228
|
+
title: aso.title || "",
|
|
229
|
+
shortDescription: aso.shortDescription || "",
|
|
230
|
+
fullDescription: generateFullDescription(localeData, metadata),
|
|
231
|
+
packageName: config.packageName,
|
|
232
|
+
defaultLanguage: locale,
|
|
233
|
+
screenshots: {
|
|
234
|
+
phone: localeScreenshots.phone || [],
|
|
235
|
+
tablet: localeScreenshots.tablet
|
|
236
|
+
},
|
|
237
|
+
featureGraphic: hasFeatureGraphic ? `/products/${slug}/screenshots/${locale}/feature-graphic.png` : metadata.featureGraphic
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const googleLocaleKeys = Object.keys(googlePlayLocales);
|
|
241
|
+
if (googleLocaleKeys.length > 0) {
|
|
242
|
+
const hasConfigDefault = isGooglePlayLocale(defaultLocale) && Boolean(googlePlayLocales[defaultLocale]);
|
|
243
|
+
const resolvedDefault = hasConfigDefault ? defaultLocale : googlePlayLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : googleLocaleKeys[0];
|
|
244
|
+
asoData.googlePlay = {
|
|
245
|
+
locales: googlePlayLocales,
|
|
246
|
+
defaultLocale: resolvedDefault,
|
|
247
|
+
// App-level contact information
|
|
248
|
+
contactEmail: metadata.contactEmail,
|
|
249
|
+
contactWebsite: metadata.supportUrl,
|
|
250
|
+
youtubeUrl: metadata.youtubeUrl
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (config.bundleId) {
|
|
255
|
+
const appStoreLocales = {};
|
|
256
|
+
const metadata = config.metadata || {};
|
|
257
|
+
const screenshots = metadata.screenshots || {};
|
|
258
|
+
for (const [locale, localeData] of Object.entries(locales)) {
|
|
259
|
+
if (!isSupportedLocale(locale)) {
|
|
260
|
+
console.debug(
|
|
261
|
+
`Skipping locale ${locale} - not a valid unified locale`
|
|
262
|
+
);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (!isAppStoreLocale(locale)) {
|
|
266
|
+
console.debug(
|
|
267
|
+
`Skipping locale ${locale} - not supported by App Store`
|
|
268
|
+
);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const aso = localeData.aso || {};
|
|
272
|
+
if (!aso || !aso.title && !aso.shortDescription) {
|
|
273
|
+
console.warn(
|
|
274
|
+
`Locale ${locale} has no ASO data (title or shortDescription)`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const screenshotsDir = path2.join(
|
|
278
|
+
productsDir,
|
|
279
|
+
slug,
|
|
280
|
+
"screenshots",
|
|
281
|
+
locale
|
|
282
|
+
);
|
|
283
|
+
const phoneDir = path2.join(screenshotsDir, "phone");
|
|
284
|
+
const tabletDir = path2.join(screenshotsDir, "tablet");
|
|
285
|
+
const hasPhoneScreenshots = fs2.existsSync(phoneDir);
|
|
286
|
+
const hasTabletScreenshots = fs2.existsSync(tabletDir);
|
|
287
|
+
const localeScreenshots = {
|
|
288
|
+
phone: hasPhoneScreenshots ? screenshots.phone?.map(
|
|
289
|
+
(p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
|
|
290
|
+
) : void 0,
|
|
291
|
+
tablet: hasTabletScreenshots ? screenshots.tablet?.map(
|
|
292
|
+
(p) => p.replace(/\/screenshots\/[^/]+\//, `/screenshots/${locale}/`)
|
|
293
|
+
) : void 0
|
|
294
|
+
};
|
|
295
|
+
appStoreLocales[locale] = {
|
|
296
|
+
name: aso.title || "",
|
|
297
|
+
subtitle: aso.subtitle,
|
|
298
|
+
description: generateFullDescription(localeData, metadata),
|
|
299
|
+
keywords: Array.isArray(aso.keywords) ? aso.keywords.join(", ") : aso.keywords,
|
|
300
|
+
promotionalText: void 0,
|
|
301
|
+
bundleId: config.bundleId,
|
|
302
|
+
locale,
|
|
303
|
+
screenshots: {
|
|
304
|
+
// 폰 스크린샷을 iphone65로 매핑
|
|
305
|
+
iphone65: localeScreenshots.phone || [],
|
|
306
|
+
// 태블릿 스크린샷을 ipadPro129로 매핑
|
|
307
|
+
ipadPro129: localeScreenshots.tablet
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const appStoreLocaleKeys = Object.keys(appStoreLocales);
|
|
312
|
+
if (appStoreLocaleKeys.length > 0) {
|
|
313
|
+
const hasConfigDefault = isAppStoreLocale(defaultLocale) && Boolean(appStoreLocales[defaultLocale]);
|
|
314
|
+
const resolvedDefault = hasConfigDefault ? defaultLocale : appStoreLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : appStoreLocaleKeys[0];
|
|
315
|
+
asoData.appStore = {
|
|
316
|
+
locales: appStoreLocales,
|
|
317
|
+
defaultLocale: resolvedDefault,
|
|
318
|
+
// App-level contact information
|
|
319
|
+
contactEmail: metadata.contactEmail,
|
|
320
|
+
supportUrl: metadata.supportUrl,
|
|
321
|
+
marketingUrl: metadata.marketingUrl,
|
|
322
|
+
privacyPolicyUrl: metadata.privacyUrl,
|
|
323
|
+
termsUrl: metadata.termsUrl
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const hasGooglePlay = !!asoData.googlePlay;
|
|
328
|
+
const hasAppStore = !!asoData.appStore;
|
|
329
|
+
console.debug(`[loadAsoFromConfig] Result for ${slug}:`);
|
|
330
|
+
console.debug(
|
|
331
|
+
` - Google Play data: ${hasGooglePlay ? "found" : "not found"}`
|
|
332
|
+
);
|
|
333
|
+
console.debug(` - App Store data: ${hasAppStore ? "found" : "not found"}`);
|
|
334
|
+
if (!hasGooglePlay && !hasAppStore) {
|
|
335
|
+
console.warn(`[loadAsoFromConfig] No ASO data generated for ${slug}`);
|
|
336
|
+
}
|
|
337
|
+
return asoData;
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error(
|
|
340
|
+
`[loadAsoFromConfig] Failed to load ASO data from config for ${slug}:`,
|
|
341
|
+
error
|
|
342
|
+
);
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function saveAsoToConfig(slug, config) {
|
|
347
|
+
const productsDir = getProductsDir();
|
|
348
|
+
const configPath = path2.join(productsDir, slug, "config.json");
|
|
349
|
+
fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
350
|
+
}
|
|
351
|
+
function saveAsoToAsoDir(slug, asoData) {
|
|
352
|
+
const rootDir = getPushDataDir();
|
|
353
|
+
if (asoData.googlePlay) {
|
|
354
|
+
const asoPath = path2.join(
|
|
355
|
+
rootDir,
|
|
356
|
+
"products",
|
|
357
|
+
slug,
|
|
358
|
+
"store",
|
|
359
|
+
"google-play",
|
|
360
|
+
"aso-data.json"
|
|
361
|
+
);
|
|
362
|
+
const dir = path2.dirname(asoPath);
|
|
363
|
+
if (!fs2.existsSync(dir)) {
|
|
364
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
365
|
+
}
|
|
366
|
+
const googlePlayData = asoData.googlePlay;
|
|
367
|
+
const multilingualData = "locales" in googlePlayData ? googlePlayData : {
|
|
368
|
+
locales: {
|
|
369
|
+
[googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData
|
|
370
|
+
},
|
|
371
|
+
defaultLocale: googlePlayData.defaultLanguage || DEFAULT_LOCALE
|
|
372
|
+
};
|
|
373
|
+
fs2.writeFileSync(
|
|
374
|
+
asoPath,
|
|
375
|
+
JSON.stringify({ googlePlay: multilingualData }, null, 2) + "\n",
|
|
376
|
+
"utf-8"
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (asoData.appStore) {
|
|
380
|
+
const asoPath = path2.join(
|
|
381
|
+
rootDir,
|
|
382
|
+
"products",
|
|
383
|
+
slug,
|
|
384
|
+
"store",
|
|
385
|
+
"app-store",
|
|
386
|
+
"aso-data.json"
|
|
387
|
+
);
|
|
388
|
+
const dir = path2.dirname(asoPath);
|
|
389
|
+
if (!fs2.existsSync(dir)) {
|
|
390
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
391
|
+
}
|
|
392
|
+
const appStoreData = asoData.appStore;
|
|
393
|
+
const multilingualData = "locales" in appStoreData ? appStoreData : {
|
|
394
|
+
locales: {
|
|
395
|
+
[appStoreData.locale || DEFAULT_LOCALE]: appStoreData
|
|
396
|
+
},
|
|
397
|
+
defaultLocale: appStoreData.locale || DEFAULT_LOCALE
|
|
398
|
+
};
|
|
399
|
+
fs2.writeFileSync(
|
|
400
|
+
asoPath,
|
|
401
|
+
JSON.stringify({ appStore: multilingualData }, null, 2) + "\n",
|
|
402
|
+
"utf-8"
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export {
|
|
408
|
+
getAsoDataDir,
|
|
409
|
+
getPullDataDir,
|
|
410
|
+
getPushDataDir,
|
|
411
|
+
getPublicDir,
|
|
412
|
+
getKeywordResearchDir,
|
|
413
|
+
getProductsDir,
|
|
414
|
+
getGeminiApiKey,
|
|
415
|
+
loadAsoFromConfig,
|
|
416
|
+
saveAsoToConfig,
|
|
417
|
+
saveAsoToAsoDir
|
|
418
|
+
};
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pabal-resource-mcp",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for ASO data management with shared types and utilities",
|
|
6
6
|
"author": "skyu",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup src/index.ts src/browser.ts src/bin/mcp-server.ts --format esm --dts --external node:fs --external node:path --external node:os",
|
|
59
59
|
"dev": "tsx src/bin/mcp-server.ts",
|
|
60
|
+
"test": "tsx --test \"tests/**/*.test.ts\"",
|
|
60
61
|
"typecheck": "tsc --noEmit",
|
|
61
62
|
"prepublishOnly": "npm run build"
|
|
62
63
|
},
|