@gpc-cli/core 0.9.55 → 0.9.56

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 CHANGED
@@ -1472,4 +1472,60 @@ declare function decodeNotification(base64Payload: string): DecodedNotification;
1472
1472
  */
1473
1473
  declare function formatNotification(notification: DecodedNotification): Record<string, unknown>;
1474
1474
 
1475
- export { ApiError, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, 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, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildLocaleBundle, bundleToReleaseNotes, 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, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
1475
+ interface KeystoreFingerprint {
1476
+ sha256: string;
1477
+ alias: string;
1478
+ keystorePath: string;
1479
+ }
1480
+ interface ApiSigningFingerprint {
1481
+ sha256: string;
1482
+ versionCode: number;
1483
+ }
1484
+ interface SigningKeyComparison {
1485
+ local?: KeystoreFingerprint;
1486
+ api?: ApiSigningFingerprint;
1487
+ match: boolean | null;
1488
+ }
1489
+ declare function normalizeFingerprint(fp: string): string;
1490
+ declare function parseKeytoolOutput(stdout: string): {
1491
+ sha256: string;
1492
+ alias: string;
1493
+ };
1494
+ declare function getKeystoreFingerprint(keystorePath: string, storePassword: string, keyAlias?: string): Promise<KeystoreFingerprint>;
1495
+ declare function getApiSigningFingerprint(accessToken: string, packageName: string, apiHost?: string): Promise<ApiSigningFingerprint | null>;
1496
+ declare function compareFingerprints(a: string, b: string): boolean;
1497
+
1498
+ interface SigningConsistencyResult {
1499
+ currentVersionCode: number;
1500
+ currentFingerprint: string;
1501
+ previousVersionCode?: number;
1502
+ previousFingerprint?: string;
1503
+ consistent: boolean;
1504
+ firstRelease: boolean;
1505
+ }
1506
+ declare function checkSigningConsistency(accessToken: string, packageName: string, apiHost?: string): Promise<SigningConsistencyResult>;
1507
+
1508
+ interface ChecklistItem {
1509
+ id: string;
1510
+ title: string;
1511
+ status: "done" | "action-needed" | "cannot-detect";
1512
+ detail?: string;
1513
+ actionUrl?: string;
1514
+ }
1515
+ interface ChecklistResult {
1516
+ items: ChecklistItem[];
1517
+ completed: number;
1518
+ total: number;
1519
+ }
1520
+ interface ChecklistInput {
1521
+ authenticated: boolean;
1522
+ accountEmail?: string;
1523
+ appAccessible?: boolean;
1524
+ bundleCount?: number;
1525
+ hasGeneratedApks?: boolean;
1526
+ interactiveAnswers?: Record<string, boolean>;
1527
+ }
1528
+ declare function buildChecklist(input: ChecklistInput): ChecklistResult;
1529
+ declare function renderChecklistMarkdown(result: ChecklistResult, accountEmail: string): string;
1530
+
1531
+ export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, 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 KeystoreFingerprint, 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 SigningConsistencyResult, type SigningKeyComparison, 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, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, compareBundles, compareFingerprints, 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, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, 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, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, 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, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -8580,6 +8580,323 @@ function formatNotification(notification) {
8580
8580
  }
8581
8581
  return { ...base, type: "unknown" };
8582
8582
  }
