@gpc-cli/core 0.9.51 → 0.9.53
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/index.d.ts +83 -1
- package/dist/index.js +488 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.d.ts
CHANGED
|
@@ -1323,9 +1323,91 @@ declare function renderJson(g: GeneratedChangelog): string;
|
|
|
1323
1323
|
|
|
1324
1324
|
declare function renderPrompt(g: GeneratedChangelog): string;
|
|
1325
1325
|
|
|
1326
|
+
type Provider = "anthropic" | "openai" | "google";
|
|
1327
|
+
type TranslationPath = "gateway" | "direct";
|
|
1328
|
+
type ErrorReason = "rate_limited" | "auth" | "safety_blocked" | "timeout" | "network" | "no_source" | "unknown";
|
|
1329
|
+
interface TranslationResult {
|
|
1330
|
+
text: string;
|
|
1331
|
+
tokensIn: number;
|
|
1332
|
+
tokensOut: number;
|
|
1333
|
+
}
|
|
1334
|
+
type Translator = (locale: string, sourceText: string) => Promise<TranslationResult>;
|
|
1335
|
+
interface TranslatorConfig {
|
|
1336
|
+
path: TranslationPath;
|
|
1337
|
+
provider: Provider;
|
|
1338
|
+
model: string;
|
|
1339
|
+
runId: string;
|
|
1340
|
+
}
|
|
1341
|
+
interface ResolveAiConfigOptions {
|
|
1342
|
+
provider?: string;
|
|
1343
|
+
model?: string;
|
|
1344
|
+
env?: NodeJS.ProcessEnv;
|
|
1345
|
+
}
|
|
1346
|
+
declare const PROVIDER_WHITELIST: readonly Provider[];
|
|
1347
|
+
declare const DEFAULT_MODELS: Record<Provider, string>;
|
|
1348
|
+
declare function resolveAiConfig(opts?: ResolveAiConfigOptions): TranslatorConfig;
|
|
1349
|
+
declare function classifyError(err: unknown): ErrorReason;
|
|
1350
|
+
declare function createTranslator(config: TranslatorConfig): Promise<Translator>;
|
|
1351
|
+
declare function fetchAggregateCost(runId: string): Promise<number | undefined>;
|
|
1352
|
+
declare function formatPathLabel(config: TranslatorConfig): string;
|
|
1353
|
+
|
|
1354
|
+
declare const PLAY_STORE_LIMIT = 500;
|
|
1355
|
+
declare const PLACEHOLDER_TEXT = "[needs translation \u2014 pass --ai, or paste the prompt emitted by --format prompt]";
|
|
1356
|
+
type PlayStoreFormat = "md" | "json" | "prompt";
|
|
1357
|
+
interface LocaleEntry {
|
|
1358
|
+
language: string;
|
|
1359
|
+
text: string;
|
|
1360
|
+
chars: number;
|
|
1361
|
+
limit: number;
|
|
1362
|
+
status: "ok" | "over" | "placeholder" | "empty" | "failed";
|
|
1363
|
+
}
|
|
1364
|
+
interface LocaleBundle {
|
|
1365
|
+
from: string;
|
|
1366
|
+
to: string;
|
|
1367
|
+
limit: number;
|
|
1368
|
+
sourceLanguage: string;
|
|
1369
|
+
locales: LocaleEntry[];
|
|
1370
|
+
overflows: string[];
|
|
1371
|
+
}
|
|
1372
|
+
interface PlayStoreRenderOptions {
|
|
1373
|
+
locales: string[];
|
|
1374
|
+
format: PlayStoreFormat;
|
|
1375
|
+
sourceLanguage?: string;
|
|
1376
|
+
}
|
|
1377
|
+
declare function buildLocaleBundle(g: GeneratedChangelog, opts: PlayStoreRenderOptions): LocaleBundle;
|
|
1378
|
+
declare function renderPlayStoreMd(bundle: LocaleBundle): string;
|
|
1379
|
+
declare function renderPlayStoreJson(bundle: LocaleBundle): string;
|
|
1380
|
+
declare function renderPlayStorePrompt(bundle: LocaleBundle, g: GeneratedChangelog): string;
|
|
1381
|
+
declare function renderPlayStore(g: GeneratedChangelog, opts: PlayStoreRenderOptions): {
|
|
1382
|
+
output: string;
|
|
1383
|
+
bundle: LocaleBundle;
|
|
1384
|
+
};
|
|
1385
|
+
interface TranslationFailure {
|
|
1386
|
+
language: string;
|
|
1387
|
+
reason: ErrorReason;
|
|
1388
|
+
}
|
|
1389
|
+
interface TranslateBundleOptions {
|
|
1390
|
+
translator: Translator;
|
|
1391
|
+
strict?: boolean;
|
|
1392
|
+
onError?: (failure: TranslationFailure, err: unknown) => void;
|
|
1393
|
+
onTranslated?: (entry: LocaleEntry) => void;
|
|
1394
|
+
}
|
|
1395
|
+
interface TranslatedBundle extends LocaleBundle {
|
|
1396
|
+
tokensIn: number;
|
|
1397
|
+
tokensOut: number;
|
|
1398
|
+
failures: TranslationFailure[];
|
|
1399
|
+
}
|
|
1400
|
+
declare function translateBundle(bundle: LocaleBundle, options: TranslateBundleOptions): Promise<TranslatedBundle>;
|
|
1401
|
+
|
|
1326
1402
|
type Renderer = (g: GeneratedChangelog) => string;
|
|
1327
1403
|
declare const RENDERERS: Record<OutputMode, Renderer>;
|
|
1328
1404
|
|
|
1405
|
+
interface ResolveLocalesOptions {
|
|
1406
|
+
client?: PlayApiClient;
|
|
1407
|
+
packageName?: string;
|
|
1408
|
+
}
|
|
1409
|
+
declare function resolveLocales(input: string, options?: ResolveLocalesOptions): Promise<string[]>;
|
|
1410
|
+
|
|
1329
1411
|
interface RtdnStatus {
|
|
1330
1412
|
topicName: string | null;
|
|
1331
1413
|
enabled: boolean;
|
|
@@ -1371,4 +1453,4 @@ declare function decodeNotification(base64Payload: string): DecodedNotification;
|
|
|
1371
1453
|
*/
|
|
1372
1454
|
declare function formatNotification(notification: DecodedNotification): Record<string, unknown>;
|
|
1373
1455
|
|
|
1374
|
-
export { ApiError, type AppInfo, type AppStatus, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_PREFLIGHT_CONFIG, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, type ParsedCommit, type ParsedManifest, type ParsedMonth, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, batchGetOrders, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkThreshold, clearAuditLog, compareBundles, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderJson, renderMarkdown, renderPrompt, replyToReview, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
|
1456
|
+
export { ApiError, type AppInfo, type AppStatus, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, batchGetOrders, batchSyncInAppProducts, buildLocaleBundle, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkThreshold, classifyError, clearAuditLog, compareBundles, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
package/dist/index.js
CHANGED
|
@@ -7881,6 +7881,416 @@ function renderPrompt(g) {
|
|
|
7881
7881
|
return lines.join("\n");
|
|
7882
7882
|
}
|
|
7883
7883
|
|
|
7884
|
+
// src/commands/changelog-ai.ts
|
|
7885
|
+
import { randomUUID } from "crypto";
|
|
7886
|
+
var PROVIDER_WHITELIST = ["anthropic", "openai", "google"];
|
|
7887
|
+
var DEFAULT_MODELS = {
|
|
7888
|
+
anthropic: "claude-sonnet-4-6",
|
|
7889
|
+
openai: "gpt-4o-mini",
|
|
7890
|
+
google: "gemini-2.5-flash"
|
|
7891
|
+
};
|
|
7892
|
+
function resolveAiConfig(opts = {}) {
|
|
7893
|
+
const env = opts.env ?? process.env;
|
|
7894
|
+
const hasGateway = typeof env["AI_GATEWAY_API_KEY"] === "string" && env["AI_GATEWAY_API_KEY"].length > 0;
|
|
7895
|
+
let provider;
|
|
7896
|
+
if (opts.provider) {
|
|
7897
|
+
const normalized = opts.provider.toLowerCase();
|
|
7898
|
+
if (!PROVIDER_WHITELIST.includes(normalized)) {
|
|
7899
|
+
throw new GpcError(
|
|
7900
|
+
`Unknown --provider "${opts.provider}"`,
|
|
7901
|
+
"CHANGELOG_AI_UNKNOWN_PROVIDER",
|
|
7902
|
+
2,
|
|
7903
|
+
`Valid providers: ${PROVIDER_WHITELIST.join(", ")}`
|
|
7904
|
+
);
|
|
7905
|
+
}
|
|
7906
|
+
provider = normalized;
|
|
7907
|
+
} else if (hasGateway || env["ANTHROPIC_API_KEY"]) {
|
|
7908
|
+
provider = "anthropic";
|
|
7909
|
+
} else if (env["OPENAI_API_KEY"]) {
|
|
7910
|
+
provider = "openai";
|
|
7911
|
+
} else if (env["GOOGLE_GENERATIVE_AI_API_KEY"]) {
|
|
7912
|
+
provider = "google";
|
|
7913
|
+
} else {
|
|
7914
|
+
throw new GpcError(
|
|
7915
|
+
"No AI provider credentials found in env",
|
|
7916
|
+
"CHANGELOG_AI_NO_CREDENTIALS",
|
|
7917
|
+
3,
|
|
7918
|
+
"Set one of: AI_GATEWAY_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY."
|
|
7919
|
+
);
|
|
7920
|
+
}
|
|
7921
|
+
const model = opts.model ?? DEFAULT_MODELS[provider];
|
|
7922
|
+
const path = hasGateway ? "gateway" : "direct";
|
|
7923
|
+
const runId = randomUUID();
|
|
7924
|
+
return { path, provider, model, runId };
|
|
7925
|
+
}
|
|
7926
|
+
function classifyError(err) {
|
|
7927
|
+
if (!err || typeof err !== "object") return "unknown";
|
|
7928
|
+
const e = err;
|
|
7929
|
+
const name = typeof e["name"] === "string" ? e["name"] : "";
|
|
7930
|
+
const statusCode = typeof e["statusCode"] === "number" ? e["statusCode"] : typeof e["status"] === "number" ? e["status"] : 0;
|
|
7931
|
+
const message = typeof e["message"] === "string" ? e["message"].toLowerCase() : "";
|
|
7932
|
+
const finishReason = typeof e["finishReason"] === "string" ? e["finishReason"] : "";
|
|
7933
|
+
if (name === "RateLimitError" || statusCode === 429 || statusCode === 529) return "rate_limited";
|
|
7934
|
+
if (statusCode === 401 || statusCode === 403) return "auth";
|
|
7935
|
+
if (message.includes("api key invalid") || message.includes("invalid api key")) return "auth";
|
|
7936
|
+
if (finishReason === "SAFETY") return "safety_blocked";
|
|
7937
|
+
if (statusCode === 400 && (message.includes("content_policy") || message.includes("content policy") || message.includes("safety"))) {
|
|
7938
|
+
return "safety_blocked";
|
|
7939
|
+
}
|
|
7940
|
+
if (name === "AbortError" || name === "TimeoutError") return "timeout";
|
|
7941
|
+
if (message.includes("timeout") || message.includes("timed out")) return "timeout";
|
|
7942
|
+
if (message.includes("econnrefused") || message.includes("enotfound") || message.includes("etimedout") || message.includes("network") || name === "TypeError" && message.includes("fetch")) {
|
|
7943
|
+
return "network";
|
|
7944
|
+
}
|
|
7945
|
+
return "unknown";
|
|
7946
|
+
}
|
|
7947
|
+
function buildSystemPrompt() {
|
|
7948
|
+
return [
|
|
7949
|
+
`You translate Play Store "What's new" release notes for Android apps.`,
|
|
7950
|
+
"Rules:",
|
|
7951
|
+
"- Translation MUST be at most 500 Unicode code points.",
|
|
7952
|
+
'- Preserve bullet format (one item per line, starts with "- ").',
|
|
7953
|
+
"- User-facing tone. Avoid internal jargon.",
|
|
7954
|
+
'- Do not translate technical names (package names, CLI flags, "GPC").',
|
|
7955
|
+
"- Drop the conventional-commit prefix (feat:/fix:/docs:) if it feels unnatural in the target language.",
|
|
7956
|
+
"Respond with the translated text only. No explanations, no markdown headings."
|
|
7957
|
+
].join("\n");
|
|
7958
|
+
}
|
|
7959
|
+
function buildUserPrompt(locale, sourceText) {
|
|
7960
|
+
return `Translate the following release notes into ${locale}:
|
|
7961
|
+
|
|
7962
|
+
${sourceText}`;
|
|
7963
|
+
}
|
|
7964
|
+
function providerSpecificOptions(provider) {
|
|
7965
|
+
if (provider === "google") {
|
|
7966
|
+
return { google: { thinkingConfig: { thinkingBudget: 0 } } };
|
|
7967
|
+
}
|
|
7968
|
+
return {};
|
|
7969
|
+
}
|
|
7970
|
+
function readUsage(usage) {
|
|
7971
|
+
if (!usage || typeof usage !== "object") return { tokensIn: 0, tokensOut: 0 };
|
|
7972
|
+
const u = usage;
|
|
7973
|
+
const tokensIn = typeof u["inputTokens"] === "number" ? u["inputTokens"] : typeof u["promptTokens"] === "number" ? u["promptTokens"] : 0;
|
|
7974
|
+
const tokensOut = typeof u["outputTokens"] === "number" ? u["outputTokens"] : typeof u["completionTokens"] === "number" ? u["completionTokens"] : 0;
|
|
7975
|
+
return { tokensIn, tokensOut };
|
|
7976
|
+
}
|
|
7977
|
+
async function createTranslator(config) {
|
|
7978
|
+
const ai = await import("ai");
|
|
7979
|
+
const generateText = ai.generateText;
|
|
7980
|
+
if (config.path === "gateway") {
|
|
7981
|
+
const modelString = `${config.provider}/${config.model}`;
|
|
7982
|
+
return async (locale, sourceText) => {
|
|
7983
|
+
const result = await generateText({
|
|
7984
|
+
model: ai.gateway(modelString),
|
|
7985
|
+
system: buildSystemPrompt(),
|
|
7986
|
+
prompt: buildUserPrompt(locale, sourceText),
|
|
7987
|
+
temperature: 0.2,
|
|
7988
|
+
providerOptions: {
|
|
7989
|
+
gateway: { tags: [`gpc-changelog-${config.runId}`] },
|
|
7990
|
+
...providerSpecificOptions(config.provider)
|
|
7991
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7992
|
+
}
|
|
7993
|
+
});
|
|
7994
|
+
const { tokensIn, tokensOut } = readUsage(result.usage);
|
|
7995
|
+
return { text: result.text, tokensIn, tokensOut };
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
let modelFactory;
|
|
7999
|
+
if (config.provider === "anthropic") {
|
|
8000
|
+
const mod = await import("@ai-sdk/anthropic");
|
|
8001
|
+
modelFactory = (id) => mod.anthropic(id);
|
|
8002
|
+
} else if (config.provider === "openai") {
|
|
8003
|
+
const mod = await import("@ai-sdk/openai");
|
|
8004
|
+
modelFactory = (id) => mod.openai(id);
|
|
8005
|
+
} else {
|
|
8006
|
+
const mod = await import("@ai-sdk/google");
|
|
8007
|
+
modelFactory = (id) => mod.google(id);
|
|
8008
|
+
}
|
|
8009
|
+
return async (locale, sourceText) => {
|
|
8010
|
+
const result = await generateText({
|
|
8011
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8012
|
+
model: modelFactory(config.model),
|
|
8013
|
+
system: buildSystemPrompt(),
|
|
8014
|
+
prompt: buildUserPrompt(locale, sourceText),
|
|
8015
|
+
temperature: 0.2,
|
|
8016
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8017
|
+
providerOptions: providerSpecificOptions(config.provider)
|
|
8018
|
+
});
|
|
8019
|
+
const { tokensIn, tokensOut } = readUsage(result.usage);
|
|
8020
|
+
return { text: result.text, tokensIn, tokensOut };
|
|
8021
|
+
};
|
|
8022
|
+
}
|
|
8023
|
+
async function fetchAggregateCost(runId) {
|
|
8024
|
+
try {
|
|
8025
|
+
const { gateway } = await import("ai");
|
|
8026
|
+
const now = /* @__PURE__ */ new Date();
|
|
8027
|
+
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
|
|
8028
|
+
const today = now.toISOString().slice(0, 10);
|
|
8029
|
+
const startDate = yesterday.toISOString().slice(0, 10);
|
|
8030
|
+
const report = await gateway.getSpendReport({
|
|
8031
|
+
startDate,
|
|
8032
|
+
endDate: today,
|
|
8033
|
+
groupBy: "tag",
|
|
8034
|
+
tags: [`gpc-changelog-${runId}`]
|
|
8035
|
+
});
|
|
8036
|
+
const results = report.results ?? [];
|
|
8037
|
+
return results.reduce((sum, row) => sum + (row.totalCost ?? 0), 0);
|
|
8038
|
+
} catch {
|
|
8039
|
+
return void 0;
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
var PROVIDER_BRAND = {
|
|
8043
|
+
anthropic: "Anthropic",
|
|
8044
|
+
openai: "OpenAI",
|
|
8045
|
+
google: "Google"
|
|
8046
|
+
};
|
|
8047
|
+
function formatPathLabel(config) {
|
|
8048
|
+
if (config.path === "gateway") {
|
|
8049
|
+
return `routing via AI Gateway (${config.provider}/${config.model})`;
|
|
8050
|
+
}
|
|
8051
|
+
return `direct ${PROVIDER_BRAND[config.provider]} SDK (${config.model})`;
|
|
8052
|
+
}
|
|
8053
|
+
|
|
8054
|
+
// src/commands/changelog-renderers/play-store.ts
|
|
8055
|
+
var PLAY_STORE_LIMIT = 500;
|
|
8056
|
+
var PLACEHOLDER_TEXT = "[needs translation \u2014 pass --ai, or paste the prompt emitted by --format prompt]";
|
|
8057
|
+
function safeLine2(s) {
|
|
8058
|
+
return s.replace(/[\r\n]+/g, " ").trim();
|
|
8059
|
+
}
|
|
8060
|
+
function countChars(s) {
|
|
8061
|
+
return [...s].length;
|
|
8062
|
+
}
|
|
8063
|
+
function truncateToLimit(text, limit) {
|
|
8064
|
+
if (countChars(text) <= limit) return text;
|
|
8065
|
+
const chars = [...text];
|
|
8066
|
+
return chars.slice(0, limit - 1).join("") + "\u2026";
|
|
8067
|
+
}
|
|
8068
|
+
function renderEnglishBullets(g) {
|
|
8069
|
+
const lines = [];
|
|
8070
|
+
for (const type of SECTION_ORDER) {
|
|
8071
|
+
const commits = g.grouped[type] ?? [];
|
|
8072
|
+
for (const commit of commits) {
|
|
8073
|
+
lines.push(`- ${type}: ${safeLine2(commit.subject)}`);
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
return lines.join("\n");
|
|
8077
|
+
}
|
|
8078
|
+
function buildLocaleBundle(g, opts) {
|
|
8079
|
+
const sourceLanguage = opts.sourceLanguage ?? "en-US";
|
|
8080
|
+
const sourceText = renderEnglishBullets(g);
|
|
8081
|
+
const isEmpty = sourceText.length === 0;
|
|
8082
|
+
const overflows = [];
|
|
8083
|
+
const entries = opts.locales.map((language) => {
|
|
8084
|
+
if (language === sourceLanguage) {
|
|
8085
|
+
if (isEmpty) {
|
|
8086
|
+
const emptyText = "No notable changes.";
|
|
8087
|
+
return {
|
|
8088
|
+
language,
|
|
8089
|
+
text: emptyText,
|
|
8090
|
+
chars: countChars(emptyText),
|
|
8091
|
+
limit: PLAY_STORE_LIMIT,
|
|
8092
|
+
status: "empty"
|
|
8093
|
+
};
|
|
8094
|
+
}
|
|
8095
|
+
const chars = countChars(sourceText);
|
|
8096
|
+
const status = chars > PLAY_STORE_LIMIT ? "over" : "ok";
|
|
8097
|
+
if (status === "over") overflows.push(language);
|
|
8098
|
+
const text = status === "over" ? truncateToLimit(sourceText, PLAY_STORE_LIMIT) : sourceText;
|
|
8099
|
+
return {
|
|
8100
|
+
language,
|
|
8101
|
+
text,
|
|
8102
|
+
chars,
|
|
8103
|
+
limit: PLAY_STORE_LIMIT,
|
|
8104
|
+
status
|
|
8105
|
+
};
|
|
8106
|
+
}
|
|
8107
|
+
return {
|
|
8108
|
+
language,
|
|
8109
|
+
text: PLACEHOLDER_TEXT,
|
|
8110
|
+
chars: countChars(PLACEHOLDER_TEXT),
|
|
8111
|
+
limit: PLAY_STORE_LIMIT,
|
|
8112
|
+
status: "placeholder"
|
|
8113
|
+
};
|
|
8114
|
+
});
|
|
8115
|
+
return {
|
|
8116
|
+
from: g.from,
|
|
8117
|
+
to: g.to,
|
|
8118
|
+
limit: PLAY_STORE_LIMIT,
|
|
8119
|
+
sourceLanguage,
|
|
8120
|
+
locales: entries,
|
|
8121
|
+
overflows
|
|
8122
|
+
};
|
|
8123
|
+
}
|
|
8124
|
+
function renderPlayStoreMd(bundle) {
|
|
8125
|
+
const lines = [];
|
|
8126
|
+
lines.push(`# Play Store release notes (${bundle.from} \u2192 ${bundle.to})`);
|
|
8127
|
+
lines.push("");
|
|
8128
|
+
for (const entry of bundle.locales) {
|
|
8129
|
+
let heading;
|
|
8130
|
+
if (entry.status === "placeholder") {
|
|
8131
|
+
heading = `## ${entry.language} (needs translation)`;
|
|
8132
|
+
} else if (entry.status === "empty") {
|
|
8133
|
+
heading = `## ${entry.language} (empty)`;
|
|
8134
|
+
} else {
|
|
8135
|
+
const suffix = entry.status === "over" ? ` \u26A0 truncated` : "";
|
|
8136
|
+
heading = `## ${entry.language} (${entry.chars}/${entry.limit})${suffix}`;
|
|
8137
|
+
}
|
|
8138
|
+
lines.push(heading);
|
|
8139
|
+
lines.push(entry.text);
|
|
8140
|
+
lines.push("");
|
|
8141
|
+
}
|
|
8142
|
+
lines.push("## Summary");
|
|
8143
|
+
for (const entry of bundle.locales) {
|
|
8144
|
+
if (entry.status === "placeholder") {
|
|
8145
|
+
lines.push(`- ${entry.language}: placeholder`);
|
|
8146
|
+
} else if (entry.status === "empty") {
|
|
8147
|
+
lines.push(`- ${entry.language}: empty`);
|
|
8148
|
+
} else {
|
|
8149
|
+
const mark = entry.status === "over" ? "\u2717 over limit" : "\u2713";
|
|
8150
|
+
lines.push(`- ${entry.language}: ${entry.chars}/${entry.limit} ${mark}`);
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
return lines.join("\n");
|
|
8154
|
+
}
|
|
8155
|
+
function renderPlayStoreJson(bundle) {
|
|
8156
|
+
return JSON.stringify(bundle, null, 2);
|
|
8157
|
+
}
|
|
8158
|
+
function renderPlayStorePrompt(bundle, g) {
|
|
8159
|
+
const source = bundle.locales.find((e) => e.language === bundle.sourceLanguage);
|
|
8160
|
+
const targets = bundle.locales.filter((e) => e.language !== bundle.sourceLanguage);
|
|
8161
|
+
const lines = [];
|
|
8162
|
+
lines.push(`You are translating Play Store "What's new" release notes from ${bundle.sourceLanguage}.`);
|
|
8163
|
+
lines.push("");
|
|
8164
|
+
lines.push("TARGETS:");
|
|
8165
|
+
for (const t of targets) lines.push(` - ${t.language}`);
|
|
8166
|
+
if (targets.length === 0) lines.push(" (none \u2014 source-only bundle)");
|
|
8167
|
+
lines.push("");
|
|
8168
|
+
lines.push("CONSTRAINTS:");
|
|
8169
|
+
lines.push(`- Each translation MUST be \u2264 ${bundle.limit} Unicode code points`);
|
|
8170
|
+
lines.push('- Preserve the bullet format (one item per line, starts with "- ")');
|
|
8171
|
+
lines.push("- Keep a user-facing tone (no internal jargon)");
|
|
8172
|
+
lines.push('- Do not translate technical names (package names, CLI flags, "GPC")');
|
|
8173
|
+
lines.push("- Drop the conventional-commit prefix (feat:/fix:) if it feels unnatural in the target language");
|
|
8174
|
+
lines.push("");
|
|
8175
|
+
lines.push(`SOURCE (${bundle.sourceLanguage}, ${source?.chars ?? 0}/${bundle.limit} chars):`);
|
|
8176
|
+
lines.push("```");
|
|
8177
|
+
lines.push(source?.text ?? "(empty)");
|
|
8178
|
+
lines.push("```");
|
|
8179
|
+
lines.push("");
|
|
8180
|
+
if (g.headlineCandidates.length > 0) {
|
|
8181
|
+
lines.push("CONTEXT (clusters, for theme awareness):");
|
|
8182
|
+
for (const c of g.headlineCandidates) {
|
|
8183
|
+
lines.push(` - ${c.label} (${c.commits.length} commits, primary ${c.primaryType})`);
|
|
8184
|
+
}
|
|
8185
|
+
lines.push("");
|
|
8186
|
+
}
|
|
8187
|
+
lines.push("OUTPUT FORMAT (one heading + body per target language):");
|
|
8188
|
+
lines.push("```markdown");
|
|
8189
|
+
for (const t of targets) {
|
|
8190
|
+
lines.push(`## ${t.language}`);
|
|
8191
|
+
lines.push("<translation>");
|
|
8192
|
+
lines.push("");
|
|
8193
|
+
}
|
|
8194
|
+
if (targets.length === 0) {
|
|
8195
|
+
lines.push("(no target locales \u2014 nothing to translate)");
|
|
8196
|
+
}
|
|
8197
|
+
lines.push("```");
|
|
8198
|
+
return lines.join("\n");
|
|
8199
|
+
}
|
|
8200
|
+
function renderPlayStore(g, opts) {
|
|
8201
|
+
const bundle = buildLocaleBundle(g, opts);
|
|
8202
|
+
switch (opts.format) {
|
|
8203
|
+
case "md":
|
|
8204
|
+
return { output: renderPlayStoreMd(bundle), bundle };
|
|
8205
|
+
case "json":
|
|
8206
|
+
return { output: renderPlayStoreJson(bundle), bundle };
|
|
8207
|
+
case "prompt":
|
|
8208
|
+
return { output: renderPlayStorePrompt(bundle, g), bundle };
|
|
8209
|
+
}
|
|
8210
|
+
}
|
|
8211
|
+
async function translateBundle(bundle, options) {
|
|
8212
|
+
const source = bundle.locales.find((e) => e.language === bundle.sourceLanguage);
|
|
8213
|
+
const sourceText = source?.text ?? "";
|
|
8214
|
+
const hasSource = source !== void 0 && sourceText.trim().length > 0;
|
|
8215
|
+
let tokensIn = 0;
|
|
8216
|
+
let tokensOut = 0;
|
|
8217
|
+
const failures = [];
|
|
8218
|
+
const newLocales = [];
|
|
8219
|
+
for (const entry of bundle.locales) {
|
|
8220
|
+
if (entry.status !== "placeholder") {
|
|
8221
|
+
newLocales.push(entry);
|
|
8222
|
+
continue;
|
|
8223
|
+
}
|
|
8224
|
+
if (!hasSource) {
|
|
8225
|
+
const failure = { language: entry.language, reason: "no_source" };
|
|
8226
|
+
failures.push(failure);
|
|
8227
|
+
options.onError?.(failure, new Error("source locale missing or empty"));
|
|
8228
|
+
const failedText = `[translation failed: no_source]`;
|
|
8229
|
+
newLocales.push({
|
|
8230
|
+
language: entry.language,
|
|
8231
|
+
text: failedText,
|
|
8232
|
+
chars: countChars(failedText),
|
|
8233
|
+
limit: PLAY_STORE_LIMIT,
|
|
8234
|
+
status: "failed"
|
|
8235
|
+
});
|
|
8236
|
+
continue;
|
|
8237
|
+
}
|
|
8238
|
+
try {
|
|
8239
|
+
const result = await options.translator(entry.language, sourceText);
|
|
8240
|
+
tokensIn += result.tokensIn;
|
|
8241
|
+
tokensOut += result.tokensOut;
|
|
8242
|
+
let text = result.text.trim();
|
|
8243
|
+
let chars = countChars(text);
|
|
8244
|
+
let status = "ok";
|
|
8245
|
+
if (chars > PLAY_STORE_LIMIT) {
|
|
8246
|
+
text = truncateToLimit(text, PLAY_STORE_LIMIT);
|
|
8247
|
+
chars = countChars(text);
|
|
8248
|
+
status = "over";
|
|
8249
|
+
}
|
|
8250
|
+
const translated2 = {
|
|
8251
|
+
language: entry.language,
|
|
8252
|
+
text,
|
|
8253
|
+
chars,
|
|
8254
|
+
limit: PLAY_STORE_LIMIT,
|
|
8255
|
+
status
|
|
8256
|
+
};
|
|
8257
|
+
newLocales.push(translated2);
|
|
8258
|
+
options.onTranslated?.(translated2);
|
|
8259
|
+
} catch (err) {
|
|
8260
|
+
const reason = classifyError(err);
|
|
8261
|
+
const failure = { language: entry.language, reason };
|
|
8262
|
+
failures.push(failure);
|
|
8263
|
+
options.onError?.(failure, err);
|
|
8264
|
+
const failedText = `[translation failed: ${reason}]`;
|
|
8265
|
+
newLocales.push({
|
|
8266
|
+
language: entry.language,
|
|
8267
|
+
text: failedText,
|
|
8268
|
+
chars: countChars(failedText),
|
|
8269
|
+
limit: PLAY_STORE_LIMIT,
|
|
8270
|
+
status: "failed"
|
|
8271
|
+
});
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
const overflows = newLocales.filter((e) => e.status === "over").map((e) => e.language);
|
|
8275
|
+
const translated = {
|
|
8276
|
+
...bundle,
|
|
8277
|
+
locales: newLocales,
|
|
8278
|
+
overflows,
|
|
8279
|
+
tokensIn,
|
|
8280
|
+
tokensOut,
|
|
8281
|
+
failures
|
|
8282
|
+
};
|
|
8283
|
+
if (options.strict && failures.length > 0) {
|
|
8284
|
+
throw new GpcError(
|
|
8285
|
+
`${failures.length} locale${failures.length === 1 ? "" : "s"} failed to translate: ${failures.map((f) => `${f.language}=${f.reason}`).join(", ")}`,
|
|
8286
|
+
"CHANGELOG_AI_TRANSLATION_FAILED",
|
|
8287
|
+
1,
|
|
8288
|
+
"Remove --strict to continue on errors, or check credentials and retry."
|
|
8289
|
+
);
|
|
8290
|
+
}
|
|
8291
|
+
return translated;
|
|
8292
|
+
}
|
|
8293
|
+
|
|
7884
8294
|
// src/commands/changelog-renderers/index.ts
|
|
7885
8295
|
var RENDERERS = {
|
|
7886
8296
|
md: renderMarkdown,
|
|
@@ -7888,6 +8298,68 @@ var RENDERERS = {
|
|
|
7888
8298
|
prompt: renderPrompt
|
|
7889
8299
|
};
|
|
7890
8300
|
|
|
8301
|
+
// src/commands/changelog-locales.ts
|
|
8302
|
+
async function resolveLocales(input, options = {}) {
|
|
8303
|
+
const trimmed = input.trim();
|
|
8304
|
+
if (!trimmed) {
|
|
8305
|
+
throw new GpcError(
|
|
8306
|
+
"--locales is empty",
|
|
8307
|
+
"CHANGELOG_LOCALES_REQUIRED",
|
|
8308
|
+
2,
|
|
8309
|
+
"Pass a comma-separated list (e.g., --locales en-US,fr-FR) or --locales auto."
|
|
8310
|
+
);
|
|
8311
|
+
}
|
|
8312
|
+
if (trimmed === "auto") {
|
|
8313
|
+
const { client, packageName } = options;
|
|
8314
|
+
if (!client || !packageName) {
|
|
8315
|
+
throw new GpcError(
|
|
8316
|
+
"--locales auto requires an authenticated API client and --app",
|
|
8317
|
+
"CHANGELOG_LOCALES_AUTO_NO_APP",
|
|
8318
|
+
2,
|
|
8319
|
+
"Pass --app <package> or set config.app, and ensure credentials are configured."
|
|
8320
|
+
);
|
|
8321
|
+
}
|
|
8322
|
+
const edit = await client.edits.insert(packageName);
|
|
8323
|
+
try {
|
|
8324
|
+
const listings = await client.listings.list(packageName, edit.id);
|
|
8325
|
+
await client.edits.delete(packageName, edit.id);
|
|
8326
|
+
const langs = listings.map((l) => l.language);
|
|
8327
|
+
if (langs.length === 0) {
|
|
8328
|
+
throw new GpcError(
|
|
8329
|
+
`No Play Store listings found for ${packageName}`,
|
|
8330
|
+
"CHANGELOG_LOCALES_EMPTY",
|
|
8331
|
+
1,
|
|
8332
|
+
"Create at least one listing in Play Console, or pass --locales explicitly."
|
|
8333
|
+
);
|
|
8334
|
+
}
|
|
8335
|
+
return langs;
|
|
8336
|
+
} catch (error) {
|
|
8337
|
+
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
8338
|
+
});
|
|
8339
|
+
throw error;
|
|
8340
|
+
}
|
|
8341
|
+
}
|
|
8342
|
+
const locales = trimmed.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8343
|
+
if (locales.length === 0) {
|
|
8344
|
+
throw new GpcError(
|
|
8345
|
+
"--locales parsed to an empty list",
|
|
8346
|
+
"CHANGELOG_LOCALES_REQUIRED",
|
|
8347
|
+
2,
|
|
8348
|
+
"Pass a comma-separated list (e.g., --locales en-US,fr-FR) or --locales auto."
|
|
8349
|
+
);
|
|
8350
|
+
}
|
|
8351
|
+
const invalid = locales.filter((l) => !isValidBcp47(l));
|
|
8352
|
+
if (invalid.length > 0) {
|
|
8353
|
+
throw new GpcError(
|
|
8354
|
+
`Invalid locale(s): ${invalid.join(", ")}`,
|
|
8355
|
+
"CHANGELOG_LOCALES_INVALID",
|
|
8356
|
+
2,
|
|
8357
|
+
"Use BCP 47 codes recognized by Google Play (e.g., en-US, fr-FR, de-DE)."
|
|
8358
|
+
);
|
|
8359
|
+
}
|
|
8360
|
+
return locales;
|
|
8361
|
+
}
|
|
8362
|
+
|
|
7891
8363
|
// src/commands/rtdn.ts
|
|
7892
8364
|
var SUBSCRIPTION_NOTIFICATION_TYPES = {
|
|
7893
8365
|
1: "SUBSCRIPTION_RECOVERED",
|
|
@@ -7984,11 +8456,15 @@ export {
|
|
|
7984
8456
|
ApiError,
|
|
7985
8457
|
ConfigError,
|
|
7986
8458
|
DEFAULT_LIMITS,
|
|
8459
|
+
DEFAULT_MODELS,
|
|
7987
8460
|
DEFAULT_PREFLIGHT_CONFIG,
|
|
7988
8461
|
GOOGLE_PLAY_LANGUAGES,
|
|
7989
8462
|
GpcError,
|
|
7990
8463
|
NetworkError,
|
|
7991
8464
|
PERMISSION_PROPAGATION_WARNING,
|
|
8465
|
+
PLACEHOLDER_TEXT,
|
|
8466
|
+
PLAY_STORE_LIMIT,
|
|
8467
|
+
PROVIDER_WHITELIST,
|
|
7992
8468
|
PluginManager,
|
|
7993
8469
|
RENDERERS,
|
|
7994
8470
|
SECTION_ORDER,
|
|
@@ -8007,11 +8483,13 @@ export {
|
|
|
8007
8483
|
analyzeReviews2 as analyzeReviews,
|
|
8008
8484
|
batchGetOrders,
|
|
8009
8485
|
batchSyncInAppProducts,
|
|
8486
|
+
buildLocaleBundle,
|
|
8010
8487
|
cancelRecoveryAction,
|
|
8011
8488
|
cancelSubscriptionPurchase,
|
|
8012
8489
|
cancelSubscriptionV2,
|
|
8013
8490
|
checkBundleSize,
|
|
8014
8491
|
checkThreshold,
|
|
8492
|
+
classifyError,
|
|
8015
8493
|
clearAuditLog,
|
|
8016
8494
|
compareBundles,
|
|
8017
8495
|
compareVersionVitals,
|
|
@@ -8032,6 +8510,7 @@ export {
|
|
|
8032
8510
|
createSpinner,
|
|
8033
8511
|
createSubscription,
|
|
8034
8512
|
createTrack,
|
|
8513
|
+
createTranslator,
|
|
8035
8514
|
deactivateBasePlan,
|
|
8036
8515
|
deactivateOffer,
|
|
8037
8516
|
decodeNotification,
|
|
@@ -8061,6 +8540,7 @@ export {
|
|
|
8061
8540
|
downloadReport,
|
|
8062
8541
|
exportImages,
|
|
8063
8542
|
exportReviews,
|
|
8543
|
+
fetchAggregateCost,
|
|
8064
8544
|
fetchChangelog,
|
|
8065
8545
|
fetchReleaseNotes,
|
|
8066
8546
|
formatChangelogEntry,
|
|
@@ -8069,6 +8549,7 @@ export {
|
|
|
8069
8549
|
formatJunit,
|
|
8070
8550
|
formatNotification,
|
|
8071
8551
|
formatOutput,
|
|
8552
|
+
formatPathLabel,
|
|
8072
8553
|
formatSlackPayload,
|
|
8073
8554
|
formatStatusDiff,
|
|
8074
8555
|
formatStatusSummary,
|
|
@@ -8170,8 +8651,14 @@ export {
|
|
|
8170
8651
|
removeUser,
|
|
8171
8652
|
renderJson,
|
|
8172
8653
|
renderMarkdown,
|
|
8654
|
+
renderPlayStore,
|
|
8655
|
+
renderPlayStoreJson,
|
|
8656
|
+
renderPlayStoreMd,
|
|
8657
|
+
renderPlayStorePrompt,
|
|
8173
8658
|
renderPrompt,
|
|
8174
8659
|
replyToReview,
|
|
8660
|
+
resolveAiConfig,
|
|
8661
|
+
resolveLocales,
|
|
8175
8662
|
revokeSubscriptionPurchase,
|
|
8176
8663
|
runPreflight,
|
|
8177
8664
|
runWatchLoop,
|
|
@@ -8189,6 +8676,7 @@ export {
|
|
|
8189
8676
|
syncInAppProducts,
|
|
8190
8677
|
topFiles,
|
|
8191
8678
|
trackBreachState,
|
|
8679
|
+
translateBundle,
|
|
8192
8680
|
updateAppDetails,
|
|
8193
8681
|
updateDataSafety,
|
|
8194
8682
|
updateGrant,
|