pabal-web-mcp 1.0.0 → 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/dist/bin/mcp-server.js +390 -4
- package/dist/index.d.ts +144 -52
- package/package.json +3 -1
package/dist/bin/mcp-server.js
CHANGED
|
@@ -546,9 +546,10 @@ async function downloadScreenshotsToAsoDir(slug, asoData) {
|
|
|
546
546
|
"screenshots",
|
|
547
547
|
googlePlayLocale
|
|
548
548
|
);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
549
|
+
const phoneScreenshots = localeData.screenshots?.phone;
|
|
550
|
+
if (phoneScreenshots && phoneScreenshots.length > 0) {
|
|
551
|
+
for (let i = 0; i < phoneScreenshots.length; i++) {
|
|
552
|
+
const url = phoneScreenshots[i];
|
|
552
553
|
const filename = `phone-${i + 1}.png`;
|
|
553
554
|
const outputPath = path4.join(asoDir, filename);
|
|
554
555
|
if (isLocalAssetPath(url)) {
|
|
@@ -1889,6 +1890,382 @@ async function handleInitProject(input) {
|
|
|
1889
1890
|
};
|
|
1890
1891
|
}
|
|
1891
1892
|
|
|
1893
|
+
// src/tools/create-blog-html.ts
|
|
1894
|
+
import fs7 from "fs";
|
|
1895
|
+
import path8 from "path";
|
|
1896
|
+
import { z as z5 } from "zod";
|
|
1897
|
+
import { zodToJsonSchema as zodToJsonSchema5 } from "zod-to-json-schema";
|
|
1898
|
+
|
|
1899
|
+
// src/utils/blog.util.ts
|
|
1900
|
+
import path7 from "path";
|
|
1901
|
+
var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
1902
|
+
var BLOG_ROOT = "blogs";
|
|
1903
|
+
var removeDiacritics = (value) => value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
|
|
1904
|
+
var compact = (items) => (items || []).filter((item) => Boolean(item && item.trim()));
|
|
1905
|
+
function slugifyTitle(title) {
|
|
1906
|
+
const normalized = removeDiacritics(title).toLowerCase().replace(/[^a-z0-9\s-]/g, " ").replace(/[_\s]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
1907
|
+
return normalized || "post";
|
|
1908
|
+
}
|
|
1909
|
+
function normalizeDate(date) {
|
|
1910
|
+
if (date) {
|
|
1911
|
+
if (!DATE_REGEX.test(date)) {
|
|
1912
|
+
throw new Error(
|
|
1913
|
+
`Invalid date format "${date}". Use YYYY-MM-DD (e.g. 2024-09-30).`
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
return date;
|
|
1917
|
+
}
|
|
1918
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1919
|
+
}
|
|
1920
|
+
var isKoreanLocale = (locale) => locale.trim().toLowerCase().startsWith("ko");
|
|
1921
|
+
var toPublicBlogBase = (appSlug, slug) => `/${BLOG_ROOT}/${appSlug}/${slug}`;
|
|
1922
|
+
function resolveCoverImagePath(appSlug, slug, coverImage) {
|
|
1923
|
+
if (!coverImage || !coverImage.trim()) {
|
|
1924
|
+
return `/products/${appSlug}/og-image.png`;
|
|
1925
|
+
}
|
|
1926
|
+
const cleaned = coverImage.trim();
|
|
1927
|
+
const relativePath = cleaned.replace(/^\.\//, "");
|
|
1928
|
+
if (!cleaned.startsWith("/") && !/^https?:\/\//.test(cleaned)) {
|
|
1929
|
+
return `${toPublicBlogBase(appSlug, slug)}/${relativePath}`;
|
|
1930
|
+
}
|
|
1931
|
+
if (cleaned.startsWith("./")) {
|
|
1932
|
+
return `${toPublicBlogBase(appSlug, slug)}/${relativePath}`;
|
|
1933
|
+
}
|
|
1934
|
+
return cleaned;
|
|
1935
|
+
}
|
|
1936
|
+
function resolveRelativeImagePath(appSlug, slug, relativePath) {
|
|
1937
|
+
const raw = relativePath?.trim() || "./images/hero.png";
|
|
1938
|
+
const normalized = raw.replace(/^\.\//, "");
|
|
1939
|
+
return {
|
|
1940
|
+
raw,
|
|
1941
|
+
absolute: `${toPublicBlogBase(appSlug, slug)}/${normalized}`
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
function buildDescription(locale, topic, appSlug) {
|
|
1945
|
+
if (isKoreanLocale(locale)) {
|
|
1946
|
+
return `${topic}\uB97C \uC8FC\uC81C\uB85C ${appSlug}\uAC00 ASO\uC640 SEO\uB97C \uC5B4\uB5BB\uAC8C \uC5F0\uACB0\uD558\uACE0 \uBE14\uB85C\uADF8 \uD2B8\uB798\uD53D\uC744 \uC81C\uD488 \uD398\uC774\uC9C0\uB85C \uC774\uC5B4\uC8FC\uB294\uC9C0 \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.`;
|
|
1947
|
+
}
|
|
1948
|
+
return `How ${appSlug} teams turn "${topic}" into a bridge between ASO pages and SEO blogs without losing consistency.`;
|
|
1949
|
+
}
|
|
1950
|
+
function deriveTags(topic, appSlug) {
|
|
1951
|
+
const topicParts = topic.toLowerCase().split(/[^a-z0-9+]+/).filter(Boolean).slice(0, 6);
|
|
1952
|
+
const set = /* @__PURE__ */ new Set([...topicParts, appSlug.toLowerCase(), "blog"]);
|
|
1953
|
+
return Array.from(set);
|
|
1954
|
+
}
|
|
1955
|
+
function buildBlogMeta(options) {
|
|
1956
|
+
const publishedAt = normalizeDate(options.publishedAt);
|
|
1957
|
+
const modifiedAt = normalizeDate(options.modifiedAt || publishedAt);
|
|
1958
|
+
const coverImage = resolveCoverImagePath(
|
|
1959
|
+
options.appSlug,
|
|
1960
|
+
options.slug,
|
|
1961
|
+
options.coverImage
|
|
1962
|
+
);
|
|
1963
|
+
return {
|
|
1964
|
+
title: options.title,
|
|
1965
|
+
description: options.description || buildDescription(options.locale, options.topic, options.appSlug),
|
|
1966
|
+
appSlug: options.appSlug,
|
|
1967
|
+
slug: options.slug,
|
|
1968
|
+
locale: options.locale,
|
|
1969
|
+
publishedAt,
|
|
1970
|
+
modifiedAt,
|
|
1971
|
+
coverImage,
|
|
1972
|
+
tags: compact(options.tags)?.length ? Array.from(
|
|
1973
|
+
new Set(compact(options.tags).map((tag) => tag.toLowerCase()))
|
|
1974
|
+
) : deriveTags(options.topic, options.appSlug)
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
function renderBlogMetaBlock(meta) {
|
|
1978
|
+
const serialized = JSON.stringify(meta, null, 2);
|
|
1979
|
+
return `<!--BLOG_META
|
|
1980
|
+
${serialized}
|
|
1981
|
+
-->`;
|
|
1982
|
+
}
|
|
1983
|
+
function renderEnglishBody(args) {
|
|
1984
|
+
const { meta, topic, appSlug, includeRelativeImageExample, relativeImagePath } = args;
|
|
1985
|
+
const lines = [];
|
|
1986
|
+
lines.push(`<h1>${meta.title}</h1>`);
|
|
1987
|
+
lines.push(
|
|
1988
|
+
`<p>${appSlug} keeps product pages and blogs aligned. This article shows how to use "${topic}" as a shared story so ASO and SEO stay in sync.</p>`
|
|
1989
|
+
);
|
|
1990
|
+
if (includeRelativeImageExample && relativeImagePath) {
|
|
1991
|
+
lines.push(
|
|
1992
|
+
`<img src="${relativeImagePath.raw}" alt="${appSlug} ${topic} cover" />`
|
|
1993
|
+
);
|
|
1994
|
+
lines.push(
|
|
1995
|
+
`<p>The image above is stored next to this file and resolves to <code>${relativeImagePath.absolute}</code> when published.</p>`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
lines.push(`<h2>Why the gap appears</h2>`);
|
|
1999
|
+
lines.push(
|
|
2000
|
+
`<p>ASO pages are tuned for storefronts while SEO posts speak to search crawlers. Teams often duplicate work, drift on messaging, and miss internal links back to /products/${appSlug}.</p>`
|
|
2001
|
+
);
|
|
2002
|
+
lines.push(`<h3>Signals that drift</h3>`);
|
|
2003
|
+
lines.push(
|
|
2004
|
+
`<p>Different headlines, mismatched screenshots, and stale dates make ranking harder. "${topic}" is a strong bridge topic because it touches both acquisition paths.</p>`
|
|
2005
|
+
);
|
|
2006
|
+
lines.push(`<h2>How to bridge with ${appSlug}</h2>`);
|
|
2007
|
+
lines.push(
|
|
2008
|
+
`<p>Start with the product story, then reuse it in blog form. Keep the same core claim, swap storefront keywords for search intent, and reference the canonical product slug.</p>`
|
|
2009
|
+
);
|
|
2010
|
+
lines.push(`<h3>Mini playbook</h3>`);
|
|
2011
|
+
lines.push(
|
|
2012
|
+
`<ul>
|
|
2013
|
+
<li>Reuse the app store hero claim inside the intro.</li>
|
|
2014
|
+
<li>Map ASO keywords to SEO phrases for the "${topic}" angle.</li>
|
|
2015
|
+
<li>Link feature blurbs to product screenshots and changelog notes.</li>
|
|
2016
|
+
<li>Close with a CTA back to <code>/products/${appSlug}</code>.</li>
|
|
2017
|
+
</ul>`
|
|
2018
|
+
);
|
|
2019
|
+
lines.push(`<h2>Example flow to copy</h2>`);
|
|
2020
|
+
lines.push(
|
|
2021
|
+
`<p>Pick one release, outline how it helps with "${topic}", then add a short proof (metric, quote, or screenshot). Keep h2/h3 hierarchy stable so translations stay predictable.</p>`
|
|
2022
|
+
);
|
|
2023
|
+
lines.push(`<h2>Wrap up</h2>`);
|
|
2024
|
+
lines.push(
|
|
2025
|
+
`<p>${appSlug} keeps ASO and SEO talking to each other. Publish this HTML under <code>/blogs/${appSlug}/${meta.slug}/${meta.locale}.html</code> and link it from the product page so traffic flows both ways.</p>`
|
|
2026
|
+
);
|
|
2027
|
+
lines.push(
|
|
2028
|
+
`<p><strong>CTA:</strong> Explore the product page at <a href="/products/${appSlug}">/products/${appSlug}</a>.</p>`
|
|
2029
|
+
);
|
|
2030
|
+
return lines.join("\n\n");
|
|
2031
|
+
}
|
|
2032
|
+
function renderKoreanBody(args) {
|
|
2033
|
+
const { meta, topic, appSlug, includeRelativeImageExample, relativeImagePath } = args;
|
|
2034
|
+
const lines = [];
|
|
2035
|
+
lines.push(`<h1>${meta.title}</h1>`);
|
|
2036
|
+
lines.push(
|
|
2037
|
+
`<p>${appSlug}\uB294 \uC81C\uD488 \uD398\uC774\uC9C0\uC640 \uBE14\uB85C\uADF8\uC758 \uD750\uB984\uC774 \uB04A\uAE30\uC9C0 \uC54A\uB3C4\uB85D "${topic}"\uC744 \uAC19\uC740 \uC774\uC57C\uAE30\uB85C \uBB36\uC5B4\uB0C5\uB2C8\uB2E4. \uC774 \uAE00\uC740 ASO \uC2E0\uD638\uC640 SEO \uCF58\uD150\uCE20\uB97C \uD558\uB098\uC758 \uBA54\uC2DC\uC9C0\uB85C \uC5F0\uACB0\uD558\uB294 \uBC29\uBC95\uC744 \uC124\uBA85\uD569\uB2C8\uB2E4.</p>`
|
|
2038
|
+
);
|
|
2039
|
+
if (includeRelativeImageExample && relativeImagePath) {
|
|
2040
|
+
lines.push(
|
|
2041
|
+
`<img src="${relativeImagePath.raw}" alt="${appSlug} ${topic} \uD45C\uC9C0 \uC774\uBBF8\uC9C0" />`
|
|
2042
|
+
);
|
|
2043
|
+
lines.push(
|
|
2044
|
+
`<p>\uC704 \uC774\uBBF8\uC9C0\uB294 \uAE00\uACFC \uAC19\uC740 \uD3F4\uB354\uC5D0 \uC800\uC7A5\uB418\uC5B4 \uD37C\uBE14\uB9AC\uC2DC \uC2DC <code>${relativeImagePath.absolute}</code> \uACBD\uB85C\uB85C \uB178\uCD9C\uB429\uB2C8\uB2E4.</p>`
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
lines.push(`<h2>ASO\uC640 SEO\uAC00 \uAC08\uB77C\uC9C0\uB294 \uC9C0\uC810</h2>`);
|
|
2048
|
+
lines.push(
|
|
2049
|
+
`<p>\uC2A4\uD1A0\uC5B4 \uCD5C\uC801\uD654\uB294 \uC804\uD658\uC5D0 \uC9D1\uC911\uD558\uACE0, \uBE14\uB85C\uADF8\uB294 \uAC80\uC0C9 \uB178\uCD9C\uC744 \uB178\uB9BD\uB2C8\uB2E4. \uAC19\uC740 \uC81C\uD488\uC774\uB77C\uB3C4 \uC81C\uBAA9, \uC2A4\uD06C\uB9B0\uC0F7, \uB0A0\uC9DC\uAC00 \uB2EC\uB77C\uC9C0\uBA74 \uC2E0\uB8B0\uB3C4\uAC00 \uB5A8\uC5B4\uC9C0\uACE0 /products/${appSlug}\uB85C \uC774\uC5B4\uC9C0\uB294 \uB9C1\uD06C\uB3C4 \uC57D\uD574\uC9D1\uB2C8\uB2E4.</p>`
|
|
2050
|
+
);
|
|
2051
|
+
lines.push(`<h3>\uD754\uD788 \uB193\uCE58\uB294 \uC2E0\uD638</h3>`);
|
|
2052
|
+
lines.push(
|
|
2053
|
+
`<p>\uC2A4\uD1A0\uC5B4 \uBA54\uC2DC\uC9C0\uC640 \uBE14\uB85C\uADF8 \uCE74\uD53C\uAC00 \uB530\uB85C \uB180\uAC70\uB098, \uCD9C\uC2DC \uB9E5\uB77D\uC774 \uBE60\uC9C0\uB294 \uACBD\uC6B0\uC785\uB2C8\uB2E4. "${topic}" \uAC19\uC740 \uC8FC\uC81C\uB294 \uB450 \uCC44\uB110\uC744 \uBAA8\uB450 \uAC74\uB4DC\uB9AC\uAE30 \uB54C\uBB38\uC5D0 \uC77C\uAD00\uC131\uC774 \uB354 \uC911\uC694\uD569\uB2C8\uB2E4.</p>`
|
|
2054
|
+
);
|
|
2055
|
+
lines.push(`<h2>${appSlug}\uB85C \uB2E4\uB9AC \uB193\uAE30</h2>`);
|
|
2056
|
+
lines.push(
|
|
2057
|
+
`<p>\uC81C\uD488 \uC774\uC57C\uAE30\uB97C \uBA3C\uC800 \uC815\uB9AC\uD558\uACE0 \uBE14\uB85C\uADF8 \uBC84\uC804\uC73C\uB85C \uC7AC\uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uD575\uC2EC \uC8FC\uC7A5\uACFC \uC99D\uAC70\uB294 \uC720\uC9C0\uD558\uB418, \uAC80\uC0C9 \uC758\uB3C4\uC5D0 \uB9DE\uCDB0 \uD0A4\uC6CC\uB4DC\uC640 \uC608\uC2DC\uB97C \uC870\uC815\uD558\uACE0 \uC81C\uD488 \uC2AC\uB7EC\uADF8\uB97C \uD568\uAED8 \uB178\uCD9C\uD569\uB2C8\uB2E4.</p>`
|
|
2058
|
+
);
|
|
2059
|
+
lines.push(`<h3>\uC801\uC6A9 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8</h3>`);
|
|
2060
|
+
lines.push(
|
|
2061
|
+
`<ul>
|
|
2062
|
+
<li>\uC2A4\uD1A0\uC5B4 \uD5E4\uB4DC\uB77C\uC778\uC744 \uC778\uD2B8\uB85C\uC5D0 \uC7AC\uC0AC\uC6A9\uD558\uACE0 \uB3D9\uC77C\uD55C \uC8FC\uC7A5\uC73C\uB85C \uD480\uC5B4\uAC00\uAE30</li>
|
|
2063
|
+
<li>"${topic}"\uB97C \uC704\uD55C SEO \uD0A4\uC6CC\uB4DC\uB97C ASO \uD0A4\uC6CC\uB4DC\uC640 \uB9E4\uD551\uD558\uAE30</li>
|
|
2064
|
+
<li>\uC2E0\uAE30\uB2A5/\uC2A4\uD06C\uB9B0\uC0F7/\uBCC0\uACBD \uC0AC\uD56D\uC744 \uBE14\uB85C\uADF8 \uBCF8\uBB38\uC5D0 \uC9E7\uAC8C \uC5F0\uACB0\uD558\uAE30</li>
|
|
2065
|
+
<li>\uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0\uC11C <code>/products/${appSlug}</code>\uB85C \uC790\uC5F0\uC2A4\uB7EC\uC6B4 CTA \uBC30\uCE58</li>
|
|
2066
|
+
</ul>`
|
|
2067
|
+
);
|
|
2068
|
+
lines.push(`<h2>\uC0AC\uB840 \uD750\uB984 \uC608\uC2DC</h2>`);
|
|
2069
|
+
lines.push(
|
|
2070
|
+
`<p>\uCD5C\uADFC \uB9B4\uB9AC\uC2A4\uB97C \uD558\uB098 \uACE8\uB77C "${topic}"\uACFC \uC5B4\uB5BB\uAC8C \uB9DE\uBB3C\uB9AC\uB294\uC9C0 \uC124\uBA85\uD558\uACE0, \uC22B\uC790\xB7\uC778\uC6A9\xB7\uC2A4\uD06C\uB9B0\uC0F7 \uC911 \uD558\uB098\uB85C \uC99D\uAC70\uB97C \uB0A8\uAE30\uC138\uC694. h2/h3 \uAD6C\uC870\uB97C \uACE0\uC815\uD558\uBA74 \uB2E4\uAD6D\uC5B4 \uD655\uC7A5\uB3C4 \uC218\uC6D4\uD574\uC9D1\uB2C8\uB2E4.</p>`
|
|
2071
|
+
);
|
|
2072
|
+
lines.push(`<h2>\uB9C8\uBB34\uB9AC</h2>`);
|
|
2073
|
+
lines.push(
|
|
2074
|
+
`<p>${appSlug}\uB294 \uBE14\uB85C\uADF8\uC640 \uC81C\uD488 \uD398\uC774\uC9C0\uAC00 \uC11C\uB85C \uD2B8\uB798\uD53D\uC744 \uC8FC\uACE0\uBC1B\uB3C4\uB85D \uC124\uACC4\uD588\uC2B5\uB2C8\uB2E4. \uC774 HTML\uC744 <code>/blogs/${appSlug}/${meta.slug}/${meta.locale}.html</code> \uC704\uCE58\uC5D0 \uC800\uC7A5\uD558\uACE0 \uC81C\uD488 \uC0C1\uC138\uC5D0\uC11C \uB9C1\uD06C\uB97C \uAC78\uC5B4\uB450\uC138\uC694.</p>`
|
|
2075
|
+
);
|
|
2076
|
+
lines.push(
|
|
2077
|
+
`<p><strong>CTA:</strong> \uC81C\uD488 \uD398\uC774\uC9C0 <a href="/products/${appSlug}">/products/${appSlug}</a>\uC5D0\uC11C \uB354 \uC790\uC138\uD788 \uC0B4\uD3B4\uBCF4\uC138\uC694.</p>`
|
|
2078
|
+
);
|
|
2079
|
+
return lines.join("\n\n");
|
|
2080
|
+
}
|
|
2081
|
+
function renderBlogBody(options) {
|
|
2082
|
+
if (isKoreanLocale(options.meta.locale)) {
|
|
2083
|
+
return renderKoreanBody(options);
|
|
2084
|
+
}
|
|
2085
|
+
return renderEnglishBody(options);
|
|
2086
|
+
}
|
|
2087
|
+
function buildBlogHtmlDocument(options) {
|
|
2088
|
+
const metaBlock = renderBlogMetaBlock(options.meta);
|
|
2089
|
+
const body = renderBlogBody({
|
|
2090
|
+
meta: options.meta,
|
|
2091
|
+
topic: options.topic,
|
|
2092
|
+
appSlug: options.appSlug,
|
|
2093
|
+
includeRelativeImageExample: options.includeRelativeImageExample,
|
|
2094
|
+
relativeImagePath: options.relativeImagePath
|
|
2095
|
+
});
|
|
2096
|
+
return `${metaBlock}
|
|
2097
|
+
${body}`;
|
|
2098
|
+
}
|
|
2099
|
+
function resolveTargetLocales(input, defaultLocale = "en-US") {
|
|
2100
|
+
if (input.locales?.length) {
|
|
2101
|
+
const locales = input.locales.map((loc) => loc.trim()).filter(Boolean);
|
|
2102
|
+
return Array.from(new Set(locales));
|
|
2103
|
+
}
|
|
2104
|
+
const fallback = input.locale?.trim() || defaultLocale;
|
|
2105
|
+
return fallback ? [fallback] : [];
|
|
2106
|
+
}
|
|
2107
|
+
function getBlogOutputPaths(options) {
|
|
2108
|
+
const baseDir = path7.join(
|
|
2109
|
+
options.publicDir,
|
|
2110
|
+
BLOG_ROOT,
|
|
2111
|
+
options.appSlug,
|
|
2112
|
+
options.slug
|
|
2113
|
+
);
|
|
2114
|
+
const filePath = path7.join(baseDir, `${options.locale}.html`);
|
|
2115
|
+
const publicBasePath = toPublicBlogBase(options.appSlug, options.slug);
|
|
2116
|
+
return { baseDir, filePath, publicBasePath };
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/tools/create-blog-html.ts
|
|
2120
|
+
var toJsonSchema4 = zodToJsonSchema5;
|
|
2121
|
+
var DATE_REGEX2 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2122
|
+
var createBlogHtmlInputSchema = z5.object({
|
|
2123
|
+
appSlug: z5.string().trim().min(1, "appSlug is required").describe("Product/app slug used for paths and CTAs"),
|
|
2124
|
+
title: z5.string().trim().optional().describe(
|
|
2125
|
+
"English title used for slug (kebab-case). Falls back to topic when omitted."
|
|
2126
|
+
),
|
|
2127
|
+
topic: z5.string().trim().min(1, "topic is required").describe("Topic/angle to write about in the blog body"),
|
|
2128
|
+
locale: z5.string().trim().optional().default("en-US").describe("Primary locale (default en-US). Ignored when locales[] is set."),
|
|
2129
|
+
locales: z5.array(z5.string().trim().min(1)).optional().describe(
|
|
2130
|
+
"Optional list of locales to generate. Each locale gets its own HTML file."
|
|
2131
|
+
),
|
|
2132
|
+
description: z5.string().trim().optional().describe(
|
|
2133
|
+
"Meta description override. If omitted, the tool generates one from appSlug/topic per locale."
|
|
2134
|
+
),
|
|
2135
|
+
tags: z5.array(z5.string().trim().min(1)).optional().describe("Optional tags for BLOG_META. Defaults to tags derived from topic."),
|
|
2136
|
+
coverImage: z5.string().trim().optional().describe(
|
|
2137
|
+
"Cover image path. Relative paths rewrite to /blogs/<app>/<slug>/..., default is /products/<appSlug>/og-image.png."
|
|
2138
|
+
),
|
|
2139
|
+
includeRelativeImageExample: z5.boolean().optional().default(false).describe(
|
|
2140
|
+
"Inject a relative image example (./images/hero.png) into the body to demonstrate path rewriting."
|
|
2141
|
+
),
|
|
2142
|
+
relativeImagePath: z5.string().trim().optional().describe("Override the relative image path (default ./images/hero.png)."),
|
|
2143
|
+
publishedAt: z5.string().trim().regex(DATE_REGEX2, "publishedAt must use YYYY-MM-DD").optional().describe("Publish date (YYYY-MM-DD). Defaults to today."),
|
|
2144
|
+
modifiedAt: z5.string().trim().regex(DATE_REGEX2, "modifiedAt must use YYYY-MM-DD").optional().describe("Last modified date (YYYY-MM-DD). Defaults to publishedAt."),
|
|
2145
|
+
overwrite: z5.boolean().optional().default(false).describe("Overwrite existing files when true (default: false).")
|
|
2146
|
+
}).describe("Generate static HTML blog posts with BLOG_META headers.");
|
|
2147
|
+
var jsonSchema5 = toJsonSchema4(createBlogHtmlInputSchema, {
|
|
2148
|
+
name: "CreateBlogHtmlInput",
|
|
2149
|
+
target: "openApi3",
|
|
2150
|
+
$refStrategy: "none"
|
|
2151
|
+
});
|
|
2152
|
+
var inputSchema5 = jsonSchema5.definitions?.CreateBlogHtmlInput || jsonSchema5;
|
|
2153
|
+
var createBlogHtmlTool = {
|
|
2154
|
+
name: "create-blog-html",
|
|
2155
|
+
description: `Generate HTML blog posts under public/blogs/<appSlug>/<slug>/<locale>.html with a BLOG_META block.
|
|
2156
|
+
|
|
2157
|
+
Slug rules:
|
|
2158
|
+
- slug = slugify(English title, kebab-case ASCII)
|
|
2159
|
+
- path: public/blogs/<appSlug>/<slug>/<locale>.html
|
|
2160
|
+
- coverImage default: /products/<appSlug>/og-image.png (relative paths are rewritten under /blogs/<app>/<slug>/)
|
|
2161
|
+
- overwrite defaults to false (throws when file exists)
|
|
2162
|
+
|
|
2163
|
+
Template:
|
|
2164
|
+
- Intro connecting topic/app
|
|
2165
|
+
- 3-4 sections (problem \u2192 solution \u2192 tips/examples) using h2/h3
|
|
2166
|
+
- Optional relative image example (./images/hero.png)
|
|
2167
|
+
- Conclusion + CTA linking to /products/<appSlug>
|
|
2168
|
+
|
|
2169
|
+
Supports multiple locales when locales[] is provided (default single locale). Content language follows locale (ko -> Korean, otherwise English).`,
|
|
2170
|
+
inputSchema: inputSchema5
|
|
2171
|
+
};
|
|
2172
|
+
async function handleCreateBlogHtml(input) {
|
|
2173
|
+
const publicDir = getPublicDir();
|
|
2174
|
+
const {
|
|
2175
|
+
appSlug,
|
|
2176
|
+
topic,
|
|
2177
|
+
title,
|
|
2178
|
+
description,
|
|
2179
|
+
tags,
|
|
2180
|
+
coverImage,
|
|
2181
|
+
includeRelativeImageExample = false,
|
|
2182
|
+
relativeImagePath,
|
|
2183
|
+
publishedAt,
|
|
2184
|
+
modifiedAt,
|
|
2185
|
+
overwrite = false
|
|
2186
|
+
} = input;
|
|
2187
|
+
const resolvedTitle = title && title.trim() || topic.trim();
|
|
2188
|
+
const slug = slugifyTitle(resolvedTitle);
|
|
2189
|
+
const targetLocales = resolveTargetLocales(input);
|
|
2190
|
+
if (!targetLocales.length) {
|
|
2191
|
+
throw new Error("At least one locale is required to generate blog HTML.");
|
|
2192
|
+
}
|
|
2193
|
+
const shouldIncludeRelativeImage = includeRelativeImageExample || Boolean(relativeImagePath);
|
|
2194
|
+
const relativeImage = shouldIncludeRelativeImage ? resolveRelativeImagePath(appSlug, slug, relativeImagePath) : void 0;
|
|
2195
|
+
const output = {
|
|
2196
|
+
slug,
|
|
2197
|
+
baseDir: path8.join(publicDir, "blogs", appSlug, slug),
|
|
2198
|
+
files: [],
|
|
2199
|
+
coverImage: coverImage && coverImage.trim().length > 0 ? coverImage.trim() : `/products/${appSlug}/og-image.png`,
|
|
2200
|
+
metaByLocale: {}
|
|
2201
|
+
};
|
|
2202
|
+
const plannedFiles = targetLocales.map(
|
|
2203
|
+
(locale) => getBlogOutputPaths({
|
|
2204
|
+
appSlug,
|
|
2205
|
+
slug,
|
|
2206
|
+
locale,
|
|
2207
|
+
publicDir
|
|
2208
|
+
})
|
|
2209
|
+
);
|
|
2210
|
+
const existing = plannedFiles.filter(({ filePath }) => fs7.existsSync(filePath));
|
|
2211
|
+
if (existing.length > 0 && !overwrite) {
|
|
2212
|
+
const existingList = existing.map((f) => f.filePath).join("\n- ");
|
|
2213
|
+
throw new Error(
|
|
2214
|
+
`Blog HTML already exists. Set overwrite=true to replace:
|
|
2215
|
+
- ${existingList}`
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
fs7.mkdirSync(output.baseDir, { recursive: true });
|
|
2219
|
+
for (const locale of targetLocales) {
|
|
2220
|
+
const { filePath } = getBlogOutputPaths({
|
|
2221
|
+
appSlug,
|
|
2222
|
+
slug,
|
|
2223
|
+
locale,
|
|
2224
|
+
publicDir
|
|
2225
|
+
});
|
|
2226
|
+
const meta = buildBlogMeta({
|
|
2227
|
+
title: resolvedTitle,
|
|
2228
|
+
description,
|
|
2229
|
+
appSlug,
|
|
2230
|
+
slug,
|
|
2231
|
+
locale,
|
|
2232
|
+
topic,
|
|
2233
|
+
coverImage,
|
|
2234
|
+
tags,
|
|
2235
|
+
publishedAt,
|
|
2236
|
+
modifiedAt
|
|
2237
|
+
});
|
|
2238
|
+
output.coverImage = meta.coverImage;
|
|
2239
|
+
output.metaByLocale[locale] = meta;
|
|
2240
|
+
const html = buildBlogHtmlDocument({
|
|
2241
|
+
meta,
|
|
2242
|
+
topic,
|
|
2243
|
+
appSlug,
|
|
2244
|
+
includeRelativeImageExample: shouldIncludeRelativeImage,
|
|
2245
|
+
relativeImagePath: relativeImage
|
|
2246
|
+
});
|
|
2247
|
+
fs7.writeFileSync(filePath, html, "utf-8");
|
|
2248
|
+
output.files.push({ locale, path: filePath });
|
|
2249
|
+
}
|
|
2250
|
+
const summaryLines = [
|
|
2251
|
+
`Created blog HTML for ${appSlug}`,
|
|
2252
|
+
`Slug: ${slug}`,
|
|
2253
|
+
`Locales: ${targetLocales.join(", ")}`,
|
|
2254
|
+
`Cover image: ${output.coverImage}`,
|
|
2255
|
+
"",
|
|
2256
|
+
"Files:",
|
|
2257
|
+
...output.files.map((file) => `- ${file.locale}: ${file.path}`)
|
|
2258
|
+
];
|
|
2259
|
+
return {
|
|
2260
|
+
content: [
|
|
2261
|
+
{
|
|
2262
|
+
type: "text",
|
|
2263
|
+
text: summaryLines.join("\n")
|
|
2264
|
+
}
|
|
2265
|
+
]
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
|
|
1892
2269
|
// src/tools/index.ts
|
|
1893
2270
|
var tools = [
|
|
1894
2271
|
{
|
|
@@ -1922,6 +2299,14 @@ var tools = [
|
|
|
1922
2299
|
zodSchema: initProjectInputSchema,
|
|
1923
2300
|
handler: handleInitProject,
|
|
1924
2301
|
category: "Setup"
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
name: createBlogHtmlTool.name,
|
|
2305
|
+
description: createBlogHtmlTool.description,
|
|
2306
|
+
inputSchema: createBlogHtmlTool.inputSchema,
|
|
2307
|
+
zodSchema: createBlogHtmlInputSchema,
|
|
2308
|
+
handler: handleCreateBlogHtml,
|
|
2309
|
+
category: "Content"
|
|
1925
2310
|
}
|
|
1926
2311
|
];
|
|
1927
2312
|
function getToolDefinitions() {
|
|
@@ -1929,7 +2314,8 @@ function getToolDefinitions() {
|
|
|
1929
2314
|
asoToPublicTool,
|
|
1930
2315
|
publicToAsoTool,
|
|
1931
2316
|
improvePublicTool,
|
|
1932
|
-
initProjectTool
|
|
2317
|
+
initProjectTool,
|
|
2318
|
+
createBlogHtmlTool
|
|
1933
2319
|
];
|
|
1934
2320
|
}
|
|
1935
2321
|
function getToolHandler(name) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { androidpublisher_v3 } from '@googleapis/androidpublisher';
|
|
2
|
+
import { AppStoreVersionAttributes, AppStoreVersionLocalizationAttributes, AppInfoLocalizationAttributes, ScreenshotDisplayType } from 'appstore-connect-sdk/openapi';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Unified locale system for ASO (App Store Optimization)
|
|
3
6
|
* Consolidates App Store Connect and Google Play Console locale codes
|
|
@@ -70,21 +73,32 @@ declare function isAppStoreLocale(locale: string): locale is AppStoreLocale;
|
|
|
70
73
|
* Check if locale is supported by Google Play
|
|
71
74
|
*/
|
|
72
75
|
declare function isGooglePlayLocale(locale: string): locale is GooglePlayLocale;
|
|
76
|
+
/**
|
|
77
|
+
* Google Play Android Publisher base types
|
|
78
|
+
*/
|
|
79
|
+
type GooglePlayListing = androidpublisher_v3.Schema$Listing;
|
|
80
|
+
type GooglePlayImageType = NonNullable<androidpublisher_v3.Params$Resource$Edits$Images$List["imageType"]>;
|
|
81
|
+
type GooglePlayScreenshotType = Extract<GooglePlayImageType, "phoneScreenshots" | "sevenInchScreenshots" | "tenInchScreenshots" | "tvScreenshots" | "wearScreenshots">;
|
|
82
|
+
/**
|
|
83
|
+
* Google Play screenshots keyed by Android Publisher imageType
|
|
84
|
+
* Includes legacy aliases for existing data structures
|
|
85
|
+
*/
|
|
86
|
+
interface GooglePlayScreenshots extends Partial<Record<GooglePlayScreenshotType, string[]>> {
|
|
87
|
+
phone?: string[];
|
|
88
|
+
tablet?: string[];
|
|
89
|
+
tablet7?: string[];
|
|
90
|
+
tablet10?: string[];
|
|
91
|
+
tv?: string[];
|
|
92
|
+
wear?: string[];
|
|
93
|
+
}
|
|
73
94
|
/**
|
|
74
95
|
* Google Play Store ASO data
|
|
75
96
|
*/
|
|
76
|
-
interface GooglePlayAsoData {
|
|
77
|
-
title:
|
|
78
|
-
shortDescription:
|
|
79
|
-
fullDescription:
|
|
80
|
-
screenshots:
|
|
81
|
-
phone: string[];
|
|
82
|
-
tablet?: string[];
|
|
83
|
-
tablet7?: string[];
|
|
84
|
-
tablet10?: string[];
|
|
85
|
-
tv?: string[];
|
|
86
|
-
wear?: string[];
|
|
87
|
-
};
|
|
97
|
+
interface GooglePlayAsoData extends Pick<GooglePlayListing, "video" | "language"> {
|
|
98
|
+
title: NonNullable<GooglePlayListing["title"]>;
|
|
99
|
+
shortDescription: NonNullable<GooglePlayListing["shortDescription"]>;
|
|
100
|
+
fullDescription: NonNullable<GooglePlayListing["fullDescription"]>;
|
|
101
|
+
screenshots: GooglePlayScreenshots;
|
|
88
102
|
featureGraphic?: string;
|
|
89
103
|
promoGraphic?: string;
|
|
90
104
|
category?: string;
|
|
@@ -99,59 +113,57 @@ interface GooglePlayAsoData {
|
|
|
99
113
|
/**
|
|
100
114
|
* Google Play release notes (per version)
|
|
101
115
|
*/
|
|
102
|
-
|
|
103
|
-
versionCode: number;
|
|
104
|
-
versionName: string;
|
|
105
|
-
releaseNotes: {
|
|
106
|
-
[language: string]: string;
|
|
107
|
-
};
|
|
108
|
-
track: string;
|
|
109
|
-
status: string;
|
|
110
|
-
releaseDate?: string;
|
|
111
|
-
}
|
|
116
|
+
type GooglePlayReleaseNote = androidpublisher_v3.Schema$TrackRelease;
|
|
112
117
|
/**
|
|
113
118
|
* App Store release notes (per version)
|
|
114
119
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
releaseNotes: {
|
|
118
|
-
[locale: string]: string;
|
|
119
|
-
};
|
|
120
|
-
platform: string;
|
|
120
|
+
type AppStoreReleaseNote = Pick<AppStoreVersionAttributes, "versionString" | "platform"> & {
|
|
121
|
+
releaseNotes: Record<string, NonNullable<AppStoreVersionLocalizationAttributes["whatsNew"]>>;
|
|
121
122
|
releaseDate?: string;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* App Store Connect base types
|
|
126
|
+
*/
|
|
127
|
+
type AppStoreInfoLocalization = AppInfoLocalizationAttributes;
|
|
128
|
+
type AppStoreVersionLocalization = AppStoreVersionLocalizationAttributes;
|
|
129
|
+
type AppStoreScreenshotDisplayType = ScreenshotDisplayType;
|
|
130
|
+
/**
|
|
131
|
+
* App Store screenshots keyed by official display type
|
|
132
|
+
* Includes legacy aliases used in existing data structures
|
|
133
|
+
*/
|
|
134
|
+
interface AppStoreScreenshots extends Partial<Record<AppStoreScreenshotDisplayType, string[]>> {
|
|
135
|
+
iphone65?: string[];
|
|
136
|
+
iphone61?: string[];
|
|
137
|
+
iphone58?: string[];
|
|
138
|
+
iphone55?: string[];
|
|
139
|
+
iphone47?: string[];
|
|
140
|
+
iphone40?: string[];
|
|
141
|
+
ipadPro129?: string[];
|
|
142
|
+
ipadPro11?: string[];
|
|
143
|
+
ipad105?: string[];
|
|
144
|
+
ipad97?: string[];
|
|
145
|
+
appleWatch?: string[];
|
|
122
146
|
}
|
|
123
147
|
/**
|
|
124
148
|
* App Store ASO data
|
|
125
149
|
*/
|
|
126
150
|
interface AppStoreAsoData {
|
|
127
|
-
name:
|
|
128
|
-
subtitle?:
|
|
129
|
-
description:
|
|
130
|
-
keywords?:
|
|
131
|
-
promotionalText?:
|
|
132
|
-
screenshots:
|
|
133
|
-
iphone65?: string[];
|
|
134
|
-
iphone61?: string[];
|
|
135
|
-
iphone58?: string[];
|
|
136
|
-
iphone55?: string[];
|
|
137
|
-
iphone47?: string[];
|
|
138
|
-
iphone40?: string[];
|
|
139
|
-
ipadPro129?: string[];
|
|
140
|
-
ipadPro11?: string[];
|
|
141
|
-
ipad105?: string[];
|
|
142
|
-
ipad97?: string[];
|
|
143
|
-
appleWatch?: string[];
|
|
144
|
-
};
|
|
151
|
+
name: NonNullable<AppStoreInfoLocalization["name"]>;
|
|
152
|
+
subtitle?: AppStoreInfoLocalization["subtitle"];
|
|
153
|
+
description: NonNullable<AppStoreVersionLocalization["description"]>;
|
|
154
|
+
keywords?: AppStoreVersionLocalization["keywords"];
|
|
155
|
+
promotionalText?: AppStoreVersionLocalization["promotionalText"];
|
|
156
|
+
screenshots: AppStoreScreenshots;
|
|
145
157
|
appPreview?: string[];
|
|
146
158
|
primaryCategory?: string;
|
|
147
159
|
secondaryCategory?: string;
|
|
148
160
|
contentRightId?: string;
|
|
149
|
-
supportUrl?:
|
|
150
|
-
marketingUrl?:
|
|
151
|
-
privacyPolicyUrl?:
|
|
161
|
+
supportUrl?: AppStoreVersionLocalization["supportUrl"];
|
|
162
|
+
marketingUrl?: AppStoreVersionLocalization["marketingUrl"];
|
|
163
|
+
privacyPolicyUrl?: AppStoreInfoLocalization["privacyPolicyUrl"];
|
|
152
164
|
bundleId: string;
|
|
153
|
-
locale:
|
|
154
|
-
whatsNew?:
|
|
165
|
+
locale: NonNullable<AppStoreVersionLocalization["locale"]>;
|
|
166
|
+
whatsNew?: AppStoreVersionLocalization["whatsNew"];
|
|
155
167
|
}
|
|
156
168
|
/**
|
|
157
169
|
* Multilingual Google Play ASO data
|
|
@@ -354,6 +366,86 @@ interface ProductLocale {
|
|
|
354
366
|
landing?: LandingPageLocale;
|
|
355
367
|
}
|
|
356
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Types for the create-blog-html MCP tool
|
|
371
|
+
*/
|
|
372
|
+
interface BlogMeta {
|
|
373
|
+
title: string;
|
|
374
|
+
description: string;
|
|
375
|
+
appSlug: string;
|
|
376
|
+
slug: string;
|
|
377
|
+
locale: string;
|
|
378
|
+
publishedAt: string;
|
|
379
|
+
modifiedAt: string;
|
|
380
|
+
coverImage: string;
|
|
381
|
+
tags: string[];
|
|
382
|
+
}
|
|
383
|
+
interface CreateBlogHtmlInput {
|
|
384
|
+
/**
|
|
385
|
+
* Product/app slug used for paths and CTAs
|
|
386
|
+
*/
|
|
387
|
+
appSlug: string;
|
|
388
|
+
/**
|
|
389
|
+
* English title used for slug creation and H1
|
|
390
|
+
*/
|
|
391
|
+
title?: string;
|
|
392
|
+
/**
|
|
393
|
+
* Topic/angle to cover inside the article body
|
|
394
|
+
*/
|
|
395
|
+
topic: string;
|
|
396
|
+
/**
|
|
397
|
+
* Single locale to generate (default en-US). Ignored when locales[] is provided.
|
|
398
|
+
*/
|
|
399
|
+
locale?: string;
|
|
400
|
+
/**
|
|
401
|
+
* Optional list of locales to generate. Each gets its own HTML file.
|
|
402
|
+
*/
|
|
403
|
+
locales?: string[];
|
|
404
|
+
/**
|
|
405
|
+
* Meta description override. If absent, a locale-aware summary is generated from topic/appSlug.
|
|
406
|
+
*/
|
|
407
|
+
description?: string;
|
|
408
|
+
/**
|
|
409
|
+
* Optional tags for BLOG_META. If absent, tags are derived from topic and appSlug.
|
|
410
|
+
*/
|
|
411
|
+
tags?: string[];
|
|
412
|
+
/**
|
|
413
|
+
* Optional cover image. Relative paths are rewritten to /blogs/<app>/<slug>/...
|
|
414
|
+
*/
|
|
415
|
+
coverImage?: string;
|
|
416
|
+
/**
|
|
417
|
+
* Include a relative image example in the body (./images/hero.png by default).
|
|
418
|
+
*/
|
|
419
|
+
includeRelativeImageExample?: boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Override the relative image path used when includeRelativeImageExample is true.
|
|
422
|
+
*/
|
|
423
|
+
relativeImagePath?: string;
|
|
424
|
+
/**
|
|
425
|
+
* Publish date (YYYY-MM-DD). Defaults to today.
|
|
426
|
+
*/
|
|
427
|
+
publishedAt?: string;
|
|
428
|
+
/**
|
|
429
|
+
* Last modified date (YYYY-MM-DD). Defaults to publishedAt.
|
|
430
|
+
*/
|
|
431
|
+
modifiedAt?: string;
|
|
432
|
+
/**
|
|
433
|
+
* Overwrite existing HTML files when true (default false).
|
|
434
|
+
*/
|
|
435
|
+
overwrite?: boolean;
|
|
436
|
+
}
|
|
437
|
+
interface GeneratedBlogFile {
|
|
438
|
+
locale: string;
|
|
439
|
+
path: string;
|
|
440
|
+
}
|
|
441
|
+
interface CreateBlogHtmlResult {
|
|
442
|
+
slug: string;
|
|
443
|
+
baseDir: string;
|
|
444
|
+
files: GeneratedBlogFile[];
|
|
445
|
+
coverImage: string;
|
|
446
|
+
metaByLocale: Record<string, BlogMeta>;
|
|
447
|
+
}
|
|
448
|
+
|
|
357
449
|
/**
|
|
358
450
|
* Locale conversion utilities for ASO platforms
|
|
359
451
|
* Handles conversion between unified locales and platform-specific locale codes
|
|
@@ -609,4 +701,4 @@ declare function getPublicDir(): string;
|
|
|
609
701
|
*/
|
|
610
702
|
declare function getProductsDir(): string;
|
|
611
703
|
|
|
612
|
-
export { APP_STORE_TO_UNIFIED, type AppMetaLinks, type AppPageData, type AppStoreAsoData, type AppStoreLocale, type AppStoreMultilingualAsoData, type AppStoreReleaseNote, type AsoData, type AsoLocaleContent, type AsoTemplate, DEFAULT_LOCALE, type DeepPartial, type FeatureItem, GOOGLE_PLAY_TO_UNIFIED, type GooglePlayAsoData, type GooglePlayLocale, type GooglePlayMultilingualAsoData, type GooglePlayReleaseNote, type ImageAsset, type LandingCta, type LandingFeatures, type LandingHero, type LandingPage, type LandingPageLocale, type LandingReviews, type LandingScreenshots, type LayoutColors, type ProductConfig, type ProductContent, type ProductLocale, type ProductMetadata, type ProductScreenshots, type SupportedLocale, type Testimonial, UNIFIED_LOCALES, UNIFIED_TO_APP_STORE, UNIFIED_TO_GOOGLE_PLAY, type UnifiedLocale, appStoreToGooglePlay, appStoreToUnified, appStoreToUnifiedBatch, convertObjectFromAppStore, convertObjectFromGooglePlay, convertObjectToAppStore, convertObjectToGooglePlay, getAsoDataDir, getProductsDir, getPublicDir, getPullDataDir, getPushDataDir, googlePlayToAppStore, googlePlayToUnified, googlePlayToUnifiedBatch, isAppStoreLocale, isAppStoreMultilingual, isGooglePlayLocale, isGooglePlayMultilingual, isSupportedLocale, loadAsoFromConfig, saveAsoToAsoDir, saveAsoToConfig, unifiedToAppStore, unifiedToAppStoreBatch, unifiedToBothPlatforms, unifiedToGooglePlay, unifiedToGooglePlayBatch };
|
|
704
|
+
export { APP_STORE_TO_UNIFIED, type AppMetaLinks, type AppPageData, type AppStoreAsoData, type AppStoreInfoLocalization, type AppStoreLocale, type AppStoreMultilingualAsoData, type AppStoreReleaseNote, type AppStoreScreenshotDisplayType, type AppStoreScreenshots, type AppStoreVersionLocalization, type AsoData, type AsoLocaleContent, type AsoTemplate, type BlogMeta, type CreateBlogHtmlInput, type CreateBlogHtmlResult, DEFAULT_LOCALE, type DeepPartial, type FeatureItem, GOOGLE_PLAY_TO_UNIFIED, type GeneratedBlogFile, type GooglePlayAsoData, type GooglePlayImageType, type GooglePlayListing, type GooglePlayLocale, type GooglePlayMultilingualAsoData, type GooglePlayReleaseNote, type GooglePlayScreenshotType, type GooglePlayScreenshots, type ImageAsset, type LandingCta, type LandingFeatures, type LandingHero, type LandingPage, type LandingPageLocale, type LandingReviews, type LandingScreenshots, type LayoutColors, type ProductConfig, type ProductContent, type ProductLocale, type ProductMetadata, type ProductScreenshots, type SupportedLocale, type Testimonial, UNIFIED_LOCALES, UNIFIED_TO_APP_STORE, UNIFIED_TO_GOOGLE_PLAY, type UnifiedLocale, appStoreToGooglePlay, appStoreToUnified, appStoreToUnifiedBatch, convertObjectFromAppStore, convertObjectFromGooglePlay, convertObjectToAppStore, convertObjectToGooglePlay, getAsoDataDir, getProductsDir, getPublicDir, getPullDataDir, getPushDataDir, googlePlayToAppStore, googlePlayToUnified, googlePlayToUnifiedBatch, isAppStoreLocale, isAppStoreMultilingual, isGooglePlayLocale, isGooglePlayMultilingual, isSupportedLocale, loadAsoFromConfig, saveAsoToAsoDir, saveAsoToConfig, unifiedToAppStore, unifiedToAppStoreBatch, unifiedToBothPlatforms, unifiedToGooglePlay, unifiedToGooglePlayBatch };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pabal-web-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for ASO data management with shared types and utilities",
|
|
6
6
|
"author": "skyu",
|
|
@@ -39,7 +39,9 @@
|
|
|
39
39
|
"prepublishOnly": "npm run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@googleapis/androidpublisher": "^33.2.0",
|
|
42
43
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
44
|
+
"appstore-connect-sdk": "^1.3.2",
|
|
43
45
|
"zod": "^3.25.76",
|
|
44
46
|
"zod-to-json-schema": "^3.25.0"
|
|
45
47
|
},
|