8583
+
8584
+ // src/signing.ts
8585
+ import { execFile as execFile3 } from "child_process";
8586
+ import { access as access2, constants } from "fs/promises";
8587
+ var SHA256_RE = /SHA-?256\s*:\s*([A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){31})/;
8588
+ var ALIAS_RE = /Alias name:\s*(.+)/i;
8589
+ function normalizeFingerprint(fp) {
8590
+ return fp.replace(/[:\s]/g, "").toLowerCase();
8591
+ }
8592
+ function parseKeytoolOutput(stdout) {
8593
+ const sha256Match = SHA256_RE.exec(stdout);
8594
+ if (!sha256Match?.[1]) {
8595
+ throw new GpcError(
8596
+ "Could not find SHA-256 fingerprint in keytool output",
8597
+ "KEYTOOL_PARSE_ERROR",
8598
+ 1,
8599
+ "Ensure the keystore contains a valid certificate entry"
8600
+ );
8601
+ }
8602
+ const aliasMatch = ALIAS_RE.exec(stdout);
8603
+ return {
8604
+ sha256: sha256Match[1].toUpperCase(),
8605
+ alias: aliasMatch?.[1]?.trim() ?? "unknown"
8606
+ };
8607
+ }
8608
+ async function getKeystoreFingerprint(keystorePath, storePassword, keyAlias) {
8609
+ await access2(keystorePath, constants.R_OK).catch(() => {
8610
+ throw new GpcError(
8611
+ `Keystore not found or not readable: ${keystorePath}`,
8612
+ "KEYSTORE_READ_ERROR",
8613
+ 1,
8614
+ "Check the path and file permissions"
8615
+ );
8616
+ });
8617
+ const args = ["-list", "-v", "-keystore", keystorePath, "-storepass", storePassword];
8618
+ if (keyAlias) args.push("-alias", keyAlias);
8619
+ const stdout = await new Promise((resolve2, reject) => {
8620
+ execFile3("keytool", args, { timeout: 1e4 }, (err, stdout2, stderr) => {
8621
+ if (err) {
8622
+ const msg = stderr || err.message;
8623
+ if (msg.includes("not found") || err.code === "ENOENT") {
8624
+ reject(
8625
+ new GpcError(
8626
+ "keytool not found",
8627
+ "KEYTOOL_NOT_FOUND",
8628
+ 1,
8629
+ "Install a JDK (keytool ships with it) or add it to your PATH"
8630
+ )
8631
+ );
8632
+ return;
8633
+ }
8634
+ if (msg.includes("password was incorrect") || msg.includes("Keystore was tampered")) {
8635
+ reject(
8636
+ new GpcError(
8637
+ "Keystore password is incorrect or the keystore is corrupted",
8638
+ "KEYSTORE_PASSWORD_ERROR",
8639
+ 1,
8640
+ "Check --store-pass or GPC_STORE_PASSWORD"
8641
+ )
8642
+ );
8643
+ return;
8644
+ }
8645
+ reject(
8646
+ new GpcError(
8647
+ `keytool failed: ${msg}`,
8648
+ "KEYTOOL_ERROR",
8649
+ 1,
8650
+ "Check the keystore path and password"
8651
+ )
8652
+ );
8653
+ return;
8654
+ }
8655
+ resolve2(stdout2);
8656
+ });
8657
+ });
8658
+ const { sha256, alias } = parseKeytoolOutput(stdout);
8659
+ return { sha256, alias, keystorePath };
8660
+ }
8661
+ async function getApiSigningFingerprint(accessToken, packageName, apiHost = "androidpublisher.googleapis.com") {
8662
+ const baseUrl = `https://${apiHost}/androidpublisher/v3/applications/${encodeURIComponent(packageName)}`;
8663
+ const editResp = await fetch(`${baseUrl}/edits`, {
8664
+ method: "POST",
8665
+ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
8666
+ body: JSON.stringify({}),
8667
+ signal: AbortSignal.timeout(1e4)
8668
+ });
8669
+ if (!editResp.ok) return null;
8670
+ const edit = await editResp.json();
8671
+ try {
8672
+ const bundlesResp = await fetch(`${baseUrl}/edits/${edit.id}/bundles`, {
8673
+ headers: { Authorization: `Bearer ${accessToken}` },
8674
+ signal: AbortSignal.timeout(1e4)
8675
+ });
8676
+ if (!bundlesResp.ok) return null;
8677
+ const bundlesData = await bundlesResp.json();
8678
+ const bundles = bundlesData.bundles ?? [];
8679
+ if (bundles.length === 0) return null;
8680
+ const latest = bundles.reduce((max, b) => b.versionCode > max.versionCode ? b : max);
8681
+ const apksResp = await fetch(`${baseUrl}/generatedApks/${latest.versionCode}`, {
8682
+ headers: { Authorization: `Bearer ${accessToken}` },
8683
+ signal: AbortSignal.timeout(1e4)
8684
+ });
8685
+ if (!apksResp.ok) return null;
8686
+ const apksData = await apksResp.json();
8687
+ const fp = apksData.generatedApks?.[0]?.certificateSha256Fingerprint;
8688
+ if (!fp) return null;
8689
+ return { sha256: fp.toUpperCase(), versionCode: latest.versionCode };
8690
+ } finally {
8691
+ await fetch(`${baseUrl}/edits/${edit.id}`, {
8692
+ method: "DELETE",
8693
+ headers: { Authorization: `Bearer ${accessToken}` },
8694
+ signal: AbortSignal.timeout(5e3)
8695
+ }).catch(() => {
8696
+ });
8697
+ }
8698
+ }
8699
+ function compareFingerprints(a, b) {
8700
+ return normalizeFingerprint(a) === normalizeFingerprint(b);
8701
+ }
8702
+
8703
+ // src/signing-consistency.ts
8704
+ async function checkSigningConsistency(accessToken, packageName, apiHost = "androidpublisher.googleapis.com") {
8705
+ const baseUrl = `https://${apiHost}/androidpublisher/v3/applications/${encodeURIComponent(packageName)}`;
8706
+ const editResp = await fetch(`${baseUrl}/edits`, {
8707
+ method: "POST",
8708
+ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
8709
+ body: JSON.stringify({}),
8710
+ signal: AbortSignal.timeout(1e4)
8711
+ });
8712
+ if (!editResp.ok) {
8713
+ const body = await editResp.json().catch(() => ({}));
8714
+ throw new GpcError(
8715
+ `Failed to create edit: ${body.error?.message ?? `HTTP ${editResp.status}`}`,
8716
+ "EDIT_CREATE_FAILED",
8717
+ 4,
8718
+ "Check your credentials and app access permissions"
8719
+ );
8720
+ }
8721
+ const edit = await editResp.json();
8722
+ try {
8723
+ const bundlesResp = await fetch(`${baseUrl}/edits/${edit.id}/bundles`, {
8724
+ headers: { Authorization: `Bearer ${accessToken}` },
8725
+ signal: AbortSignal.timeout(1e4)
8726
+ });
8727
+ if (!bundlesResp.ok) {
8728
+ throw new GpcError(
8729
+ `Failed to list bundles: HTTP ${bundlesResp.status}`,
8730
+ "BUNDLES_LIST_FAILED",
8731
+ 4,
8732
+ "Check your API permissions for this app"
8733
+ );
8734
+ }
8735
+ const bundlesData = await bundlesResp.json();
8736
+ const bundles = (bundlesData.bundles ?? []).sort((a, b) => b.versionCode - a.versionCode);
8737
+ if (bundles.length === 0) {
8738
+ throw new GpcError(
8739
+ "No bundles found for this app",
8740
+ "NO_BUNDLES",
8741
+ 4,
8742
+ "Upload at least one AAB with 'gpc publish' before checking signing consistency"
8743
+ );
8744
+ }
8745
+ const currentVc = bundles[0].versionCode;
8746
+ const currentFp = await fetchFingerprint(baseUrl, accessToken, currentVc);
8747
+ if (bundles.length === 1) {
8748
+ return {
8749
+ currentVersionCode: currentVc,
8750
+ currentFingerprint: currentFp,
8751
+ consistent: true,
8752
+ firstRelease: true
8753
+ };
8754
+ }
8755
+ const previousVc = bundles[1].versionCode;
8756
+ const previousFp = await fetchFingerprint(baseUrl, accessToken, previousVc);
8757
+ const consistent = normalizeFingerprint(currentFp) === normalizeFingerprint(previousFp);
8758
+ return {
8759
+ currentVersionCode: currentVc,
8760
+ currentFingerprint: currentFp,
8761
+ previousVersionCode: previousVc,
8762
+ previousFingerprint: previousFp,
8763
+ consistent,
8764
+ firstRelease: false
8765
+ };
8766
+ } finally {
8767
+ await fetch(`${baseUrl}/edits/${edit.id}`, {
8768
+ method: "DELETE",
8769
+ headers: { Authorization: `Bearer ${accessToken}` },
8770
+ signal: AbortSignal.timeout(5e3)
8771
+ }).catch(() => {
8772
+ });
8773
+ }
8774
+ }
8775
+ async function fetchFingerprint(baseUrl, accessToken, versionCode) {
8776
+ const resp = await fetch(`${baseUrl}/generatedApks/${versionCode}`, {
8777
+ headers: { Authorization: `Bearer ${accessToken}` },
8778
+ signal: AbortSignal.timeout(1e4)
8779
+ });
8780
+ if (!resp.ok) {
8781
+ throw new GpcError(
8782
+ `Failed to get generated APKs for versionCode ${versionCode}: HTTP ${resp.status}`,
8783
+ "GENERATED_APKS_FAILED",
8784
+ 4,
8785
+ "Check your API permissions for this app"
8786
+ );
8787
+ }
8788
+ const data = await resp.json();
8789
+ const fp = data.generatedApks?.[0]?.certificateSha256Fingerprint;
8790
+ if (!fp) {
8791
+ throw new GpcError(
8792
+ `No signing certificate found for versionCode ${versionCode}`,
8793
+ "NO_SIGNING_CERT",
8794
+ 4,
8795
+ "The app may not be enrolled in Play App Signing"
8796
+ );
8797
+ }
8798
+ return fp.toUpperCase();
8799
+ }
8800
+
8801
+ // src/verify-checklist.ts
8802
+ var PLAY_CONSOLE_SETTINGS = "https://play.google.com/console/developers/settings";
8803
+ var VERIFICATION_PAGE = "https://developer.android.com/developer-verification";
8804
+ var PLAY_APP_SIGNING = "https://support.google.com/googleplay/android-developer/answer/9842756";
8805
+ function buildChecklist(input) {
8806
+ const items = [];
8807
+ const answers = input.interactiveAnswers ?? {};
8808
+ items.push({
8809
+ id: "account-active",
8810
+ title: "Play Console account active",
8811
+ status: input.authenticated ? "done" : "action-needed",
8812
+ detail: input.authenticated ? `Authenticated as ${input.accountEmail ?? "unknown"}` : "Could not authenticate with Google Play",
8813
+ actionUrl: input.authenticated ? void 0 : "https://play.google.com/console"
8814
+ });
8815
+ items.push(
8816
+ resolveManualStep(
8817
+ "identity-verified",
8818
+ "Identity verification complete",
8819
+ "Confirm your identity is verified under Developer Account in Play Console Settings",
8820
+ PLAY_CONSOLE_SETTINGS,
8821
+ answers
8822
+ )
8823
+ );
8824
+ items.push(
8825
+ resolveManualStep(
8826
+ "auto-registration-reviewed",
8827
+ "Auto-registration results reviewed",
8828
+ "Check the registration status banner above your app list in Play Console",
8829
+ PLAY_CONSOLE_SETTINGS,
8830
+ answers
8831
+ )
8832
+ );
8833
+ if (input.appAccessible !== void 0) {
8834
+ items.push({
8835
+ id: "app-accessible",
8836
+ title: "App accessible via API",
8837
+ status: input.appAccessible ? "done" : "action-needed",
8838
+ detail: input.appAccessible ? "App is reachable through the Play Developer API" : "Could not access app via API"
8839
+ });
8840
+ }
8841
+ if (input.bundleCount !== void 0) {
8842
+ items.push({
8843
+ id: "bundle-uploaded",
8844
+ title: "At least one bundle uploaded",
8845
+ status: input.bundleCount > 0 ? "done" : "action-needed",
8846
+ detail: input.bundleCount > 0 ? `${input.bundleCount} bundle${input.bundleCount !== 1 ? "s" : ""} on file` : "No bundles found. Upload an AAB with: gpc publish"
8847
+ });
8848
+ }
8849
+ if (input.hasGeneratedApks !== void 0) {
8850
+ items.push({
8851
+ id: "play-app-signing",
8852
+ title: "Play App Signing enrolled",
8853
+ status: input.hasGeneratedApks ? "done" : "action-needed",
8854
+ detail: input.hasGeneratedApks ? "Google manages your app signing key" : "App may not be enrolled in Play App Signing",
8855
+ actionUrl: input.hasGeneratedApks ? void 0 : PLAY_APP_SIGNING
8856
+ });
8857
+ }
8858
+ items.push(
8859
+ resolveManualStep(
8860
+ "additional-keys",
8861
+ "Additional signing keys registered",
8862
+ "If you distribute outside Play with a different key, register it in Play Console",
8863
+ VERIFICATION_PAGE,
8864
+ answers
8865
+ )
8866
+ );
8867
+ const completed = items.filter((i) => i.status === "done").length;
8868
+ return { items, completed, total: items.length };
8869
+ }
8870
+ function resolveManualStep(id, title, detail, actionUrl, answers) {
8871
+ if (id in answers) {
8872
+ return {
8873
+ id,
8874
+ title,
8875
+ status: answers[id] ? "done" : "action-needed",
8876
+ detail,
8877
+ actionUrl: answers[id] ? void 0 : actionUrl
8878
+ };
8879
+ }
8880
+ return { id, title, status: "cannot-detect", detail, actionUrl };
8881
+ }
8882
+ function renderChecklistMarkdown(result, accountEmail) {
8883
+ const lines = [
8884
+ "# Developer Verification Checklist",
8885
+ "",
8886
+ `Account: ${accountEmail}`,
8887
+ `Date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
8888
+ `Progress: ${result.completed}/${result.total}`,
8889
+ ""
8890
+ ];
8891
+ for (const item of result.items) {
8892
+ const check = item.status === "done" ? "x" : " ";
8893
+ lines.push(`- [${check}] ${item.title}`);
8894
+ if (item.detail) lines.push(` ${item.detail}`);
8895
+ if (item.actionUrl && item.status !== "done") lines.push(` ${item.actionUrl}`);
8896
+ }
8897
+ lines.push("");
8898
+ return lines.join("\n");
8899
+ }
8583
8900
  export {
8584
8901
  ApiError,
8585
8902
  ConfigError,
@@ -8612,16 +8929,19 @@ export {
8612
8929
  applyReleaseNotes,
8613
8930
  batchGetOrders,
8614
8931
  batchSyncInAppProducts,
8932
+ buildChecklist,
8615
8933
  buildLocaleBundle,
8616
8934
  bundleToReleaseNotes,
8617
8935
  cancelRecoveryAction,
8618
8936
  cancelSubscriptionPurchase,
8619
8937
  cancelSubscriptionV2,
8620
8938
  checkBundleSize,
8939
+ checkSigningConsistency,
8621
8940
  checkThreshold,
8622
8941
  classifyError,
8623
8942
  clearAuditLog,
8624
8943
  compareBundles,
8944
+ compareFingerprints,
8625
8945
  compareVersionVitals,
8626
8946
  compareVitalsTrend,
8627
8947
  computeStatusDiff,
@@ -8689,12 +9009,14 @@ export {
8689
9009
  generateMigrationPlan,
8690
9010
  generateNotesFromGit,
8691
9011
  getAllScannerNames,
9012
+ getApiSigningFingerprint,
8692
9013
  getAppInfo,
8693
9014
  getAppStatus,
8694
9015
  getCountryAvailability,
8695
9016
  getDeviceTier,
8696
9017
  getExternalTransaction,
8697
9018
  getInAppProduct,
9019
+ getKeystoreFingerprint,
8698
9020
  getListings,
8699
9021
  getOffer,
8700
9022
  getOneTimeOffer,
@@ -8758,10 +9080,12 @@ export {
8758
9080
  loadStatusCache,
8759
9081
  maybePaginate,
8760
9082
  migratePrices,
9083
+ normalizeFingerprint,
8761
9084
  parseAppfile,
8762
9085
  parseCommit,
8763
9086
  parseFastfile,
8764
9087
  parseGrantArg,
9088
+ parseKeytoolOutput,
8765
9089
  parseMonth,
8766
9090
  parseRemoteUrl,
8767
9091
  pauseTrain,
@@ -8779,6 +9103,7 @@ export {
8779
9103
  relativeTime,
8780
9104
  removeTesters,
8781
9105
  removeUser,
9106
+ renderChecklistMarkdown,
8782
9107
  renderJson,
8783
9108
  renderMarkdown,
8784
9109
  renderPlayStore,