pabal-store-api-mcp 1.1.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 +95 -0
- package/bin/pabal-mcp.js +6 -0
- package/dist/src/core/clients/app-store-factory.d.ts +29 -0
- package/dist/src/core/clients/app-store-factory.js +72 -0
- package/dist/src/core/clients/client-factory-helpers.d.ts +7 -0
- package/dist/src/core/clients/client-factory-helpers.js +10 -0
- package/dist/src/core/clients/google-play-factory.d.ts +29 -0
- package/dist/src/core/clients/google-play-factory.js +72 -0
- package/dist/src/core/clients/types.d.ts +8 -0
- package/dist/src/core/clients/types.js +1 -0
- package/dist/src/core/helpers/formatters.d.ts +3 -0
- package/dist/src/core/helpers/formatters.js +38 -0
- package/dist/src/core/helpers/registration.d.ts +21 -0
- package/dist/src/core/helpers/registration.js +21 -0
- package/dist/src/core/helpers/translate-release-notes.d.ts +46 -0
- package/dist/src/core/helpers/translate-release-notes.js +87 -0
- package/dist/src/core/services/app-resolution-service.d.ts +14 -0
- package/dist/src/core/services/app-resolution-service.js +35 -0
- package/dist/src/core/services/app-store-service.d.ts +41 -0
- package/dist/src/core/services/app-store-service.js +266 -0
- package/dist/src/core/services/google-play-service.d.ts +36 -0
- package/dist/src/core/services/google-play-service.js +203 -0
- package/dist/src/core/services/service-helpers.d.ts +15 -0
- package/dist/src/core/services/service-helpers.js +31 -0
- package/dist/src/core/services/types.d.ts +81 -0
- package/dist/src/core/services/types.js +1 -0
- package/dist/src/core/workflows/version-info.d.ts +29 -0
- package/dist/src/core/workflows/version-info.js +100 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +279 -0
- package/dist/src/packages/common/errors/app-error.d.ts +39 -0
- package/dist/src/packages/common/errors/app-error.js +134 -0
- package/dist/src/packages/common/errors/error-codes.d.ts +63 -0
- package/dist/src/packages/common/errors/error-codes.js +71 -0
- package/dist/src/packages/common/errors/status-codes.d.ts +10 -0
- package/dist/src/packages/common/errors/status-codes.js +9 -0
- package/dist/src/packages/configs/aso-config/constants.d.ts +14 -0
- package/dist/src/packages/configs/aso-config/constants.js +102 -0
- package/dist/src/packages/configs/aso-config/locale-guards.d.ts +3 -0
- package/dist/src/packages/configs/aso-config/locale-guards.js +7 -0
- package/dist/src/packages/configs/aso-config/store.d.ts +11 -0
- package/dist/src/packages/configs/aso-config/store.js +11 -0
- package/dist/src/packages/configs/aso-config/types.d.ts +98 -0
- package/dist/src/packages/configs/aso-config/types.js +2 -0
- package/dist/src/packages/configs/aso-config/utils.d.ts +43 -0
- package/dist/src/packages/configs/aso-config/utils.js +223 -0
- package/dist/src/packages/configs/secrets-config/config.d.ts +12 -0
- package/dist/src/packages/configs/secrets-config/config.js +187 -0
- package/dist/src/packages/configs/secrets-config/constants.d.ts +1 -0
- package/dist/src/packages/configs/secrets-config/constants.js +1 -0
- package/dist/src/packages/configs/secrets-config/errors.d.ts +9 -0
- package/dist/src/packages/configs/secrets-config/errors.js +15 -0
- package/dist/src/packages/configs/secrets-config/registered-apps.d.ts +52 -0
- package/dist/src/packages/configs/secrets-config/registered-apps.js +108 -0
- package/dist/src/packages/configs/secrets-config/schemas.d.ts +21 -0
- package/dist/src/packages/configs/secrets-config/schemas.js +9 -0
- package/dist/src/packages/configs/secrets-config/types.d.ts +8 -0
- package/dist/src/packages/configs/secrets-config/types.js +1 -0
- package/dist/src/packages/stores/app-store/api-converters.d.ts +26 -0
- package/dist/src/packages/stores/app-store/api-converters.js +131 -0
- package/dist/src/packages/stores/app-store/api-endpoints.d.ts +33 -0
- package/dist/src/packages/stores/app-store/api-endpoints.js +157 -0
- package/dist/src/packages/stores/app-store/auth.d.ts +12 -0
- package/dist/src/packages/stores/app-store/auth.js +36 -0
- package/dist/src/packages/stores/app-store/client.d.ts +78 -0
- package/dist/src/packages/stores/app-store/client.js +637 -0
- package/dist/src/packages/stores/app-store/constants.d.ts +11 -0
- package/dist/src/packages/stores/app-store/constants.js +38 -0
- package/dist/src/packages/stores/app-store/generated-types.d.ts +118537 -0
- package/dist/src/packages/stores/app-store/generated-types.js +5 -0
- package/dist/src/packages/stores/app-store/types.d.ts +39 -0
- package/dist/src/packages/stores/app-store/types.js +9 -0
- package/dist/src/packages/stores/app-store/verify-auth.d.ts +16 -0
- package/dist/src/packages/stores/app-store/verify-auth.js +34 -0
- package/dist/src/packages/stores/play-store/api-converters.d.ts +58 -0
- package/dist/src/packages/stores/play-store/api-converters.js +209 -0
- package/dist/src/packages/stores/play-store/api-endpoints.d.ts +68 -0
- package/dist/src/packages/stores/play-store/api-endpoints.js +145 -0
- package/dist/src/packages/stores/play-store/client.d.ts +55 -0
- package/dist/src/packages/stores/play-store/client.js +628 -0
- package/dist/src/packages/stores/play-store/constants.d.ts +10 -0
- package/dist/src/packages/stores/play-store/constants.js +17 -0
- package/dist/src/packages/stores/play-store/types.d.ts +146 -0
- package/dist/src/packages/stores/play-store/types.js +9 -0
- package/dist/src/packages/stores/play-store/verify-auth.d.ts +13 -0
- package/dist/src/packages/stores/play-store/verify-auth.js +31 -0
- package/dist/src/tools/apps/add.d.ts +28 -0
- package/dist/src/tools/apps/add.js +307 -0
- package/dist/src/tools/apps/init.d.ts +58 -0
- package/dist/src/tools/apps/init.js +390 -0
- package/dist/src/tools/apps/search.d.ts +33 -0
- package/dist/src/tools/apps/search.js +147 -0
- package/dist/src/tools/aso/pull.d.ts +22 -0
- package/dist/src/tools/aso/pull.js +264 -0
- package/dist/src/tools/aso/push.d.ts +23 -0
- package/dist/src/tools/aso/push.js +189 -0
- package/dist/src/tools/auth/app-store.d.ts +9 -0
- package/dist/src/tools/auth/app-store.js +34 -0
- package/dist/src/tools/auth/check.d.ts +14 -0
- package/dist/src/tools/auth/check.js +50 -0
- package/dist/src/tools/auth/play-store.d.ts +9 -0
- package/dist/src/tools/auth/play-store.js +30 -0
- package/dist/src/tools/release/check-versions.d.ts +14 -0
- package/dist/src/tools/release/check-versions.js +65 -0
- package/dist/src/tools/release/create.d.ts +23 -0
- package/dist/src/tools/release/create.js +128 -0
- package/dist/src/tools/release/pull-notes.d.ts +22 -0
- package/dist/src/tools/release/pull-notes.js +151 -0
- package/dist/src/tools/release/update-notes.d.ts +110 -0
- package/dist/src/tools/release/update-notes.js +537 -0
- package/package.json +71 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setup-apps: Query apps from store and auto-register
|
|
3
|
+
*/
|
|
4
|
+
import { loadConfig } from "../../packages/configs/secrets-config/config.js";
|
|
5
|
+
import { registerApp, findApp, loadRegisteredApps, saveRegisteredApps, } from "../../packages/configs/secrets-config/registered-apps.js";
|
|
6
|
+
import { toRegisteredAppStoreInfo, toRegisteredGooglePlayInfo, } from "../../core/helpers/registration.js";
|
|
7
|
+
import { AppStoreService } from "../../core/services/app-store-service.js";
|
|
8
|
+
import { GooglePlayService } from "../../core/services/google-play-service.js";
|
|
9
|
+
const appStoreService = new AppStoreService();
|
|
10
|
+
const googlePlayService = new GooglePlayService();
|
|
11
|
+
/**
|
|
12
|
+
* Check Play Store access (기존 호환성을 위한 래퍼)
|
|
13
|
+
*/
|
|
14
|
+
async function checkPlayStoreAccess(packageName) {
|
|
15
|
+
const appInfo = await googlePlayService.fetchAppInfo(packageName);
|
|
16
|
+
if (!appInfo.found) {
|
|
17
|
+
return { accessible: false };
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
accessible: true,
|
|
21
|
+
title: appInfo.name,
|
|
22
|
+
supportedLocales: appInfo.supportedLocales,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export async function handleSetupApps(options) {
|
|
26
|
+
const { store = "both", packageName } = options;
|
|
27
|
+
let config;
|
|
28
|
+
try {
|
|
29
|
+
config = loadConfig();
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: `❌ Failed to load config: ${message}`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
console.error(`[MCP] 📱 Initializing apps (store: ${store})`);
|
|
44
|
+
// both: Query App Store apps then check Play Store
|
|
45
|
+
if (store === "both" || store === "appStore") {
|
|
46
|
+
if (!config.appStore) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: "❌ App Store authentication not configured. Please check ~/.config/pabal-mcp/config.json.",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const clientResult = appStoreService.createClient("dummy"); // listAllApps() does not use bundleId
|
|
57
|
+
if (!clientResult.success) {
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: `❌ Failed to create App Store client: ${clientResult.error.message}`,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
console.error(`[MCP] 📋 Fetching app list from App Store...`);
|
|
69
|
+
const apps = await clientResult.data.listAllApps({
|
|
70
|
+
onlyReleased: true,
|
|
71
|
+
});
|
|
72
|
+
console.error(`[MCP] ✅ Found ${apps.length} apps`);
|
|
73
|
+
// 모든 앱의 언어 정보를 미리 가져오기 위한 클라이언트 인스턴스
|
|
74
|
+
const appInfoClientResult = appStoreService.createClient("dummy");
|
|
75
|
+
if (!appInfoClientResult.success) {
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: `❌ Failed to create App Store info client: ${appInfoClientResult.error.message}`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const appInfoClient = appInfoClientResult.data;
|
|
86
|
+
if (apps.length === 0) {
|
|
87
|
+
return {
|
|
88
|
+
content: [
|
|
89
|
+
{
|
|
90
|
+
type: "text",
|
|
91
|
+
text: "📱 No apps registered in App Store.",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Prepare Play Store service account
|
|
97
|
+
const playStoreEnabled = store === "both" && !!config.playStore?.serviceAccountJson;
|
|
98
|
+
// Auto-register
|
|
99
|
+
const registered = [];
|
|
100
|
+
const skipped = [];
|
|
101
|
+
const playStoreFound = [];
|
|
102
|
+
const playStoreNotFound = [];
|
|
103
|
+
for (let i = 0; i < apps.length; i++) {
|
|
104
|
+
const app = apps[i];
|
|
105
|
+
// Use only last part of bundleId as slug (com.quartz.postblackbelt -> postblackbelt)
|
|
106
|
+
const parts = app.bundleId.split(".");
|
|
107
|
+
const slug = parts[parts.length - 1].toLowerCase();
|
|
108
|
+
console.error(`[MCP] [${i + 1}/${apps.length}] Processing: ${app.name} (${app.bundleId})`);
|
|
109
|
+
// Check if already registered (findApp searches by slug, bundleId, packageName)
|
|
110
|
+
let existing;
|
|
111
|
+
try {
|
|
112
|
+
existing = findApp(app.bundleId);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(`[MCP] ❌ Failed to load registered apps: ${error instanceof Error ? error.message : String(error)}`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (existing) {
|
|
119
|
+
// Update language info for existing apps
|
|
120
|
+
let appsConfig;
|
|
121
|
+
try {
|
|
122
|
+
appsConfig = loadRegisteredApps();
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error(`[MCP] ❌ Failed to load registered apps: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const appIndex = appsConfig.apps.findIndex((a) => a.slug === existing.slug);
|
|
129
|
+
if (appIndex >= 0) {
|
|
130
|
+
let updated = false;
|
|
131
|
+
// Update App Store language info
|
|
132
|
+
if (existing.appStore) {
|
|
133
|
+
const appStoreInfo = await appStoreService.fetchAppInfo(app.bundleId, appInfoClient);
|
|
134
|
+
if (appStoreInfo.found && appStoreInfo.supportedLocales) {
|
|
135
|
+
if (!appsConfig.apps[appIndex].appStore) {
|
|
136
|
+
appsConfig.apps[appIndex].appStore = {
|
|
137
|
+
bundleId: app.bundleId,
|
|
138
|
+
appId: app.id,
|
|
139
|
+
name: app.name,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
appsConfig.apps[appIndex].appStore.supportedLocales =
|
|
143
|
+
appStoreInfo.supportedLocales;
|
|
144
|
+
updated = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Update Google Play info (when in both mode)
|
|
148
|
+
if (playStoreEnabled) {
|
|
149
|
+
const playResult = await checkPlayStoreAccess(app.bundleId);
|
|
150
|
+
if (playResult.accessible) {
|
|
151
|
+
if (!appsConfig.apps[appIndex].googlePlay) {
|
|
152
|
+
appsConfig.apps[appIndex].googlePlay = {
|
|
153
|
+
packageName: app.bundleId,
|
|
154
|
+
name: playResult.title,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
appsConfig.apps[appIndex].googlePlay.supportedLocales =
|
|
158
|
+
playResult.supportedLocales;
|
|
159
|
+
appsConfig.apps[appIndex].googlePlay.name = playResult.title;
|
|
160
|
+
updated = true;
|
|
161
|
+
playStoreFound.push(app.name);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
playStoreNotFound.push(app.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (updated) {
|
|
168
|
+
saveRegisteredApps(appsConfig);
|
|
169
|
+
skipped.push(`${app.name} (${app.bundleId}) - language info updated`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
skipped.push(`${app.name} (${app.bundleId}) - already registered`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
skipped.push(`${app.name} (${app.bundleId}) - already registered`);
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
// App Store 정보 가져오기 (언어 정보 포함)
|
|
181
|
+
const appStoreInfo = await appStoreService.fetchAppInfo(app.bundleId, appInfoClient);
|
|
182
|
+
// Check Play Store (when in both mode)
|
|
183
|
+
let googlePlayInfo = undefined;
|
|
184
|
+
if (playStoreEnabled) {
|
|
185
|
+
const playResult = await checkPlayStoreAccess(app.bundleId);
|
|
186
|
+
if (playResult.accessible) {
|
|
187
|
+
googlePlayInfo = {
|
|
188
|
+
packageName: app.bundleId,
|
|
189
|
+
name: playResult.title,
|
|
190
|
+
supportedLocales: playResult.supportedLocales,
|
|
191
|
+
};
|
|
192
|
+
playStoreFound.push(app.name);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
playStoreNotFound.push(app.name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const registeredAppStoreInfo = toRegisteredAppStoreInfo({
|
|
200
|
+
bundleId: app.bundleId,
|
|
201
|
+
appInfo: appStoreInfo,
|
|
202
|
+
}) || {
|
|
203
|
+
bundleId: app.bundleId,
|
|
204
|
+
appId: app.id,
|
|
205
|
+
name: app.name,
|
|
206
|
+
};
|
|
207
|
+
registerApp({
|
|
208
|
+
slug,
|
|
209
|
+
name: app.name,
|
|
210
|
+
appStore: registeredAppStoreInfo,
|
|
211
|
+
googlePlay: googlePlayInfo,
|
|
212
|
+
});
|
|
213
|
+
console.error(`[MCP] ✅ Registered: ${slug}`);
|
|
214
|
+
registered.push({
|
|
215
|
+
name: app.name,
|
|
216
|
+
slug,
|
|
217
|
+
appStoreLocales: registeredAppStoreInfo.supportedLocales,
|
|
218
|
+
googlePlayLocales: googlePlayInfo?.supportedLocales,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
skipped.push(`${app.name} (${app.bundleId}) - registration failed`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const lines = [`📱 **App Setup Complete**\n`];
|
|
226
|
+
if (registered.length > 0) {
|
|
227
|
+
lines.push(`✅ **Registered** (${registered.length}):`);
|
|
228
|
+
for (const r of registered) {
|
|
229
|
+
const storeInfo = r.googlePlayLocales ? " (🍎+🤖)" : " (🍎)";
|
|
230
|
+
let localeInfo = "";
|
|
231
|
+
if (r.appStoreLocales && r.appStoreLocales.length > 0) {
|
|
232
|
+
localeInfo += `\n 🍎 App Store: ${r.appStoreLocales.join(", ")}`;
|
|
233
|
+
}
|
|
234
|
+
if (r.googlePlayLocales && r.googlePlayLocales.length > 0) {
|
|
235
|
+
localeInfo += `\n 🤖 Google Play: ${r.googlePlayLocales.join(", ")}`;
|
|
236
|
+
}
|
|
237
|
+
lines.push(` • ${r.name}${storeInfo} → slug: "${r.slug}"${localeInfo}`);
|
|
238
|
+
}
|
|
239
|
+
lines.push("");
|
|
240
|
+
}
|
|
241
|
+
if (skipped.length > 0) {
|
|
242
|
+
lines.push(`⏭️ **Skipped** (${skipped.length}):`);
|
|
243
|
+
for (const s of skipped) {
|
|
244
|
+
lines.push(` • ${s}`);
|
|
245
|
+
}
|
|
246
|
+
lines.push("");
|
|
247
|
+
}
|
|
248
|
+
if (playStoreEnabled) {
|
|
249
|
+
lines.push(`**Play Store Check Results:**`);
|
|
250
|
+
lines.push(` 🤖 Found: ${playStoreFound.length}`);
|
|
251
|
+
if (playStoreFound.length > 0) {
|
|
252
|
+
for (const name of playStoreFound) {
|
|
253
|
+
lines.push(` • ${name}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
lines.push(` ❌ Not found: ${playStoreNotFound.length}`);
|
|
257
|
+
if (playStoreNotFound.length > 0) {
|
|
258
|
+
for (const name of playStoreNotFound) {
|
|
259
|
+
lines.push(` • ${name}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
lines.push("");
|
|
263
|
+
}
|
|
264
|
+
lines.push('You can now reference apps in other tools using the `app: "slug"` parameter.');
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
267
|
+
_meta: {
|
|
268
|
+
registered: registered.length,
|
|
269
|
+
skipped: skipped.length,
|
|
270
|
+
playStoreFound: playStoreFound.length,
|
|
271
|
+
playStoreNotFound: playStoreNotFound.length,
|
|
272
|
+
apps,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
278
|
+
return {
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: "text",
|
|
282
|
+
text: `❌ Failed to query App Store apps: ${msg}`,
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (store === "googlePlay") {
|
|
289
|
+
console.error(`[MCP] 📋 Processing Google Play app: ${packageName || "N/A"}`);
|
|
290
|
+
if (!config.playStore) {
|
|
291
|
+
return {
|
|
292
|
+
content: [
|
|
293
|
+
{
|
|
294
|
+
type: "text",
|
|
295
|
+
text: "❌ Google Play authentication not configured. Please check ~/.config/pabal-mcp/config.json.",
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (!packageName) {
|
|
301
|
+
return {
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: "text",
|
|
305
|
+
text: `⚠️ Google Play API does not support listing apps.
|
|
306
|
+
|
|
307
|
+
Provide packageName to verify and register that app:
|
|
308
|
+
\`\`\`json
|
|
309
|
+
{ "store": "googlePlay", "packageName": "com.example.app" }
|
|
310
|
+
\`\`\``,
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
// Google Play 정보 가져오기 (언어 정보 포함)
|
|
317
|
+
console.error(`[MCP] 🔍 Fetching Google Play app info...`);
|
|
318
|
+
const googlePlayInfo = await googlePlayService.fetchAppInfo(packageName);
|
|
319
|
+
if (!googlePlayInfo.found) {
|
|
320
|
+
throw new Error("Failed to access Google Play app");
|
|
321
|
+
}
|
|
322
|
+
// Use only last part of packageName as slug (com.quartz.postblackbelt -> postblackbelt)
|
|
323
|
+
const parts = packageName.split(".");
|
|
324
|
+
const slug = parts[parts.length - 1].toLowerCase();
|
|
325
|
+
// Check if already registered (findApp searches by slug, bundleId, packageName)
|
|
326
|
+
const existing = findApp(packageName);
|
|
327
|
+
if (existing) {
|
|
328
|
+
return {
|
|
329
|
+
content: [
|
|
330
|
+
{
|
|
331
|
+
type: "text",
|
|
332
|
+
text: `⏭️ App is already registered: "${existing.slug}"`,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
_meta: { app: existing },
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// Register
|
|
339
|
+
const registeredGooglePlayInfo = toRegisteredGooglePlayInfo({
|
|
340
|
+
packageName,
|
|
341
|
+
appInfo: googlePlayInfo,
|
|
342
|
+
});
|
|
343
|
+
console.error(`[MCP] 💾 Registering app with slug: ${slug}`);
|
|
344
|
+
const newApp = registerApp({
|
|
345
|
+
slug,
|
|
346
|
+
name: googlePlayInfo.name || packageName,
|
|
347
|
+
googlePlay: registeredGooglePlayInfo,
|
|
348
|
+
});
|
|
349
|
+
console.error(`[MCP] ✅ App registered successfully`);
|
|
350
|
+
const localeInfo = registeredGooglePlayInfo?.supportedLocales &&
|
|
351
|
+
registeredGooglePlayInfo.supportedLocales.length > 0
|
|
352
|
+
? `\n• Supported Languages: ${registeredGooglePlayInfo.supportedLocales.join(", ")}`
|
|
353
|
+
: "";
|
|
354
|
+
return {
|
|
355
|
+
content: [
|
|
356
|
+
{
|
|
357
|
+
type: "text",
|
|
358
|
+
text: `✅ Google Play app registration complete
|
|
359
|
+
|
|
360
|
+
• Package Name: \`${packageName}\`
|
|
361
|
+
• Slug: \`${slug}\`
|
|
362
|
+
• Name: ${newApp.name}${localeInfo}
|
|
363
|
+
|
|
364
|
+
You can now reference this app in other tools using the \`app: "${slug}"\` parameter.`,
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
_meta: { app: newApp },
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
372
|
+
return {
|
|
373
|
+
content: [
|
|
374
|
+
{
|
|
375
|
+
type: "text",
|
|
376
|
+
text: `❌ Failed to access Google Play app: ${msg}`,
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
content: [
|
|
384
|
+
{
|
|
385
|
+
type: "text",
|
|
386
|
+
text: "❌ store parameter must be 'appStore', 'googlePlay', or 'both'.",
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
};
|
|
390
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* apps-search: Search registered apps
|
|
3
|
+
*
|
|
4
|
+
* Search apps from registered-apps.json.
|
|
5
|
+
* - Called without query: Returns all app list
|
|
6
|
+
* - Called with query: Search by slug, bundleId, packageName, name
|
|
7
|
+
*/
|
|
8
|
+
import { type RegisteredApp } from "../../packages/configs/secrets-config/registered-apps.js";
|
|
9
|
+
interface SearchAppsOptions {
|
|
10
|
+
/** Search term (slug, bundleId, packageName, name). Returns all apps if empty */
|
|
11
|
+
query?: string;
|
|
12
|
+
/** Store filter (default: all) */
|
|
13
|
+
store?: "all" | "appStore" | "googlePlay";
|
|
14
|
+
}
|
|
15
|
+
export declare function handleSearchApps(options: SearchAppsOptions): Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
_meta: {
|
|
21
|
+
apps: RegisteredApp[];
|
|
22
|
+
count: number;
|
|
23
|
+
};
|
|
24
|
+
isError?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
content: {
|
|
27
|
+
type: "text";
|
|
28
|
+
text: string;
|
|
29
|
+
}[];
|
|
30
|
+
isError: boolean;
|
|
31
|
+
_meta?: undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* apps-search: Search registered apps
|
|
3
|
+
*
|
|
4
|
+
* Search apps from registered-apps.json.
|
|
5
|
+
* - Called without query: Returns all app list
|
|
6
|
+
* - Called with query: Search by slug, bundleId, packageName, name
|
|
7
|
+
*/
|
|
8
|
+
import { loadRegisteredApps, findApp, } from "../../packages/configs/secrets-config/registered-apps.js";
|
|
9
|
+
/**
|
|
10
|
+
* Check if app matches query
|
|
11
|
+
*/
|
|
12
|
+
function matchesQuery(app, query) {
|
|
13
|
+
const lowerQuery = query.toLowerCase();
|
|
14
|
+
// slug match
|
|
15
|
+
if (app.slug.toLowerCase().includes(lowerQuery))
|
|
16
|
+
return true;
|
|
17
|
+
// name match
|
|
18
|
+
if (app.name.toLowerCase().includes(lowerQuery))
|
|
19
|
+
return true;
|
|
20
|
+
// App Store bundleId match
|
|
21
|
+
if (app.appStore?.bundleId?.toLowerCase().includes(lowerQuery))
|
|
22
|
+
return true;
|
|
23
|
+
// App Store name match
|
|
24
|
+
if (app.appStore?.name?.toLowerCase().includes(lowerQuery))
|
|
25
|
+
return true;
|
|
26
|
+
// Google Play packageName match
|
|
27
|
+
if (app.googlePlay?.packageName?.toLowerCase().includes(lowerQuery))
|
|
28
|
+
return true;
|
|
29
|
+
// Google Play name match
|
|
30
|
+
if (app.googlePlay?.name?.toLowerCase().includes(lowerQuery))
|
|
31
|
+
return true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Apply store filter
|
|
36
|
+
*/
|
|
37
|
+
function filterByStore(apps, store) {
|
|
38
|
+
if (store === "all")
|
|
39
|
+
return apps;
|
|
40
|
+
return apps.filter((app) => {
|
|
41
|
+
if (store === "appStore")
|
|
42
|
+
return !!app.appStore;
|
|
43
|
+
if (store === "googlePlay")
|
|
44
|
+
return !!app.googlePlay;
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format app information
|
|
50
|
+
*/
|
|
51
|
+
function formatAppInfo(app) {
|
|
52
|
+
const lines = [];
|
|
53
|
+
lines.push(`📱 **${app.name}** (\`${app.slug}\`)`);
|
|
54
|
+
if (app.appStore) {
|
|
55
|
+
lines.push(` 🍎 App Store: \`${app.appStore.bundleId}\``);
|
|
56
|
+
if (app.appStore.appId) {
|
|
57
|
+
lines.push(` App ID: ${app.appStore.appId}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (app.googlePlay) {
|
|
61
|
+
lines.push(` 🤖 Google Play: \`${app.googlePlay.packageName}\``);
|
|
62
|
+
}
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
export async function handleSearchApps(options) {
|
|
66
|
+
const { query, store = "all" } = options;
|
|
67
|
+
console.error(`[MCP] 🔍 Searching apps (query: ${query || "all"}, store: ${store})`);
|
|
68
|
+
try {
|
|
69
|
+
const config = loadRegisteredApps();
|
|
70
|
+
console.error(`[MCP] Loaded ${config.apps.length} apps from config`);
|
|
71
|
+
if (config.apps.length > 0) {
|
|
72
|
+
console.error(`[MCP] App slugs: ${config.apps.map((a) => a.slug).join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
let results;
|
|
75
|
+
if (!query) {
|
|
76
|
+
// If no query, return full list
|
|
77
|
+
results = config.apps;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Try exact match first (by slug, bundleId, packageName)
|
|
81
|
+
const exactMatch = findApp(query);
|
|
82
|
+
// Also search for partial matches
|
|
83
|
+
const partialMatches = config.apps.filter((app) => matchesQuery(app, query));
|
|
84
|
+
// Combine results: exact match first, then partial matches
|
|
85
|
+
// Remove duplicates by slug
|
|
86
|
+
const seenSlugs = new Set();
|
|
87
|
+
results = [];
|
|
88
|
+
// Add exact match first if found
|
|
89
|
+
if (exactMatch && !seenSlugs.has(exactMatch.slug)) {
|
|
90
|
+
results.push(exactMatch);
|
|
91
|
+
seenSlugs.add(exactMatch.slug);
|
|
92
|
+
}
|
|
93
|
+
// Add partial matches (excluding exact match if already added)
|
|
94
|
+
for (const app of partialMatches) {
|
|
95
|
+
if (!seenSlugs.has(app.slug)) {
|
|
96
|
+
results.push(app);
|
|
97
|
+
seenSlugs.add(app.slug);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Apply store filter
|
|
102
|
+
results = filterByStore(results, store);
|
|
103
|
+
if (results.length === 0) {
|
|
104
|
+
const message = query
|
|
105
|
+
? `No apps found matching "${query}".`
|
|
106
|
+
: "No apps registered.";
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: `❌ ${message}
|
|
112
|
+
|
|
113
|
+
💡 Register apps using apps-add or apps-init tools.`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
_meta: { apps: [], count: 0 },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const header = query
|
|
120
|
+
? `🔍 Search results for "${query}": ${results.length}`
|
|
121
|
+
: `📋 Registered app list: ${results.length}`;
|
|
122
|
+
const appList = results.map(formatAppInfo).join("\n\n");
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: `${header}
|
|
128
|
+
|
|
129
|
+
${appList}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
_meta: { apps: results, count: results.length },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: `❌ App search failed: ${message}`,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
isError: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { StoreType } from "../../packages/configs/aso-config/types.js";
|
|
2
|
+
interface AsoPullOptions {
|
|
3
|
+
app?: string;
|
|
4
|
+
packageName?: string;
|
|
5
|
+
bundleId?: string;
|
|
6
|
+
store?: StoreType;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function handleAsoPull(options: AsoPullOptions): Promise<{
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
isError?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
isError: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
export {};
|