@gpc-cli/cli 0.9.39 → 0.9.41

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.
Files changed (86) hide show
  1. package/dist/{anomalies-KRRA75MJ.js → anomalies-NU2IN2GJ.js} +4 -9
  2. package/dist/anomalies-NU2IN2GJ.js.map +1 -0
  3. package/dist/bin.js +2 -2
  4. package/dist/{chunk-ELZTOKWU.js → chunk-25HYD4WO.js} +29 -29
  5. package/dist/chunk-AA577WVQ.js +23 -0
  6. package/dist/chunk-AA577WVQ.js.map +1 -0
  7. package/dist/{config-36GIPNWX.js → config-222P3MKK.js} +1 -1
  8. package/dist/config-222P3MKK.js.map +1 -0
  9. package/dist/{data-safety-JR6PZ2BD.js → data-safety-L64D6ZZX.js} +5 -15
  10. package/dist/data-safety-L64D6ZZX.js.map +1 -0
  11. package/dist/{device-tiers-5SGJPSYG.js → device-tiers-UZVKC726.js} +5 -15
  12. package/dist/device-tiers-UZVKC726.js.map +1 -0
  13. package/dist/{diff-V7VBKG27.js → diff-V77SMKAQ.js} +5 -10
  14. package/dist/diff-V77SMKAQ.js.map +1 -0
  15. package/dist/{external-transactions-O5P4QBIT.js → external-transactions-6DWF5U3Q.js} +5 -15
  16. package/dist/external-transactions-6DWF5U3Q.js.map +1 -0
  17. package/dist/{feedback-TTMAF5I6.js → feedback-YIFDVXST.js} +11 -4
  18. package/dist/feedback-YIFDVXST.js.map +1 -0
  19. package/dist/{games-X57AGM3E.js → games-BT777WUO.js} +8 -13
  20. package/dist/games-BT777WUO.js.map +1 -0
  21. package/dist/{generated-apks-KB2PLWDI.js → generated-apks-RJWTIX7L.js} +5 -15
  22. package/dist/generated-apks-RJWTIX7L.js.map +1 -0
  23. package/dist/{iap-BNIAHBDN.js → iap-QTO5Q6SN.js} +5 -15
  24. package/dist/iap-QTO5Q6SN.js.map +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/{init-FXB25TPF.js → init-JZ2THPMS.js} +4 -4
  27. package/dist/init-JZ2THPMS.js.map +1 -0
  28. package/dist/{internal-sharing-M74VNIQ2.js → internal-sharing-DEOI3ENH.js} +5 -15
  29. package/dist/internal-sharing-DEOI3ENH.js.map +1 -0
  30. package/dist/{listings-JOFAZBKU.js → listings-NJWIKN4J.js} +5 -15
  31. package/dist/listings-NJWIKN4J.js.map +1 -0
  32. package/dist/{one-time-products-3WNXDKE3.js → one-time-products-6CQNNTBX.js} +5 -15
  33. package/dist/one-time-products-6CQNNTBX.js.map +1 -0
  34. package/dist/{pricing-OZO66DVL.js → pricing-XQSDTTK5.js} +5 -15
  35. package/dist/pricing-XQSDTTK5.js.map +1 -0
  36. package/dist/{publish-EPZXLGKZ.js → publish-OJJFPDRM.js} +4 -9
  37. package/dist/publish-OJJFPDRM.js.map +1 -0
  38. package/dist/{purchases-7ZPVCN6D.js → purchases-745IKIG5.js} +5 -15
  39. package/dist/purchases-745IKIG5.js.map +1 -0
  40. package/dist/{recovery-YGPOVUFD.js → recovery-BBF6NQ4C.js} +5 -15
  41. package/dist/recovery-BBF6NQ4C.js.map +1 -0
  42. package/dist/{releases-472GERCZ.js → releases-C2WC2K4E.js} +12 -9
  43. package/dist/releases-C2WC2K4E.js.map +1 -0
  44. package/dist/{reviews-MOVGATUI.js → reviews-2B5SKNXW.js} +5 -15
  45. package/dist/reviews-2B5SKNXW.js.map +1 -0
  46. package/dist/{status-SRG6RUCV.js → status-WHGLODGV.js} +4 -2
  47. package/dist/status-WHGLODGV.js.map +1 -0
  48. package/dist/{subscriptions-LSOJID6H.js → subscriptions-4XKNTOOL.js} +12 -17
  49. package/dist/subscriptions-4XKNTOOL.js.map +1 -0
  50. package/dist/{testers-SDLVWQ2Z.js → testers-3Y753PFW.js} +5 -15
  51. package/dist/testers-3Y753PFW.js.map +1 -0
  52. package/dist/{train-PX5Z26PQ.js → train-YA4KGVGZ.js} +4 -9
  53. package/dist/train-YA4KGVGZ.js.map +1 -0
  54. package/dist/{update-2ECQ2F5U.js → update-ISG6JXID.js} +2 -2
  55. package/dist/{version-KRDVPPQ4.js → version-BN45CRWB.js} +2 -2
  56. package/dist/{vitals-6EYDCGQK.js → vitals-A4CS4MSS.js} +4 -9
  57. package/dist/vitals-A4CS4MSS.js.map +1 -0
  58. package/package.json +4 -4
  59. package/dist/anomalies-KRRA75MJ.js.map +0 -1
  60. package/dist/config-36GIPNWX.js.map +0 -1
  61. package/dist/data-safety-JR6PZ2BD.js.map +0 -1
  62. package/dist/device-tiers-5SGJPSYG.js.map +0 -1
  63. package/dist/diff-V7VBKG27.js.map +0 -1
  64. package/dist/external-transactions-O5P4QBIT.js.map +0 -1
  65. package/dist/feedback-TTMAF5I6.js.map +0 -1
  66. package/dist/games-X57AGM3E.js.map +0 -1
  67. package/dist/generated-apks-KB2PLWDI.js.map +0 -1
  68. package/dist/iap-BNIAHBDN.js.map +0 -1
  69. package/dist/init-FXB25TPF.js.map +0 -1
  70. package/dist/internal-sharing-M74VNIQ2.js.map +0 -1
  71. package/dist/listings-JOFAZBKU.js.map +0 -1
  72. package/dist/one-time-products-3WNXDKE3.js.map +0 -1
  73. package/dist/pricing-OZO66DVL.js.map +0 -1
  74. package/dist/publish-EPZXLGKZ.js.map +0 -1
  75. package/dist/purchases-7ZPVCN6D.js.map +0 -1
  76. package/dist/recovery-YGPOVUFD.js.map +0 -1
  77. package/dist/releases-472GERCZ.js.map +0 -1
  78. package/dist/reviews-MOVGATUI.js.map +0 -1
  79. package/dist/status-SRG6RUCV.js.map +0 -1
  80. package/dist/subscriptions-LSOJID6H.js.map +0 -1
  81. package/dist/testers-SDLVWQ2Z.js.map +0 -1
  82. package/dist/train-PX5Z26PQ.js.map +0 -1
  83. package/dist/vitals-6EYDCGQK.js.map +0 -1
  84. /package/dist/{chunk-ELZTOKWU.js.map → chunk-25HYD4WO.js.map} +0 -0
  85. /package/dist/{update-2ECQ2F5U.js.map → update-ISG6JXID.js.map} +0 -0
  86. /package/dist/{version-KRDVPPQ4.js.map → version-BN45CRWB.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["// Named exports only. No default export.\n\nimport type { Command } from \"commander\";\nimport { initProject } from \"@gpc-cli/core\";\nimport { isInteractive, promptInput, promptSelect } from \"../prompt.js\";\nimport { green, dim, yellow } from \"../colors.js\";\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Scaffold project config, metadata, and CI templates\")\n .option(\"--app <package>\", \"Android package name (e.g. com.example.app)\")\n .option(\"--ci <platform>\", \"Generate CI template: github, gitlab\")\n .option(\"--force\", \"Overwrite existing files\")\n .action(async (options) => {\n let app = options[\"app\"] as string | undefined;\n let ci = options[\"ci\"] as \"github\" | \"gitlab\" | undefined;\n\n // Interactive prompts when TTY\n if (isInteractive(program) && !app) {\n app = await promptInput(\"Package name (e.g. com.example.app)\");\n if (!app) app = undefined;\n }\n\n if (isInteractive(program) && !ci) {\n const ciChoice = await promptSelect(\"Generate CI template?\", [\"none\", \"github\", \"gitlab\"]);\n if (ciChoice !== \"none\") ci = ciChoice as \"github\" | \"gitlab\";\n }\n\n if (ci && ci !== \"github\" && ci !== \"gitlab\") {\n console.error(`Error: Invalid --ci value \"${ci}\". Use: github, gitlab`);\n process.exit(2);\n }\n\n const result = await initProject({\n dir: process.cwd(),\n app: app || undefined,\n ci,\n skipExisting: !options[\"force\"],\n });\n\n if (result.created.length === 0 && result.skipped.length > 0) {\n console.log(yellow(\"⚠ All files already exist. Use --force to overwrite.\"));\n for (const f of result.skipped) {\n console.log(` ${dim(\"skip\")} ${f}`);\n }\n return;\n }\n\n for (const f of result.created) {\n console.log(` ${green(\"✓\")} ${f}`);\n }\n for (const f of result.skipped) {\n console.log(` ${dim(\"skip\")} ${f}`);\n }\n\n console.log(\"\");\n console.log(\n `${green(\"✓\")} Project initialized (${result.created.length} file${result.created.length === 1 ? \"\" : \"s\"} created)`,\n );\n\n if (result.created.some((f) => f.includes(\".gpcrc.json\"))) {\n console.log(dim(\"\\nNext steps:\"));\n console.log(dim(\" 1. gpc auth login --service-account path/to/key.json\"));\n console.log(dim(\" 2. gpc doctor\"));\n console.log(dim(\" 3. gpc status\"));\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAGA,SAAS,mBAAmB;AAIrB,SAAS,oBAAoB,SAAwB;AAC1D,UACG,QAAQ,MAAM,EACd,YAAY,qDAAqD,EACjE,OAAO,mBAAmB,6CAA6C,EACvE,OAAO,mBAAmB,sCAAsC,EAChE,OAAO,WAAW,0BAA0B,EAC5C,OAAO,OAAO,YAAY;AACzB,QAAI,MAAM,QAAQ,KAAK;AACvB,QAAI,KAAK,QAAQ,IAAI;AAGrB,QAAI,cAAc,OAAO,KAAK,CAAC,KAAK;AAClC,YAAM,MAAM,YAAY,qCAAqC;AAC7D,UAAI,CAAC,IAAK,OAAM;AAAA,IAClB;AAEA,QAAI,cAAc,OAAO,KAAK,CAAC,IAAI;AACjC,YAAM,WAAW,MAAM,aAAa,yBAAyB,CAAC,QAAQ,UAAU,QAAQ,CAAC;AACzF,UAAI,aAAa,OAAQ,MAAK;AAAA,IAChC;AAEA,QAAI,MAAM,OAAO,YAAY,OAAO,UAAU;AAC5C,cAAQ,MAAM,8BAA8B,EAAE,wBAAwB;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,YAAY;AAAA,MAC/B,KAAK,QAAQ,IAAI;AAAA,MACjB,KAAK,OAAO;AAAA,MACZ;AAAA,MACA,cAAc,CAAC,QAAQ,OAAO;AAAA,IAChC,CAAC;AAED,QAAI,OAAO,QAAQ,WAAW,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC5D,cAAQ,IAAI,OAAO,2DAAsD,CAAC;AAC1E,iBAAW,KAAK,OAAO,SAAS;AAC9B,gBAAQ,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE;AAAA,MACrC;AACA;AAAA,IACF;AAEA,eAAW,KAAK,OAAO,SAAS;AAC9B,cAAQ,IAAI,KAAK,MAAM,QAAG,CAAC,IAAI,CAAC,EAAE;AAAA,IACpC;AACA,eAAW,KAAK,OAAO,SAAS;AAC9B,cAAQ,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE;AAAA,IACrC;AAEA,YAAQ,IAAI,EAAE;AACd,YAAQ;AAAA,MACN,GAAG,MAAM,QAAG,CAAC,yBAAyB,OAAO,QAAQ,MAAM,QAAQ,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG;AAAA,IAC3G;AAEA,QAAI,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,aAAa,CAAC,GAAG;AACzD,cAAQ,IAAI,IAAI,eAAe,CAAC;AAChC,cAAQ,IAAI,IAAI,wDAAwD,CAAC;AACzE,cAAQ,IAAI,IAAI,iBAAiB,CAAC;AAClC,cAAQ,IAAI,IAAI,iBAAiB,CAAC;AAAA,IACpC;AAAA,EACF,CAAC;AACL;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/internal-sharing.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport { uploadInternalSharing, formatOutput, createSpinner } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerInternalSharingCommands(program: Command): void {\n const cmd = program\n .command(\"internal-sharing\")\n .description(\"Upload bundles or APKs for instant internal sharing\");\n\n cmd\n .command(\"upload <file>\")\n .description(\"Upload a bundle or APK for internal app sharing\")\n .option(\"--type <type>\", \"File type: bundle or apk (auto-detected from extension)\")\n .action(async (file: string, opts: { type?: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n const fileType = opts.type as \"bundle\" | \"apk\" | undefined;\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"internal-sharing upload\",\n action: \"upload for internal sharing\",\n target: file,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n const spinner = createSpinner(\"Uploading for internal sharing...\");\n if (!program.opts()[\"quiet\"] && process.stderr.isTTY) spinner.start();\n\n try {\n const result = await uploadInternalSharing(client, packageName, file, fileType);\n spinner.stop(\"Upload complete\");\n console.log(formatOutput(result, format));\n } catch (error) {\n spinner.fail(\"Upload failed\");\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":";;;;;;;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,uBAAuB,cAAc,qBAAqB;AAInE,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,UAAU,QAAmB;AAC1C,QAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,SAAO,gBAAgB,EAAE,KAAK,CAAC;AACjC;AAEO,SAAS,gCAAgC,SAAwB;AACtE,QAAM,MAAM,QACT,QAAQ,kBAAkB,EAC1B,YAAY,qDAAqD;AAEpE,MACG,QAAQ,eAAe,EACvB,YAAY,iDAAiD,EAC7D,OAAO,iBAAiB,yDAAyD,EACjF,OAAO,OAAO,MAAc,SAA4B;AACvD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,UAAM,WAAW,KAAK;AAEtB,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,UAAU,cAAc,mCAAmC;AACjE,QAAI,CAAC,QAAQ,KAAK,EAAE,OAAO,KAAK,QAAQ,OAAO,MAAO,SAAQ,MAAM;AAEpE,QAAI;AACF,YAAM,SAAS,MAAM,sBAAsB,QAAQ,aAAa,MAAM,QAAQ;AAC9E,cAAQ,KAAK,iBAAiB;AAC9B,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,KAAK,eAAe;AAC5B,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/listings.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { ImageType } from \"@gpc-cli/api\";\nimport {\n getListings,\n updateListing,\n deleteListing,\n pullListings,\n pushListings,\n diffListingsEnhanced,\n lintLocalListings,\n analyzeRemoteListings,\n listImages,\n uploadImage,\n deleteImage,\n exportImages,\n getCountryAvailability,\n formatOutput,\n createSpinner,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { isInteractive, requireOption, requireConfirm } from \"../prompt.js\";\nimport { green, red } from \"../colors.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nconst VALID_IMAGE_TYPES: ImageType[] = [\n \"phoneScreenshots\",\n \"sevenInchScreenshots\",\n \"tenInchScreenshots\",\n \"tvScreenshots\",\n \"wearScreenshots\",\n \"icon\",\n \"featureGraphic\",\n \"tvBanner\",\n];\n\nfunction validateImageType(type: string): ImageType {\n if (!VALID_IMAGE_TYPES.includes(type as ImageType)) {\n console.error(`Error: Invalid image type \"${type}\".`);\n console.error(`Valid types: ${VALID_IMAGE_TYPES.join(\", \")}`);\n process.exit(2);\n }\n return type as ImageType;\n}\n\nexport function registerListingsCommands(program: Command): void {\n const listings = program.command(\"listings\").description(\"Manage store listings and metadata\");\n\n // Get\n listings\n .command(\"get\")\n .description(\"Get store listing(s)\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getListings(client, packageName, options.lang);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Update\n listings\n .command(\"update\")\n .description(\"Update a store listing\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .option(\"--title <text>\", \"App title\")\n .option(\"--short <text>\", \"Short description\")\n .option(\"--full <text>\", \"Full description\")\n .option(\"--full-file <path>\", \"Read full description from file\")\n .option(\"--video <url>\", \"Video URL\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const interactive = isInteractive(program);\n\n options.lang = await requireOption(\n \"lang\",\n options.lang,\n {\n message: \"Language code (BCP 47):\",\n default: \"en-US\",\n },\n interactive,\n );\n const format = getOutputFormat(program, config);\n\n try {\n const data: Record<string, string> = {};\n if (options[\"title\"]) data[\"title\"] = options[\"title\"];\n if (options[\"short\"]) data[\"shortDescription\"] = options[\"short\"];\n if (options[\"full\"]) data[\"fullDescription\"] = options[\"full\"];\n if (options[\"fullFile\"]) {\n const { readFile } = await import(\"node:fs/promises\");\n data[\"fullDescription\"] = (await readFile(options[\"fullFile\"], \"utf-8\")).trimEnd();\n }\n if (options[\"video\"]) data[\"video\"] = options[\"video\"];\n\n if (Object.keys(data).length === 0) {\n console.error(\n \"Error: Provide at least one field to update (--title, --short, --full, --full-file, --video).\",\n );\n process.exit(2);\n }\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"listings update\",\n action: \"update listing for\",\n target: options.lang,\n details: data,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n const result = await updateListing(client, packageName, options.lang, data);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Delete\n listings\n .command(\"delete\")\n .description(\"Delete a store listing for a language\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const interactive = isInteractive(program);\n\n options.lang = await requireOption(\n \"lang\",\n options.lang,\n {\n message: \"Language code (BCP 47):\",\n },\n interactive,\n );\n\n await requireConfirm(`Delete listing for \"${options.lang}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"listings delete\",\n action: \"delete listing for\",\n target: options.lang,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await deleteListing(client, packageName, options.lang);\n console.log(`Listing for \"${options.lang}\" deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Pull\n listings\n .command(\"pull\")\n .description(\"Download listings to Fastlane-format directory\")\n .option(\"--dir <path>\", \"Target directory (default: metadata)\", \"metadata\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await pullListings(client, packageName, options.dir);\n console.log(\n formatOutput(\n {\n directory: options.dir,\n languages: result.listings.map((l) => l.language),\n count: result.listings.length,\n },\n format,\n ),\n );\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Push\n listings\n .command(\"push\")\n .description(\"Upload listings from Fastlane-format directory\")\n .option(\"--dir <path>\", \"Source directory (default: metadata)\", \"metadata\")\n .option(\"--force\", \"Push even if fields exceed character limits\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n const spinner = createSpinner(\"Pushing listings...\");\n if (!program.opts()[\"quiet\"] && process.stderr.isTTY) spinner.start();\n\n try {\n const dryRun = isDryRun(program);\n const result = await pushListings(client, packageName, options.dir, {\n dryRun,\n force: options.force,\n });\n spinner.stop(dryRun ? \"Dry-run complete (no changes made)\" : \"Listings pushed\");\n console.log(formatOutput(result, format));\n } catch (error) {\n spinner.fail(\"Push failed\");\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Diff (enhanced: --lang filter, word-level inline diff)\n listings\n .command(\"diff\")\n .description(\"Compare local Fastlane-format metadata against remote listings\")\n .option(\"--dir <path>\", \"Local metadata directory\", \"metadata\")\n .option(\"--lang <language>\", \"Filter diff to a specific language\")\n .option(\"--word-diff\", \"Show word-level inline diff for fullDescription\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const diffs = await diffListingsEnhanced(client, packageName, options.dir, {\n lang: options.lang,\n wordLevel: options.wordDiff,\n });\n\n if (diffs.length === 0) {\n if (format === \"json\") {\n console.log(formatOutput([], format));\n } else {\n console.log(\"No differences found.\");\n }\n return;\n }\n\n if (format === \"json\") {\n console.log(formatOutput(diffs, format));\n } else {\n for (const diff of diffs) {\n const charInfo = (diff as unknown as Record<string, unknown>)[\"chars\"]\n ? ` (${(diff as unknown as Record<string, unknown>)[\"chars\"]} chars)`\n : \"\";\n console.log(`[${diff.language}] ${diff.field}:${charInfo}`);\n if ((diff as unknown as Record<string, unknown>)[\"diffSummary\"]) {\n console.log(` ${(diff as unknown as Record<string, unknown>)[\"diffSummary\"]}`);\n } else {\n console.log(green(` + local: ${diff.local || \"(empty)\"}`));\n console.log(red(` - remote: ${diff.remote || \"(empty)\"}`));\n }\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Lint (local, no API)\n listings\n .command(\"lint\")\n .description(\"Lint local listing metadata for Play Store character limits (no API)\")\n .option(\"--dir <path>\", \"Metadata directory\", \"metadata\")\n .action(async (options) => {\n const format = getOutputFormat(program, await loadConfig());\n try {\n const results = await lintLocalListings(options.dir);\n if (format === \"json\") {\n console.log(formatOutput(results, format));\n return;\n }\n let exitCode = 0;\n for (const r of results) {\n console.log(`\\n[${r.language}] ${r.valid ? green(\"✓ valid\") : red(\"✗ over limit\")}`);\n const rows = r.fields.map((f) => ({\n field: f.field,\n chars: f.chars,\n limit: f.limit,\n pct: `${f.pct}%`,\n status: f.status === \"ok\" ? green(\"✓\") : f.status === \"warn\" ? \"⚠\" : red(\"✗\"),\n }));\n console.log(formatOutput(rows, \"table\"));\n if (!r.valid) exitCode = 1;\n }\n if (exitCode) process.exit(exitCode);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n // Analyze (live, fetches remote)\n listings\n .command(\"analyze\")\n .description(\"Analyze live Play Store listings for character limit compliance\")\n .option(\"--expected <locales>\", \"Comma-separated list of expected locale codes\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n const spinner = createSpinner(\"Fetching remote listings...\");\n if (!program.opts()[\"quiet\"] && process.stderr.isTTY) spinner.start();\n\n try {\n const expectedLocales = options.expected\n ? (options.expected as string).split(\",\").map((s: string) => s.trim())\n : undefined;\n const { results, missingLocales } = await analyzeRemoteListings(client, packageName, {\n expectedLocales,\n });\n spinner.stop(\"Done\");\n\n if (format === \"json\") {\n console.log(formatOutput({ results, missingLocales }, format));\n return;\n }\n\n let exitCode = 0;\n for (const r of results) {\n console.log(`\\n[${r.language}] ${r.valid ? green(\"✓ valid\") : red(\"✗ over limit\")}`);\n const rows = r.fields.map((f) => ({\n field: f.field,\n chars: f.chars,\n limit: f.limit,\n pct: `${f.pct}%`,\n status: f.status === \"ok\" ? green(\"✓\") : f.status === \"warn\" ? \"⚠\" : red(\"✗\"),\n }));\n console.log(formatOutput(rows, \"table\"));\n if (!r.valid) exitCode = 1;\n }\n\n if (missingLocales && missingLocales.length > 0) {\n console.log(`\\nMissing locales: ${missingLocales.join(\", \")}`);\n }\n\n if (exitCode) process.exit(exitCode);\n } catch (error) {\n spinner.fail(\"Analysis failed\");\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Images subcommand\n const images = listings.command(\"images\").description(\"Manage listing images\");\n\n // Images list\n images\n .command(\"list\")\n .description(\"List images for a language and type\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .option(\"--type <type>\", \"Image type\")\n .option(\"--limit <n>\", \"Maximum results to return\")\n .option(\"--next-page <token>\", \"Pagination token for next page\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const interactive = isInteractive(program);\n\n options.lang = await requireOption(\n \"lang\",\n options.lang,\n {\n message: \"Language code (BCP 47):\",\n default: \"en-US\",\n },\n interactive,\n );\n\n options.type = await requireOption(\n \"type\",\n options.type,\n {\n message: \"Image type:\",\n choices: VALID_IMAGE_TYPES as unknown as string[],\n },\n interactive,\n );\n\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n const imageType = validateImageType(options.type);\n\n try {\n const result = await listImages(client, packageName, options.lang, imageType);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Images upload\n images\n .command(\"upload <file>\")\n .description(\"Upload an image\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .option(\"--type <type>\", \"Image type\")\n .action(async (file: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const interactive = isInteractive(program);\n\n options.lang = await requireOption(\n \"lang\",\n options.lang,\n {\n message: \"Language code (BCP 47):\",\n default: \"en-US\",\n },\n interactive,\n );\n\n options.type = await requireOption(\n \"type\",\n options.type,\n {\n message: \"Image type:\",\n choices: VALID_IMAGE_TYPES as unknown as string[],\n },\n interactive,\n );\n\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n const imageType = validateImageType(options.type);\n\n const spinner = createSpinner(\"Uploading image...\");\n if (!program.opts()[\"quiet\"] && process.stderr.isTTY) spinner.start();\n\n try {\n const result = await uploadImage(client, packageName, options.lang, imageType, file);\n spinner.stop(\"Image uploaded\");\n console.log(formatOutput(result, format));\n } catch (error) {\n spinner.fail(\"Image upload failed\");\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Images delete\n images\n .command(\"delete\")\n .description(\"Delete an image\")\n .option(\"--lang <language>\", \"Language code (BCP 47)\")\n .option(\"--type <type>\", \"Image type\")\n .option(\"--id <imageId>\", \"Image ID to delete\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const interactive = isInteractive(program);\n\n options.lang = await requireOption(\n \"lang\",\n options.lang,\n {\n message: \"Language code (BCP 47):\",\n },\n interactive,\n );\n\n options.type = await requireOption(\n \"type\",\n options.type,\n {\n message: \"Image type:\",\n choices: VALID_IMAGE_TYPES as unknown as string[],\n },\n interactive,\n );\n\n options.id = await requireOption(\n \"id\",\n options.id,\n {\n message: \"Image ID to delete:\",\n },\n interactive,\n );\n\n await requireConfirm(`Delete image \"${options.id}\"?`, program);\n\n const client = await getClient(config);\n const imageType = validateImageType(options.type);\n\n try {\n await deleteImage(client, packageName, options.lang, imageType, options.id);\n console.log(`Image \"${options.id}\" deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Images export\n images\n .command(\"export\")\n .description(\"Export all images to a local directory\")\n .option(\"--dir <path>\", \"Output directory\", \"images\")\n .option(\"--lang <language>\", \"Language code (BCP 47) — export only this language\")\n .option(\"--type <type>\", \"Image type — export only this type\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n const exportOpts: { lang?: string; type?: ImageType } = {};\n if (options.lang) exportOpts.lang = options.lang;\n if (options.type) {\n exportOpts.type = validateImageType(options.type);\n }\n\n const spinner = createSpinner(\"Exporting images...\");\n if (!program.opts()[\"quiet\"] && process.stderr.isTTY) spinner.start();\n\n try {\n const result = await exportImages(client, packageName, options.dir, exportOpts);\n spinner.stop(\"Images exported\");\n console.log(formatOutput(result, format));\n } catch (error) {\n spinner.fail(\"Image export failed\");\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Availability\n listings\n .command(\"availability\")\n .description(\"Get country availability for a track\")\n .option(\"--track <track>\", \"Track name\", \"production\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getCountryAvailability(client, packageName, options.track);\n const countries = (result as unknown as Record<string, unknown>)[\"countryTargeting\"] as\n | unknown[]\n | undefined;\n if (\n format !== \"json\" &&\n (!countries || (Array.isArray(countries) && countries.length === 0)) &&\n Object.keys(result as object).length === 0\n ) {\n console.log(\"No availability data.\");\n return;\n }\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/one-time-products.ts"],"sourcesContent":["import type { GpcConfig } from \"@gpc-cli/config\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { OneTimeProduct } from \"@gpc-cli/api\";\nimport {\n listOneTimeProducts,\n getOneTimeProduct,\n createOneTimeProduct,\n updateOneTimeProduct,\n deleteOneTimeProduct,\n listOneTimeOffers,\n getOneTimeOffer,\n createOneTimeOffer,\n updateOneTimeOffer,\n deleteOneTimeOffer,\n diffOneTimeProduct,\n formatOutput,\n sortResults,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { requireConfirm } from \"../prompt.js\";\nimport { readJsonFile } from \"../json.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerOneTimeProductsCommands(program: Command): void {\n const otp = program\n .command(\"one-time-products\")\n .alias(\"otp\")\n .description(\"Manage one-time products and offers (modern OTP API)\");\n\n otp\n .command(\"list\")\n .description(\"List one-time products\")\n .option(\"--sort <field>\", \"Sort by field (prefix with - for descending)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await listOneTimeProducts(client, packageName);\n if (options.sort) {\n result.oneTimeProducts = sortResults(result.oneTimeProducts, options.sort);\n }\n const products = result.oneTimeProducts || [];\n if (format !== \"json\") {\n if (products.length === 0) {\n console.log(\"No one-time products found.\");\n return;\n }\n const summary = products.map((p: OneTimeProduct) => ({\n productId: p.productId,\n purchaseType: (p as unknown as Record<string, unknown>)[\"purchaseType\"] || \"-\",\n listings: p.listings ? Object.keys(p.listings).length : 0,\n firstTitle: p.listings ? Object.values(p.listings)[0]?.title || \"-\" : \"-\",\n }));\n console.log(formatOutput(summary, format));\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n otp\n .command(\"get <product-id>\")\n .description(\"Get a one-time product\")\n .action(async (productId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getOneTimeProduct(client, packageName, productId);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n otp\n .command(\"create\")\n .description(\"Create a one-time product from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with product data\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"one-time-products create\",\n action: \"create\",\n target: `one-time product from ${options.file}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const data = await readJsonFile(options.file);\n const result = await createOneTimeProduct(client, packageName, data as any);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n otp\n .command(\"update <product-id>\")\n .description(\"Update a one-time product from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with product data\")\n .option(\"--update-mask <fields>\", \"Comma-separated field mask\")\n .action(async (productId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"one-time-products update\",\n action: \"update\",\n target: productId,\n details: { file: options.file },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const data = await readJsonFile(options.file);\n const result = await updateOneTimeProduct(\n client,\n packageName,\n productId,\n data as any,\n options.updateMask,\n );\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n otp\n .command(\"delete <product-id>\")\n .description(\"Delete a one-time product\")\n .action(async (productId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Delete one-time product \"${productId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"one-time-products delete\",\n action: \"delete\",\n target: productId,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await deleteOneTimeProduct(client, packageName, productId);\n console.log(`One-time product ${productId} deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Offers ---\n const offers = otp.command(\"offers\").description(\"Manage one-time product offers\");\n\n offers\n .command(\"list <product-id>\")\n .description(\"List offers for a one-time product\")\n .option(\"--sort <field>\", \"Sort by field (prefix with - for descending)\")\n .action(async (productId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await listOneTimeOffers(client, packageName, productId);\n if (options.sort) {\n result.oneTimeOffers = sortResults(result.oneTimeOffers, options.sort);\n }\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n offers\n .command(\"get <product-id> <offer-id>\")\n .description(\"Get an offer for a one-time product\")\n .action(async (productId: string, offerId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getOneTimeOffer(client, packageName, productId, offerId);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n offers\n .command(\"create <product-id>\")\n .description(\"Create an offer from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with offer data\")\n .action(async (productId: string, options: { file: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"one-time-products offers create\",\n action: \"create offer for\",\n target: productId,\n details: { file: options.file },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const data = await readJsonFile(options.file);\n const result = await createOneTimeOffer(client, packageName, productId, data as any);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n offers\n .command(\"update <product-id> <offer-id>\")\n .description(\"Update an offer from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with offer data\")\n .option(\"--update-mask <fields>\", \"Comma-separated field mask\")\n .action(\n async (\n productId: string,\n offerId: string,\n options: { file: string; updateMask?: string },\n ) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"one-time-products offers update\",\n action: \"update offer\",\n target: `${productId}/${offerId}`,\n details: { file: options.file },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const data = await readJsonFile(options.file);\n const result = await updateOneTimeOffer(\n client,\n packageName,\n productId,\n offerId,\n data as any,\n options.updateMask,\n );\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n },\n );\n\n offers\n .command(\"delete <product-id> <offer-id>\")\n .description(\"Delete an offer\")\n .action(async (productId: string, offerId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Delete offer \"${offerId}\" for product \"${productId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"one-time-products offers delete\",\n action: \"delete offer\",\n target: `${productId}/${offerId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await deleteOneTimeOffer(client, packageName, productId, offerId);\n console.log(`Offer ${offerId} deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Diff ---\n otp\n .command(\"diff <product-id>\")\n .description(\"Compare local JSON file against remote one-time product\")\n .requiredOption(\"--file <path>\", \"Local JSON file to compare against remote\")\n .action(async (productId: string, options: { file: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const localData = (await readJsonFile(options.file)) as OneTimeProduct;\n const diffs = await diffOneTimeProduct(client, packageName, productId, localData);\n if (diffs.length === 0) {\n console.log(\"No differences found.\");\n } else {\n console.log(formatOutput(diffs, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/pricing.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport { convertRegionPrices, formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerPricingCommands(program: Command): void {\n const pricing = program.command(\"pricing\").description(\"Pricing and regional price conversion\");\n\n pricing\n .command(\"convert\")\n .description(\"Convert a price to all regional prices\")\n .option(\"--from <currency>\", \"Source currency code (e.g. USD)\")\n .option(\"--amount <number>\", \"Price amount (e.g. 4.99)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const { isInteractive, requireOption } = await import(\"../prompt.js\");\n const interactive = isInteractive(program);\n\n options.from = await requireOption(\n \"from\",\n options.from,\n {\n message: \"Source currency code (e.g. USD):\",\n default: \"USD\",\n },\n interactive,\n );\n\n options.amount = await requireOption(\n \"amount\",\n options.amount,\n {\n message: \"Price amount (e.g. 4.99):\",\n },\n interactive,\n );\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await convertRegionPrices(client, packageName, options.from, options.amount);\n if (format !== \"json\") {\n const prices = (result as unknown as Record<string, unknown>)[\"convertedRegionPrices\"] as\n | Record<string, Record<string, unknown>>\n | undefined;\n if (prices) {\n const rows = Object.entries(prices).map(([region, data]) => {\n const money = data[\"price\"] as Record<string, unknown> | undefined;\n const units = money?.[\"units\"] || \"0\";\n const nanos = String(money?.[\"nanos\"] || 0)\n .padStart(9, \"0\")\n .slice(0, 2);\n return {\n region,\n price: money\n ? `${units}.${nanos}`\n : data[\"priceMicros\"]\n ? String(Number(data[\"priceMicros\"]) / 1_000_000)\n : \"-\",\n currencyCode: (money?.[\"currencyCode\"] || data[\"currencyCode\"] || \"-\") as string,\n };\n });\n console.log(formatOutput(rows, format));\n } else {\n console.log(formatOutput(result, format));\n }\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n const { PlayApiError } = await import(\"@gpc-cli/api\");\n if (\n error instanceof PlayApiError &&\n (error.code === \"API_HTTP_400\" || error.code === \"API_EDIT_EXPIRED\")\n ) {\n console.error(\n \"Error: Price conversion is not available for this app. \" +\n \"Ensure the app has monetization configured in Google Play Console (at least one paid product or subscription).\",\n );\n } else {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n }\n process.exit(4);\n }\n });\n}\n"],"mappings":";;;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,qBAAqB,oBAAoB;AAGlD,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,UAAU,QAAmB;AAC1C,QAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,SAAO,gBAAgB,EAAE,KAAK,CAAC;AACjC;AAEO,SAAS,wBAAwB,SAAwB;AAC9D,QAAM,UAAU,QAAQ,QAAQ,SAAS,EAAE,YAAY,uCAAuC;AAE9F,UACG,QAAQ,SAAS,EACjB,YAAY,wCAAwC,EACpD,OAAO,qBAAqB,iCAAiC,EAC7D,OAAO,qBAAqB,0BAA0B,EACtD,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,EAAE,eAAe,cAAc,IAAI,MAAM,OAAO,sBAAc;AACpE,UAAM,cAAc,cAAc,OAAO;AAEzC,YAAQ,OAAO,MAAM;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAEA,YAAQ,SAAS,MAAM;AAAA,MACrB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,MACX;AAAA,MACA;AAAA,IACF;AACA,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,oBAAoB,QAAQ,aAAa,QAAQ,MAAM,QAAQ,MAAM;AAC1F,UAAI,WAAW,QAAQ;AACrB,cAAM,SAAU,OAA8C,uBAAuB;AAGrF,YAAI,QAAQ;AACV,gBAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM;AAC1D,kBAAM,QAAQ,KAAK,OAAO;AAC1B,kBAAM,QAAQ,QAAQ,OAAO,KAAK;AAClC,kBAAM,QAAQ,OAAO,QAAQ,OAAO,KAAK,CAAC,EACvC,SAAS,GAAG,GAAG,EACf,MAAM,GAAG,CAAC;AACb,mBAAO;AAAA,cACL;AAAA,cACA,OAAO,QACH,GAAG,KAAK,IAAI,KAAK,KACjB,KAAK,aAAa,IAChB,OAAO,OAAO,KAAK,aAAa,CAAC,IAAI,GAAS,IAC9C;AAAA,cACN,cAAe,QAAQ,cAAc,KAAK,KAAK,cAAc,KAAK;AAAA,YACpE;AAAA,UACF,CAAC;AACD,kBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,QACxC,OAAO;AACL,kBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,QAC1C;AAAA,MACF,OAAO;AACL,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,cAAc;AACpD,UACE,iBAAiB,iBAChB,MAAM,SAAS,kBAAkB,MAAM,SAAS,qBACjD;AACA,gBAAQ;AAAA,UACN;AAAA,QAEF;AAAA,MACF,OAAO;AACL,gBAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,MAClF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { appendFile, stat } from \"node:fs/promises\";\nimport type { GpcConfig, OutputFormat } from \"@gpc-cli/config\";\nimport type { Command } from \"commander\";\nimport { loadConfig, getCacheDir } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { RetryLogEntry } from \"@gpc-cli/api\";\nimport {\n publish,\n generateNotesFromGit,\n writeAuditLog,\n createAuditEntry,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport type { PublishResult, DryRunPublishResult } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun } from \"../dry-run.js\";\nimport { isInteractive, promptSelect, promptInput } from \"../prompt.js\";\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\n// ---------------------------------------------------------------------------\n// Output formatters\n// ---------------------------------------------------------------------------\n\nfunction formatChecks(checks: { name: string; passed: boolean; message: string }[]): string[] {\n return checks.map((c) => ` ${c.passed ? PASS : FAIL} ${c.message}`);\n}\n\nfunction formatValidationOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") {\n return formatOutput({ success: false, validation: result.validation }, format);\n }\n const lines = [\"Validation failed:\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatPublishOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const upload = result.upload;\n if (!upload) return formatOutput(result, format);\n const rollout =\n upload.status === \"inProgress\" && \"userFraction\" in upload\n ? ` (${Math.round(Number((upload as Record<string, unknown>)[\"userFraction\"]) * 100)}% rollout)`\n : \"\";\n\n const lines = [\"Published successfully\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n lines.push(\"\");\n lines.push(` versionCode ${upload.versionCode}`);\n lines.push(` track ${upload.track}`);\n lines.push(` status ${upload.status}${rollout}`);\n return lines.join(\"\\n\");\n}\n\nfunction formatDryRunOutput(result: DryRunPublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const lines = [\"Dry run — no changes made\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n\n const u = result.upload;\n lines.push(\"\");\n lines.push(` Track ${u.track}`);\n\n if (u.currentReleases.length === 0) {\n lines.push(` Current (no releases on this track)`);\n } else {\n for (const r of u.currentReleases) {\n const fraction =\n r.userFraction !== undefined ? ` (${Math.round(r.userFraction * 100)}%)` : \"\";\n lines.push(` Current ${r.versionCodes.join(\", \")} · ${r.status}${fraction}`);\n }\n }\n const plannedFraction =\n u.plannedRelease.userFraction !== undefined\n ? ` (${Math.round(u.plannedRelease.userFraction * 100)}%)`\n : \"\";\n lines.push(` Would be (new bundle) · ${u.plannedRelease.status}${plannedFraction}`);\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function registerPublishCommand(program: Command): void {\n program\n .command(\"publish <file>\")\n .description(\"Validate, upload, and release in one step\")\n .option(\"--track <track>\", \"Target track\", \"internal\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage (1-100)\")\n .option(\"--notes <text>\", \"Release notes (en-US)\")\n .option(\"--notes-dir <dir>\", \"Read release notes from directory (<dir>/<lang>.txt)\")\n .option(\"--notes-from-git\", \"Generate release notes from git commit history\")\n .option(\"--since <ref>\", \"Git ref to start from (tag, SHA) — used with --notes-from-git\")\n .option(\"--name <name>\", \"Release name\")\n .option(\"--mapping <file>\", \"ProGuard/R8 mapping file for deobfuscation\")\n .option(\"--retry-log <path>\", \"Write retry log entries to file (JSONL)\")\n .action(async (file: string, options) => {\n try {\n await stat(file);\n } catch {\n console.error(`Error: File not found: ${file}`);\n process.exit(2);\n }\n\n const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);\n if (noteSources.length > 1) {\n console.error(\n \"Error: Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.\",\n );\n process.exit(2);\n }\n\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n // Interactive mode: prompt for missing options\n if (isInteractive(program)) {\n if (!options.track || options.track === \"internal\") {\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n options.track = await promptSelect(\"Select track:\", tracks, \"internal\");\n }\n\n if (!options.rollout && options.track === \"production\") {\n const rolloutStr = await promptInput(\n \"Staged rollout percentage (1-100, blank for full)\",\n \"100\",\n );\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir && !options.notesFromGit) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n // Rollout range guard\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n console.error(\n `Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n );\n process.exit(2);\n }\n }\n\n // Resolve git-based release notes before calling publish\n if (options.notesFromGit) {\n const gitNotes = await generateNotesFromGit({ since: options.since });\n options.notes = gitNotes.text;\n if (gitNotes.truncated) {\n console.error(\n `${WARN} Release notes truncated to 500 characters (${gitNotes.commitCount} commits from ${gitNotes.since}).`,\n );\n }\n }\n\n let onRetry: ((entry: RetryLogEntry) => void) | undefined;\n if (options.retryLog) {\n onRetry = (entry: RetryLogEntry) => {\n appendFile(options.retryLog, JSON.stringify(entry) + \"\\n\").catch(() => {});\n };\n }\n\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const client = createApiClient({ auth, onRetry });\n\n if (isDryRun(program)) {\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n dryRun: true,\n });\n console.log(formatDryRunOutput(result as DryRunPublishResult, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n return;\n }\n\n const auditEntry = createAuditEntry(\n \"publish\",\n { file, track: options.track, rollout: options.rollout },\n packageName,\n );\n\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n });\n\n if (!result.upload) {\n console.log(formatValidationOutput(result as PublishResult, format));\n auditEntry.success = false;\n auditEntry.error = \"Validation failed\";\n process.exit(1);\n }\n\n console.log(formatPublishOutput(result as PublishResult, format));\n auditEntry.success = true;\n } catch (error) {\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,YAAY,YAAY;AAGjC,SAAS,YAAY,mBAAmB;AACxC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAMP,IAAM,OAAO;AACb,IAAM,OAAO;AACb,IAAM,OAAO;AAEb,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAMA,SAAS,aAAa,QAAwE;AAC5F,SAAO,OAAO,IAAI,CAAC,MAAM,KAAK,EAAE,SAAS,OAAO,IAAI,IAAI,EAAE,OAAO,EAAE;AACrE;AAEA,SAAS,uBAAuB,QAAuB,QAA8B;AACnF,MAAI,WAAW,SAAS;AACtB,WAAO,aAAa,EAAE,SAAS,OAAO,YAAY,OAAO,WAAW,GAAG,MAAM;AAAA,EAC/E;AACA,QAAM,QAAQ,CAAC,wBAAwB,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC;AAChF,aAAW,KAAK,OAAO,WAAW,UAAU;AAC1C,UAAM,KAAK,KAAK,IAAI,IAAI,CAAC,EAAE;AAAA,EAC7B;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,oBAAoB,QAAuB,QAA8B;AAChF,MAAI,WAAW,QAAS,QAAO,aAAa,QAAQ,MAAM;AAE1D,QAAM,SAAS,OAAO;AACtB,MAAI,CAAC,OAAQ,QAAO,aAAa,QAAQ,MAAM;AAC/C,QAAM,UACJ,OAAO,WAAW,gBAAgB,kBAAkB,SAChD,KAAK,KAAK,MAAM,OAAQ,OAAmC,cAAc,CAAC,IAAI,GAAG,CAAC,eAClF;AAEN,QAAM,QAAQ,CAAC,4BAA4B,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC;AACpF,aAAW,KAAK,OAAO,WAAW,UAAU;AAC1C,UAAM,KAAK,KAAK,IAAI,IAAI,CAAC,EAAE;AAAA,EAC7B;AACA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,mBAAmB,OAAO,WAAW,EAAE;AAClD,QAAM,KAAK,mBAAmB,OAAO,KAAK,EAAE;AAC5C,QAAM,KAAK,mBAAmB,OAAO,MAAM,GAAG,OAAO,EAAE;AACvD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,mBAAmB,QAA6B,QAA8B;AACrF,MAAI,WAAW,QAAS,QAAO,aAAa,QAAQ,MAAM;AAE1D,QAAM,QAAQ,CAAC,oCAA+B,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC;AACvF,aAAW,KAAK,OAAO,WAAW,UAAU;AAC1C,UAAM,KAAK,KAAK,IAAI,IAAI,CAAC,EAAE;AAAA,EAC7B;AAEA,QAAM,IAAI,OAAO;AACjB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,eAAe,EAAE,KAAK,EAAE;AAEnC,MAAI,EAAE,gBAAgB,WAAW,GAAG;AAClC,UAAM,KAAK,yCAAyC;AAAA,EACtD,OAAO;AACL,eAAW,KAAK,EAAE,iBAAiB;AACjC,YAAM,WACJ,EAAE,iBAAiB,SAAY,KAAK,KAAK,MAAM,EAAE,eAAe,GAAG,CAAC,OAAO;AAC7E,YAAM,KAAK,eAAe,EAAE,aAAa,KAAK,IAAI,CAAC,SAAM,EAAE,MAAM,GAAG,QAAQ,EAAE;AAAA,IAChF;AAAA,EACF;AACA,QAAM,kBACJ,EAAE,eAAe,iBAAiB,SAC9B,KAAK,KAAK,MAAM,EAAE,eAAe,eAAe,GAAG,CAAC,OACpD;AACN,QAAM,KAAK,iCAA8B,EAAE,eAAe,MAAM,GAAG,eAAe,EAAE;AACpF,SAAO,MAAM,KAAK,IAAI;AACxB;AAMO,SAAS,uBAAuB,SAAwB;AAC7D,UACG,QAAQ,gBAAgB,EACxB,YAAY,2CAA2C,EACvD,OAAO,mBAAmB,gBAAgB,UAAU,EACpD,OAAO,uBAAuB,mCAAmC,EACjE,OAAO,kBAAkB,uBAAuB,EAChD,OAAO,qBAAqB,sDAAsD,EAClF,OAAO,oBAAoB,gDAAgD,EAC3E,OAAO,iBAAiB,oEAA+D,EACvF,OAAO,iBAAiB,cAAc,EACtC,OAAO,oBAAoB,4CAA4C,EACvE,OAAO,sBAAsB,yCAAyC,EACtE,OAAO,OAAO,MAAc,YAAY;AACvC,QAAI;AACF,YAAM,KAAK,IAAI;AAAA,IACjB,QAAQ;AACN,cAAQ,MAAM,0BAA0B,IAAI,EAAE;AAC9C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,CAAC,QAAQ,OAAO,QAAQ,UAAU,QAAQ,YAAY,EAAE,OAAO,OAAO;AAC1F,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAG9C,QAAI,cAAc,OAAO,GAAG;AAC1B,UAAI,CAAC,QAAQ,SAAS,QAAQ,UAAU,YAAY;AAClD,cAAM,SAAS,CAAC,YAAY,SAAS,QAAQ,YAAY;AACzD,gBAAQ,QAAQ,MAAM,aAAa,iBAAiB,QAAQ,UAAU;AAAA,MACxE;AAEA,UAAI,CAAC,QAAQ,WAAW,QAAQ,UAAU,cAAc;AACtD,cAAM,aAAa,MAAM;AAAA,UACvB;AAAA,UACA;AAAA,QACF;AACA,YAAI,cAAc,eAAe,OAAO;AACtC,kBAAQ,UAAU;AAAA,QACpB;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,YAAY,CAAC,QAAQ,cAAc;AAChE,cAAM,QAAQ,MAAM,YAAY,sCAAsC;AACtE,YAAI,MAAO,SAAQ,QAAQ;AAAA,MAC7B;AAAA,IACF;AAGA,QAAI,QAAQ,YAAY,QAAW;AACjC,YAAM,UAAU,OAAO,QAAQ,OAAO;AACtC,UAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,KAAK,UAAU,KAAK;AAC7D,gBAAQ;AAAA,UACN,6DAA6D,QAAQ,OAAO;AAAA,QAC9E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,QAAI,QAAQ,cAAc;AACxB,YAAM,WAAW,MAAM,qBAAqB,EAAE,OAAO,QAAQ,MAAM,CAAC;AACpE,cAAQ,QAAQ,SAAS;AACzB,UAAI,SAAS,WAAW;AACtB,gBAAQ;AAAA,UACN,GAAG,IAAI,+CAA+C,SAAS,WAAW,iBAAiB,SAAS,KAAK;AAAA,QAC3G;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI,QAAQ,UAAU;AACpB,gBAAU,CAAC,UAAyB;AAClC,mBAAW,QAAQ,UAAU,KAAK,UAAU,KAAK,IAAI,IAAI,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,YAAY;AAAA,MAC7B,oBAAoB,OAAO,MAAM;AAAA,MACjC,WAAW,YAAY;AAAA,IACzB,CAAC;AACD,UAAM,SAAS,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAEhD,QAAI,SAAS,OAAO,GAAG;AACrB,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ,QAAQ,aAAa,MAAM;AAAA,UACtD,OAAO,QAAQ;AAAA,UACf,gBAAgB,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI;AAAA,UAC5D,OAAO,QAAQ;AAAA,UACf,UAAU,QAAQ;AAAA,UAClB,aAAa,QAAQ;AAAA,UACrB,aAAa,QAAQ;AAAA,UACrB,QAAQ;AAAA,QACV,CAAC;AACD,gBAAQ,IAAI,mBAAmB,QAA+B,MAAM,CAAC;AAAA,MACvE,SAAS,OAAO;AACd,gBAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAEA,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,EAAE,MAAM,OAAO,QAAQ,OAAO,SAAS,QAAQ,QAAQ;AAAA,MACvD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,aAAa,MAAM;AAAA,QACtD,OAAO,QAAQ;AAAA,QACf,gBAAgB,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI;AAAA,QAC5D,OAAO,QAAQ;AAAA,QACf,UAAU,QAAQ;AAAA,QAClB,aAAa,QAAQ;AAAA,QACrB,aAAa,QAAQ;AAAA,MACvB,CAAC;AAED,UAAI,CAAC,OAAO,QAAQ;AAClB,gBAAQ,IAAI,uBAAuB,QAAyB,MAAM,CAAC;AACnE,mBAAW,UAAU;AACrB,mBAAW,QAAQ;AACnB,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,cAAQ,IAAI,oBAAoB,QAAyB,MAAM,CAAC;AAChE,iBAAW,UAAU;AAAA,IACvB,SAAS,OAAO;AACd,iBAAW,UAAU;AACrB,iBAAW,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACxE,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB,UAAE;AACA,iBAAW,aAAa,KAAK,IAAI,IAAI,IAAI,KAAK,WAAW,SAAS,EAAE,QAAQ;AAC5E,oBAAc,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC1C;AAAA,EACF,CAAC;AACL;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/purchases.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport {\n getProductPurchase,\n acknowledgeProductPurchase,\n consumeProductPurchase,\n getSubscriptionPurchase,\n cancelSubscriptionPurchase,\n deferSubscriptionPurchase,\n revokeSubscriptionPurchase,\n refundSubscriptionV2,\n listVoidedPurchases,\n refundOrder,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isInteractive, requireOption, requireConfirm } from \"../prompt.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerPurchasesCommands(program: Command): void {\n const purchases = program.command(\"purchases\").description(\"Manage purchases and orders\");\n\n purchases\n .command(\"get <product-id> <token>\")\n .description(\"Get a product purchase\")\n .action(async (productId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getProductPurchase(client, packageName, productId, token);\n if (format !== \"json\") {\n const r = result as unknown as Record<string, unknown>;\n const row = {\n orderId: r[\"orderId\"] || \"-\",\n purchaseState: r[\"purchaseState\"] ?? \"-\",\n consumptionState: r[\"consumptionState\"] ?? \"-\",\n purchaseTime: r[\"purchaseTimeMillis\"]\n ? new Date(Number(r[\"purchaseTimeMillis\"])).toISOString()\n : \"-\",\n acknowledged: r[\"acknowledgementState\"] ?? \"-\",\n };\n console.log(formatOutput(row, format));\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n purchases\n .command(\"acknowledge <product-id> <token>\")\n .description(\"Acknowledge a product purchase\")\n .option(\"--payload <text>\", \"Developer payload\")\n .action(async (productId: string, token: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases acknowledge\",\n action: \"acknowledge\",\n target: `${productId}/${token}`,\n details: { payload: options.payload },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);\n console.log(`Purchase acknowledged.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n purchases\n .command(\"consume <product-id> <token>\")\n .description(\"Consume a product purchase\")\n .action(async (productId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases consume\",\n action: \"consume\",\n target: `${productId}/${token}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await consumeProductPurchase(client, packageName, productId, token);\n console.log(`Purchase consumed.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Subscription purchases ---\n const sub = purchases.command(\"subscription\").description(\"Manage subscription purchases\");\n\n sub\n .command(\"get <token>\")\n .description(\"Get a subscription purchase (v2)\")\n .action(async (token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getSubscriptionPurchase(client, packageName, token);\n if (format !== \"json\") {\n const r = result as unknown as Record<string, unknown>;\n const lineItems = r[\"lineItems\"] as Record<string, unknown>[] | undefined;\n const row = {\n subscriptionState: r[\"subscriptionState\"] || \"-\",\n startTime: r[\"startTime\"] || \"-\",\n expiryTime: r[\"expiryTime\"] || \"-\",\n linkedPurchaseToken: r[\"linkedPurchaseToken\"] ? \"yes\" : \"no\",\n lineItems: lineItems?.length || 0,\n acknowledgement: r[\"acknowledgementState\"] || \"-\",\n };\n console.log(formatOutput(row, format));\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"cancel <subscription-id> <token>\")\n .description(\"Cancel a subscription (v1)\")\n .action(async (subscriptionId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases subscription cancel\",\n action: \"cancel subscription\",\n target: `${subscriptionId}/${token}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);\n console.log(`Subscription cancelled.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"defer <subscription-id> <token>\")\n .description(\"Defer a subscription expiry\")\n .option(\"--expiry <iso-date>\", \"Desired new expiry date (ISO 8601)\")\n .action(async (subscriptionId: string, token: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n const interactive = isInteractive(program);\n\n options.expiry = await requireOption(\n \"expiry\",\n options.expiry,\n {\n message: \"New expiry date (ISO 8601, e.g. 2026-12-31T23:59:59Z):\",\n },\n interactive,\n );\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"purchases subscription defer\",\n action: \"defer subscription\",\n target: `${subscriptionId}/${token}`,\n details: { expiry: options.expiry },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await deferSubscriptionPurchase(\n client,\n packageName,\n subscriptionId,\n token,\n options.expiry,\n );\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"refund <token>\")\n .description(\"Refund a subscription purchase (v2)\")\n .action(async (token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Refund subscription for token \"${token.slice(0, 16)}...\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases subscription refund\",\n action: \"refund subscription\",\n target: token,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await refundSubscriptionV2(client, packageName, token);\n console.log(`Subscription refunded.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"revoke <token>\")\n .description(\"Revoke a subscription (v2)\")\n .action(async (token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases subscription revoke\",\n action: \"revoke subscription\",\n target: token,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await revokeSubscriptionPurchase(client, packageName, token);\n console.log(`Subscription revoked.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Voided purchases ---\n purchases\n .command(\"voided\")\n .description(\"List voided purchases\")\n .option(\"--start-time <time>\", \"Start time (milliseconds)\")\n .option(\"--end-time <time>\", \"End time (milliseconds)\")\n .addOption(\n new Option(\"--max-results <n>\", \"Maximum results per page\").argParser(parseInt).hideHelp(),\n )\n .option(\"--limit <n>\", \"Maximum total results\", parseInt)\n .option(\"--next-page <token>\", \"Resume from page token\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await listVoidedPurchases(client, packageName, {\n startTime: options.startTime,\n endTime: options.endTime,\n maxResults: options.maxResults,\n limit: options.limit,\n nextPage: options.nextPage,\n });\n if (format !== \"json\") {\n const purchases = (result as Record<string, unknown>)[\"voidedPurchases\"] as\n | Record<string, unknown>[]\n | undefined;\n if (purchases && purchases.length > 0) {\n const rows = purchases.map((p) => ({\n orderId: p[\"orderId\"] || \"-\",\n purchaseToken: String(p[\"purchaseToken\"] || \"-\").slice(0, 16) + \"...\",\n voidedTime: p[\"voidedTimeMillis\"]\n ? new Date(Number(p[\"voidedTimeMillis\"])).toISOString()\n : \"-\",\n voidedSource: p[\"voidedSource\"] ?? \"-\",\n voidedReason: p[\"voidedReason\"] ?? \"-\",\n }));\n console.log(formatOutput(rows, format));\n } else {\n console.log(\"No voided purchases found.\");\n }\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Orders ---\n const orders = purchases.command(\"orders\").description(\"Manage orders\");\n\n orders\n .command(\"refund <order-id>\")\n .description(\"Refund an order\")\n .option(\"--full-refund\", \"Full refund\")\n .option(\"--prorated-refund\", \"Prorated refund\")\n .action(async (orderId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Refund order \"${orderId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases orders refund\",\n action: \"refund\",\n target: orderId,\n details: { fullRefund: options.fullRefund, proratedRefund: options.proratedRefund },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await refundOrder(client, packageName, orderId, {\n fullRefund: options.fullRefund,\n proratedRefund: options.proratedRefund,\n });\n console.log(`Order ${orderId} refunded.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;AACA,SAAS,cAAc;AAEvB,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAKP,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,UAAU,QAAmB;AAC1C,QAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,SAAO,gBAAgB,EAAE,KAAK,CAAC;AACjC;AAEO,SAAS,0BAA0B,SAAwB;AAChE,QAAM,YAAY,QAAQ,QAAQ,WAAW,EAAE,YAAY,6BAA6B;AAExF,YACG,QAAQ,0BAA0B,EAClC,YAAY,wBAAwB,EACpC,OAAO,OAAO,WAAmB,UAAkB;AAClD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,QAAQ,aAAa,WAAW,KAAK;AAC7E,UAAI,WAAW,QAAQ;AACrB,cAAM,IAAI;AACV,cAAM,MAAM;AAAA,UACV,SAAS,EAAE,SAAS,KAAK;AAAA,UACzB,eAAe,EAAE,eAAe,KAAK;AAAA,UACrC,kBAAkB,EAAE,kBAAkB,KAAK;AAAA,UAC3C,cAAc,EAAE,oBAAoB,IAChC,IAAI,KAAK,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,YAAY,IACtD;AAAA,UACJ,cAAc,EAAE,sBAAsB,KAAK;AAAA,QAC7C;AACA,gBAAQ,IAAI,aAAa,KAAK,MAAM,CAAC;AAAA,MACvC,OAAO;AACL,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,YACG,QAAQ,kCAAkC,EAC1C,YAAY,gCAAgC,EAC5C,OAAO,oBAAoB,mBAAmB,EAC9C,OAAO,OAAO,WAAmB,OAAe,YAAY;AAC3D,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,GAAG,SAAS,IAAI,KAAK;AAAA,UAC7B,SAAS,EAAE,SAAS,QAAQ,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,2BAA2B,QAAQ,aAAa,WAAW,OAAO,QAAQ,OAAO;AACvF,cAAQ,IAAI,wBAAwB;AAAA,IACtC,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,YACG,QAAQ,8BAA8B,EACtC,YAAY,4BAA4B,EACxC,OAAO,OAAO,WAAmB,UAAkB;AAClD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,GAAG,SAAS,IAAI,KAAK;AAAA,QAC/B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,uBAAuB,QAAQ,aAAa,WAAW,KAAK;AAClE,cAAQ,IAAI,oBAAoB;AAAA,IAClC,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,QAAM,MAAM,UAAU,QAAQ,cAAc,EAAE,YAAY,+BAA+B;AAEzF,MACG,QAAQ,aAAa,EACrB,YAAY,kCAAkC,EAC9C,OAAO,OAAO,UAAkB;AAC/B,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,wBAAwB,QAAQ,aAAa,KAAK;AACvE,UAAI,WAAW,QAAQ;AACrB,cAAM,IAAI;AACV,cAAM,YAAY,EAAE,WAAW;AAC/B,cAAM,MAAM;AAAA,UACV,mBAAmB,EAAE,mBAAmB,KAAK;AAAA,UAC7C,WAAW,EAAE,WAAW,KAAK;AAAA,UAC7B,YAAY,EAAE,YAAY,KAAK;AAAA,UAC/B,qBAAqB,EAAE,qBAAqB,IAAI,QAAQ;AAAA,UACxD,WAAW,WAAW,UAAU;AAAA,UAChC,iBAAiB,EAAE,sBAAsB,KAAK;AAAA,QAChD;AACA,gBAAQ,IAAI,aAAa,KAAK,MAAM,CAAC;AAAA,MACvC,OAAO;AACL,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,kCAAkC,EAC1C,YAAY,4BAA4B,EACxC,OAAO,OAAO,gBAAwB,UAAkB;AACvD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,GAAG,cAAc,IAAI,KAAK;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,2BAA2B,QAAQ,aAAa,gBAAgB,KAAK;AAC3E,cAAQ,IAAI,yBAAyB;AAAA,IACvC,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,iCAAiC,EACzC,YAAY,6BAA6B,EACzC,OAAO,uBAAuB,oCAAoC,EAClE,OAAO,OAAO,gBAAwB,OAAe,YAAY;AAChE,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,UAAM,cAAc,cAAc,OAAO;AAEzC,YAAQ,SAAS,MAAM;AAAA,MACrB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAEA,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,GAAG,cAAc,IAAI,KAAK;AAAA,UAClC,SAAS,EAAE,QAAQ,QAAQ,OAAO;AAAA,QACpC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AACA,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,gBAAgB,EACxB,YAAY,qCAAqC,EACjD,OAAO,OAAO,UAAkB;AAC/B,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,UAAM,eAAe,kCAAkC,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,OAAO;AAEzF,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,qBAAqB,QAAQ,aAAa,KAAK;AACrD,cAAQ,IAAI,wBAAwB;AAAA,IACtC,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,gBAAgB,EACxB,YAAY,4BAA4B,EACxC,OAAO,OAAO,UAAkB;AAC/B,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,2BAA2B,QAAQ,aAAa,KAAK;AAC3D,cAAQ,IAAI,uBAAuB;AAAA,IACrC,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,YACG,QAAQ,QAAQ,EAChB,YAAY,uBAAuB,EACnC,OAAO,uBAAuB,2BAA2B,EACzD,OAAO,qBAAqB,yBAAyB,EACrD;AAAA,IACC,IAAI,OAAO,qBAAqB,0BAA0B,EAAE,UAAU,QAAQ,EAAE,SAAS;AAAA,EAC3F,EACC,OAAO,eAAe,yBAAyB,QAAQ,EACvD,OAAO,uBAAuB,wBAAwB,EACtD,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,oBAAoB,QAAQ,aAAa;AAAA,QAC5D,WAAW,QAAQ;AAAA,QACnB,SAAS,QAAQ;AAAA,QACjB,YAAY,QAAQ;AAAA,QACpB,OAAO,QAAQ;AAAA,QACf,UAAU,QAAQ;AAAA,MACpB,CAAC;AACD,UAAI,WAAW,QAAQ;AACrB,cAAMA,aAAa,OAAmC,iBAAiB;AAGvE,YAAIA,cAAaA,WAAU,SAAS,GAAG;AACrC,gBAAM,OAAOA,WAAU,IAAI,CAAC,OAAO;AAAA,YACjC,SAAS,EAAE,SAAS,KAAK;AAAA,YACzB,eAAe,OAAO,EAAE,eAAe,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI;AAAA,YAChE,YAAY,EAAE,kBAAkB,IAC5B,IAAI,KAAK,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,YAAY,IACpD;AAAA,YACJ,cAAc,EAAE,cAAc,KAAK;AAAA,YACnC,cAAc,EAAE,cAAc,KAAK;AAAA,UACrC,EAAE;AACF,kBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,QACxC,OAAO;AACL,kBAAQ,IAAI,4BAA4B;AAAA,QAC1C;AAAA,MACF,OAAO;AACL,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,QAAM,SAAS,UAAU,QAAQ,QAAQ,EAAE,YAAY,eAAe;AAEtE,SACG,QAAQ,mBAAmB,EAC3B,YAAY,iBAAiB,EAC7B,OAAO,iBAAiB,aAAa,EACrC,OAAO,qBAAqB,iBAAiB,EAC7C,OAAO,OAAO,SAAiB,YAAY;AAC1C,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AAEpE,UAAM,eAAe,iBAAiB,OAAO,MAAM,OAAO;AAE1D,QAAI,SAAS,OAAO,GAAG;AACrB,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS,EAAE,YAAY,QAAQ,YAAY,gBAAgB,QAAQ,eAAe;AAAA,QACpF;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,YAAY,QAAQ,aAAa,SAAS;AAAA,QAC9C,YAAY,QAAQ;AAAA,QACpB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AACD,cAAQ,IAAI,SAAS,OAAO,YAAY;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;","names":["purchases"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/recovery.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport {\n listRecoveryActions,\n cancelRecoveryAction,\n deployRecoveryAction,\n createRecoveryAction,\n addRecoveryTargeting,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { getOutputFormat } from \"../format.js\";\nimport { requireConfirm } from \"../prompt.js\";\nimport { readFileSync } from \"node:fs\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerRecoveryCommands(program: Command): void {\n const recovery = program.command(\"recovery\").description(\"Manage app recovery actions\");\n\n recovery\n .command(\"list\")\n .description(\"List app recovery actions\")\n .option(\"--version-code <code>\", \"Filter by version code\", parseInt)\n .option(\"--limit <n>\", \"Maximum results to return\")\n .option(\"--next-page <token>\", \"Pagination token for next page\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n if (options.versionCode === undefined) {\n console.error(\n \"Error: --version-code is required. The API requires a version code to filter recovery actions.\",\n );\n console.error(\"Usage: gpc recovery list --version-code <code>\");\n process.exit(2);\n }\n const result = await listRecoveryActions(client, packageName, options.versionCode);\n if (Array.isArray(result) && result.length === 0) {\n if (format === \"json\") {\n console.log(formatOutput([], format));\n } else {\n console.log(\"No recovery actions found.\");\n }\n return;\n }\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n recovery\n .command(\"cancel <id>\")\n .description(\"Cancel a recovery action\")\n .action(async (id: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"recovery cancel\",\n action: \"cancel\",\n target: id,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n await requireConfirm(`Cancel recovery action ${id}?`, program);\n\n const client = await getClient(config);\n\n try {\n await cancelRecoveryAction(client, packageName, id);\n console.log(\n formatOutput({ success: true, appRecoveryId: id, action: \"cancelled\" }, format),\n );\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n recovery\n .command(\"deploy <id>\")\n .description(\"Deploy a recovery action\")\n .action(async (id: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"recovery deploy\",\n action: \"deploy\",\n target: id,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n await requireConfirm(`Deploy recovery action ${id}?`, program);\n\n const client = await getClient(config);\n\n try {\n await deployRecoveryAction(client, packageName, id);\n console.log(formatOutput({ success: true, appRecoveryId: id, action: \"deployed\" }, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n recovery\n .command(\"create\")\n .description(\"Create a new app recovery action\")\n .requiredOption(\"--file <path>\", \"Path to JSON file with recovery action data\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(readFileSync(options.file, \"utf-8\"));\n } catch (err) {\n console.error(\n `Error: Could not read recovery action data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exit(2);\n }\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"recovery create\",\n action: \"create recovery action\",\n target: packageName,\n details: data,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await createRecoveryAction(client, packageName, data);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n recovery\n .command(\"add-targeting <action-id>\")\n .description(\"Add targeting rules to an existing recovery action\")\n .requiredOption(\"--file <path>\", \"Path to JSON file with targeting data\")\n .action(async (actionId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n let targeting: Record<string, unknown>;\n try {\n targeting = JSON.parse(readFileSync(options.file, \"utf-8\"));\n } catch (err) {\n console.error(\n `Error: Could not read targeting data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exit(2);\n }\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"recovery add-targeting\",\n action: \"add targeting to recovery action\",\n target: actionId,\n details: targeting,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await addRecoveryTargeting(client, packageName, actionId, targeting);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/releases.ts"],"sourcesContent":["import { appendFile, stat } from \"node:fs/promises\";\nimport { basename, extname } from \"node:path\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient, createReportingClient } from \"@gpc-cli/api\";\nimport type { RetryLogEntry, ExternallyHostedApk, UploadProgressEvent } from \"@gpc-cli/api\";\nimport {\n uploadRelease,\n getReleasesStatus,\n promoteRelease,\n updateRollout,\n readReleaseNotesFromDir,\n generateNotesFromGit,\n writeAuditLog,\n createAuditEntry,\n uploadExternallyHosted,\n diffReleases,\n fetchReleaseNotes,\n getVitalsCrashes,\n checkThreshold,\n} from \"@gpc-cli/core\";\nimport { formatOutput, sortResults, createSpinner } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport {\n isInteractive,\n promptSelect,\n promptInput,\n requireOption,\n requireConfirm,\n} from \"../prompt.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nfunction createRetryLogger(retryLogPath?: string): ((entry: RetryLogEntry) => void) | undefined {\n if (!retryLogPath) return undefined;\n return (entry: RetryLogEntry) => {\n const line = JSON.stringify(entry) + \"\\n\";\n appendFile(retryLogPath, line).catch(() => {});\n };\n}\n\nasync function getClient(config: GpcConfig, retryLogPath?: string, uploadTimeout?: number) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth, onRetry: createRetryLogger(retryLogPath), uploadTimeout });\n}\n\nexport function registerReleasesCommands(program: Command): void {\n const releases = program.command(\"releases\").description(\"Manage releases and rollouts\");\n\n // Upload\n releases\n .command(\"upload <file>\")\n .description(\"Upload AAB/APK and assign to a track\")\n .option(\"--track <track>\", \"Target track\", \"internal\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage (1-100)\")\n .option(\"--notes <text>\", \"Release notes (en-US)\")\n .option(\"--name <name>\", \"Release name\")\n .option(\"--mapping <file>\", \"ProGuard/R8 mapping file for deobfuscation\")\n .option(\"--notes-dir <dir>\", \"Read release notes from directory (<dir>/<lang>.txt)\")\n .option(\"--notes-from-git\", \"Generate release notes from git commit history\")\n .option(\"--copy-notes-from <track>\", \"Copy release notes from another track\")\n .option(\"--since <ref>\", \"Git ref to start from (tag, SHA) — used with --notes-from-git\")\n .option(\"--retry-log <path>\", \"Write retry log entries to file (JSONL)\")\n .option(\n \"--timeout <ms>\",\n \"Upload timeout in milliseconds (auto-scales with file size by default)\",\n parseInt,\n )\n .action(async (file: string, options) => {\n try {\n await stat(file);\n } catch {\n console.error(`Error: File not found: ${file}`);\n process.exit(2);\n }\n\n const ext = extname(file).toLowerCase();\n if (ext !== \".aab\" && ext !== \".apk\") {\n console.error(`Error: Expected .aab or .apk file, got \"${ext || \"(no extension)\"}\"`);\n process.exit(2);\n }\n\n const noteSources = [\n options.notes,\n options.notesDir,\n options.notesFromGit,\n options.copyNotesFrom,\n ].filter(Boolean);\n if (noteSources.length > 1) {\n console.error(\n \"Error: Cannot combine --notes, --notes-dir, --notes-from-git, and --copy-notes-from. Use only one.\",\n );\n process.exit(2);\n }\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n // Interactive mode: prompt for missing options\n if (isInteractive(program)) {\n if (!options.track || options.track === \"internal\") {\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n options.track = await promptSelect(\"Select track:\", tracks, \"internal\");\n }\n\n if (!options.rollout && options.track === \"production\") {\n const rolloutStr = await promptInput(\n \"Staged rollout percentage (1-100, blank for full)\",\n \"100\",\n );\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n console.error(\n `Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n );\n process.exit(2);\n }\n }\n\n const { size: fileSize } = await stat(file);\n const jsonMode = format === \"json\";\n const client = await getClient(config, options.retryLog, options.timeout);\n\n const showProgress = !jsonMode && process.stderr.isTTY && !program.opts()[\"quiet\"];\n const sizeMB = (fileSize / (1024 * 1024)).toFixed(1);\n\n function formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n }\n\n const BAR_WIDTH = 20;\n const onUploadProgress = showProgress\n ? (event: UploadProgressEvent) => {\n const filled = Math.round((event.percent / 100) * BAR_WIDTH);\n const bar = \"█\".repeat(filled) + \"░\".repeat(BAR_WIDTH - filled);\n const uploaded = formatBytes(event.bytesUploaded);\n const total = formatBytes(event.totalBytes);\n const speed =\n event.bytesPerSecond > 0 ? `${formatBytes(event.bytesPerSecond)}/s` : \"...\";\n const eta = event.etaSeconds > 0 ? `ETA ${event.etaSeconds}s` : \"\";\n process.stderr.write(\n `\\r ${bar} ${event.percent}% ${uploaded}/${total} ${speed} ${eta}\\x1b[K`,\n );\n }\n : undefined;\n\n if (isDryRun(program)) {\n try {\n const result = await uploadRelease(client, packageName, file, {\n track: options.track,\n userFraction: options.rollout ? Number(options.rollout) / 100 : undefined,\n dryRun: true,\n });\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n return;\n }\n\n const auditEntry = createAuditEntry(\n \"releases upload\",\n {\n file,\n track: options.track,\n rollout: options.rollout,\n },\n packageName,\n );\n\n const spinner = createSpinner(`Uploading ${basename(file)} (${sizeMB} MB)...`);\n if (!showProgress) spinner.start();\n\n try {\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.copyNotesFrom) {\n releaseNotes = await fetchReleaseNotes(client, packageName, options.copyNotesFrom);\n } else if (options.notesFromGit) {\n const gitNotes = await generateNotesFromGit({ since: options.since });\n releaseNotes = [{ language: gitNotes.language, text: gitNotes.text }];\n } else if (options.notesDir) {\n releaseNotes = await readReleaseNotesFromDir(options.notesDir);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n const result = await uploadRelease(client, packageName, file, {\n track: options.track,\n userFraction: options.rollout ? Number(options.rollout) / 100 : undefined,\n releaseNotes,\n releaseName: options.name,\n mappingFile: options.mapping,\n onUploadProgress,\n });\n if (showProgress) {\n process.stderr.write(`\\r ✓ Uploaded ${basename(file)} ${sizeMB} MB\\x1b[K\\n`);\n }\n spinner.stop(\"Upload complete\");\n console.log(formatOutput(result, format));\n auditEntry.success = true;\n } catch (error) {\n if (showProgress) {\n process.stderr.write(\"\\n\");\n }\n spinner.fail(\"Upload failed\");\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n\n // Status\n releases\n .command(\"status\")\n .description(\"Show current release status across tracks\")\n .option(\"--track <track>\", \"Filter by track\")\n .option(\"--sort <field>\", \"Sort by field (prefix with - for descending)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const TRACK_ORDER = [\"production\", \"beta\", \"alpha\", \"internal\"];\n const rawStatuses = await getReleasesStatus(client, packageName, options.track);\n const statuses = options.track\n ? Array.isArray(rawStatuses)\n ? rawStatuses.filter((s: any) => s.track === options.track)\n : rawStatuses\n : rawStatuses;\n const sorted = Array.isArray(statuses)\n ? options.sort\n ? sortResults(statuses, options.sort)\n : [...statuses].sort((a, b) => {\n const ai = TRACK_ORDER.indexOf(\n String((a as unknown as Record<string, unknown>)[\"track\"] ?? \"\"),\n );\n const bi = TRACK_ORDER.indexOf(\n String((b as unknown as Record<string, unknown>)[\"track\"] ?? \"\"),\n );\n return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);\n })\n : statuses;\n if (format !== \"json\" && Array.isArray(sorted)) {\n const rows = sorted.map((s: unknown) => {\n const sr = s as Record<string, unknown>;\n return {\n track: sr[\"track\"] || \"-\",\n status: sr[\"status\"] || \"-\",\n name: sr[\"name\"] || \"-\",\n versionCodes: Array.isArray(sr[\"versionCodes\"])\n ? (sr[\"versionCodes\"] as unknown[]).join(\", \")\n : \"-\",\n userFraction:\n sr[\"userFraction\"] !== undefined\n ? `${Math.round(Number(sr[\"userFraction\"]) * 100)}%`\n : \"—\",\n };\n });\n console.log(formatOutput(rows, format));\n } else {\n console.log(formatOutput(sorted, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Promote\n releases\n .command(\"promote\")\n .description(\"Promote a release from one track to another\")\n .option(\"--from <track>\", \"Source track\")\n .option(\"--to <track>\", \"Target track\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage\")\n .option(\"--notes <text>\", \"Release notes\")\n .option(\"--copy-notes-from <track>\", \"Copy release notes from another track\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n const interactive = isInteractive(program);\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n\n options.from = await requireOption(\n \"from\",\n options.from,\n {\n message: \"Source track:\",\n choices: tracks,\n },\n interactive,\n );\n\n options.to = await requireOption(\n \"to\",\n options.to,\n {\n message: \"Target track:\",\n choices: tracks.filter((t: string) => t !== options.from),\n },\n interactive,\n );\n\n if (options.from === options.to) {\n console.error(\n `Error: --from and --to must be different tracks (both are \"${options.from}\")`,\n );\n process.exit(2);\n }\n\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n console.error(\n `Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n );\n process.exit(2);\n }\n }\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"releases promote\",\n action: \"promote\",\n target: `${options.from} → ${options.to}`,\n details: { rollout: options.rollout },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.copyNotesFrom) {\n releaseNotes = await fetchReleaseNotes(client, packageName, options.copyNotesFrom);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n const result = await promoteRelease(client, packageName, options.from, options.to, {\n userFraction: options.rollout ? Number(options.rollout) / 100 : undefined,\n releaseNotes,\n });\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Rollout subcommands\n const rollout = releases.command(\"rollout\").description(\"Manage staged rollouts\");\n\n for (const action of [\"increase\", \"halt\", \"resume\", \"complete\"] as const) {\n const cmd = rollout\n .command(action)\n .description(`${action.charAt(0).toUpperCase() + action.slice(1)} a staged rollout`)\n .option(\"--track <track>\", \"Track name\");\n\n if (action === \"increase\") {\n cmd.option(\"--to <percent>\", \"New rollout percentage\");\n cmd.option(\"--vitals-gate\", \"Halt rollout if crash rate exceeds configured threshold\");\n }\n\n cmd.action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n const interactive = isInteractive(program);\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n\n options.track = await requireOption(\n \"track\",\n options.track,\n {\n message: \"Track:\",\n choices: tracks,\n },\n interactive,\n );\n\n if (action === \"increase\") {\n options.to = await requireOption(\n \"to\",\n options.to,\n {\n message: \"New rollout percentage (1-100):\",\n },\n interactive,\n );\n }\n\n if (action === \"increase\" && options.to !== undefined) {\n const to = Number(options.to);\n if (!Number.isFinite(to) || to < 1 || to > 100) {\n console.error(`Error: --to must be a number between 1 and 100 (got: ${options.to})`);\n process.exit(2);\n }\n }\n\n // Require confirmation for destructive rollout halt\n if (action === \"halt\") {\n await requireConfirm(\n `Halt rollout on track \"${options.track}\" for ${packageName}?`,\n program,\n );\n }\n\n if (isDryRun(program)) {\n if (action === \"increase\" && options.vitalsGate) {\n console.error(\n \"Warning: --vitals-gate is ignored in --dry-run mode. Gate will run on live execution.\",\n );\n }\n printDryRun(\n {\n command: `releases rollout ${action}`,\n action: action,\n target: options.track,\n details: { percentage: options.to !== undefined ? `${options.to}%` : undefined },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await updateRollout(\n client,\n packageName,\n options.track,\n action,\n options.to ? Number(options.to) / 100 : undefined,\n );\n\n // Vitals gate: check crash rate after rollout increase\n if (action === \"increase\" && options.vitalsGate) {\n const threshold = (config as any).vitals?.thresholds?.crashRate;\n if (!threshold) {\n console.error(\n \"Warning: --vitals-gate requires vitals.thresholds.crashRate in config. Skipping gate.\",\n );\n } else {\n try {\n const { auth: authConfig } = config;\n const vitalsAuth = await resolveAuth({\n serviceAccountPath: authConfig?.serviceAccount,\n });\n const reportingClient = createReportingClient({ auth: vitalsAuth });\n const vitalsResult = await getVitalsCrashes(reportingClient, packageName, {\n days: 1,\n });\n const latest = (vitalsResult as any).data?.[0]?.crashRate;\n const check = checkThreshold(latest, threshold);\n if (check.breached) {\n await updateRollout(client, packageName, options.track, \"halt\");\n console.error(\n `Vitals gate: crash rate ${String(latest)}% > threshold ${String(threshold)}%. Rollout halted.`,\n );\n process.exit(6);\n }\n } catch (vitalsErr) {\n console.error(\n `Warning: Vitals gate check failed: ${vitalsErr instanceof Error ? vitalsErr.message : String(vitalsErr)}`,\n );\n }\n }\n }\n\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n }\n\n // Release notes\n releases\n .command(\"notes\")\n .description(\"Get or set release notes\")\n .argument(\"<action>\", \"Action: get|set\")\n .option(\"--track <track>\", \"Track name\")\n .option(\"--lang <language>\", \"Language code\", \"en-US\")\n .option(\"--notes <text>\", \"Release notes text\")\n .option(\"--file <path>\", \"Read notes from file\")\n .action(async (action: string, options) => {\n if (action === \"set\") {\n console.error(\n \"Error: gpc releases notes set is not implemented as a standalone command.\\n\" +\n \"Use --notes, --notes-dir, or --notes-from-git with:\\n\" +\n \" gpc releases upload\\n\" +\n \" gpc publish\",\n );\n process.exit(1);\n }\n\n if (action === \"get\") {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n const track = options.track ?? \"internal\";\n const statuses = await getReleasesStatus(client, packageName, track);\n const notes = Array.isArray(statuses)\n ? statuses.flatMap((s: any) => s.releaseNotes ?? [])\n : ((statuses as any).releaseNotes ?? []);\n if (notes.length === 0) {\n const hasCompleted =\n Array.isArray(statuses) && statuses.some((s: any) => s.status === \"completed\");\n if (hasCompleted) {\n console.log(\n `No release notes found on track \"${track}\". Notes may not be retained for completed releases. Try: gpc releases diff --from ${track} --to ${track}`,\n );\n } else {\n console.log(\"No release notes found.\");\n }\n return;\n }\n console.log(formatOutput(notes, format));\n return;\n }\n\n console.error(\"Usage: gpc releases notes <get|set> --track <track>\");\n process.exit(2);\n });\n\n // Upload externally hosted APK\n releases\n .command(\"upload-external\")\n .description(\"Upload an externally hosted APK configuration\")\n .requiredOption(\"--url <url>\", \"External URL where the APK is hosted\")\n .requiredOption(\"--file <config>\", \"Path to JSON config file with APK metadata\")\n .action(async (options: { url: string; file: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n try {\n const { readFile } = await import(\"node:fs/promises\");\n const raw = await readFile(options.file, \"utf-8\");\n const apkConfig = JSON.parse(raw) as Record<string, unknown>;\n\n // Override with CLI-provided URL\n apkConfig[\"externallyHostedUrl\"] = options.url;\n\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n const client = createApiClient({ auth });\n const result = await uploadExternallyHosted(\n client,\n packageName,\n apkConfig as unknown as ExternallyHostedApk,\n );\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Diff\n releases\n .command(\"diff\")\n .description(\"Compare releases between two tracks\")\n .option(\"--from <track>\", \"Source track\", \"internal\")\n .option(\"--to <track>\", \"Target track\", \"production\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await diffReleases(client, packageName, options.from, options.to);\n if (result.diffs.length === 0) {\n console.log(`No differences between ${result.fromTrack} and ${result.toTrack}.`);\n } else {\n if (format === \"json\") {\n console.log(formatOutput(result, format));\n } else {\n console.log(`Differences: ${result.fromTrack} vs ${result.toTrack}\\n`);\n console.log(formatOutput(result.diffs, format));\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Count\n releases\n .command(\"count\")\n .description(\"Count releases per track\")\n .option(\"--track <track>\", \"Filter to a specific track\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const statuses = await getReleasesStatus(client, packageName, options.track);\n\n // Group by track\n const trackCounts = new Map<string, { total: number; statuses: Record<string, number> }>();\n for (const r of statuses) {\n const entry = trackCounts.get(r.track) ?? { total: 0, statuses: {} };\n entry.total++;\n entry.statuses[r.status] = (entry.statuses[r.status] ?? 0) + 1;\n trackCounts.set(r.track, entry);\n }\n\n if (format === \"json\") {\n const data = Object.fromEntries(\n [...trackCounts.entries()].map(([track, info]) => [track, info]),\n );\n console.log(formatOutput(data, format));\n } else {\n const rows = [...trackCounts.entries()].map(([track, info]) => ({\n track,\n releases: info.total,\n ...info.statuses,\n }));\n if (rows.length === 0) {\n console.log(\"No releases found.\");\n } else {\n console.log(formatOutput(rows, format));\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,YAAY;AACjC,SAAS,UAAU,eAAe;AAGlC,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,6BAA6B;AAEvD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc,aAAa,qBAAqB;AAWzD,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,cAAqE;AAC9F,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,CAAC,UAAyB;AAC/B,UAAM,OAAO,KAAK,UAAU,KAAK,IAAI;AACrC,eAAW,cAAc,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC/C;AACF;AAEA,eAAe,UAAU,QAAmB,cAAuB,eAAwB;AACzF,QAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,SAAO,gBAAgB,EAAE,MAAM,SAAS,kBAAkB,YAAY,GAAG,cAAc,CAAC;AAC1F;AAEO,SAAS,yBAAyB,SAAwB;AAC/D,QAAM,WAAW,QAAQ,QAAQ,UAAU,EAAE,YAAY,8BAA8B;AAGvF,WACG,QAAQ,eAAe,EACvB,YAAY,sCAAsC,EAClD,OAAO,mBAAmB,gBAAgB,UAAU,EACpD,OAAO,uBAAuB,mCAAmC,EACjE,OAAO,kBAAkB,uBAAuB,EAChD,OAAO,iBAAiB,cAAc,EACtC,OAAO,oBAAoB,4CAA4C,EACvE,OAAO,qBAAqB,sDAAsD,EAClF,OAAO,oBAAoB,gDAAgD,EAC3E,OAAO,6BAA6B,uCAAuC,EAC3E,OAAO,iBAAiB,oEAA+D,EACvF,OAAO,sBAAsB,yCAAyC,EACtE;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,MAAc,YAAY;AACvC,QAAI;AACF,YAAM,KAAK,IAAI;AAAA,IACjB,QAAQ;AACN,cAAQ,MAAM,0BAA0B,IAAI,EAAE;AAC9C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,MAAM,QAAQ,IAAI,EAAE,YAAY;AACtC,QAAI,QAAQ,UAAU,QAAQ,QAAQ;AACpC,cAAQ,MAAM,2CAA2C,OAAO,gBAAgB,GAAG;AACnF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc;AAAA,MAClB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,EAAE,OAAO,OAAO;AAChB,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAG9C,QAAI,cAAc,OAAO,GAAG;AAC1B,UAAI,CAAC,QAAQ,SAAS,QAAQ,UAAU,YAAY;AAClD,cAAM,SAAS,CAAC,YAAY,SAAS,QAAQ,YAAY;AACzD,gBAAQ,QAAQ,MAAM,aAAa,iBAAiB,QAAQ,UAAU;AAAA,MACxE;AAEA,UAAI,CAAC,QAAQ,WAAW,QAAQ,UAAU,cAAc;AACtD,cAAM,aAAa,MAAM;AAAA,UACvB;AAAA,UACA;AAAA,QACF;AACA,YAAI,cAAc,eAAe,OAAO;AACtC,kBAAQ,UAAU;AAAA,QACpB;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,UAAU;AACvC,cAAM,QAAQ,MAAM,YAAY,sCAAsC;AACtE,YAAI,MAAO,SAAQ,QAAQ;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,QAAQ,YAAY,QAAW;AACjC,YAAMA,WAAU,OAAO,QAAQ,OAAO;AACtC,UAAI,CAAC,OAAO,SAASA,QAAO,KAAKA,WAAU,KAAKA,WAAU,KAAK;AAC7D,gBAAQ;AAAA,UACN,6DAA6D,QAAQ,OAAO;AAAA,QAC9E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,EAAE,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI;AAC1C,UAAM,WAAW,WAAW;AAC5B,UAAM,SAAS,MAAM,UAAU,QAAQ,QAAQ,UAAU,QAAQ,OAAO;AAExE,UAAM,eAAe,CAAC,YAAY,QAAQ,OAAO,SAAS,CAAC,QAAQ,KAAK,EAAE,OAAO;AACjF,UAAM,UAAU,YAAY,OAAO,OAAO,QAAQ,CAAC;AAEnD,aAAS,YAAY,OAAuB;AAC1C,UAAI,SAAS,OAAO,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,OAAO,QAAQ,CAAC,CAAC;AACpF,UAAI,SAAS,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AACtE,UAAI,SAAS,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AACtD,aAAO,GAAG,KAAK;AAAA,IACjB;AAEA,UAAM,YAAY;AAClB,UAAM,mBAAmB,eACrB,CAAC,UAA+B;AAC9B,YAAM,SAAS,KAAK,MAAO,MAAM,UAAU,MAAO,SAAS;AAC3D,YAAM,MAAM,SAAI,OAAO,MAAM,IAAI,SAAI,OAAO,YAAY,MAAM;AAC9D,YAAM,WAAW,YAAY,MAAM,aAAa;AAChD,YAAM,QAAQ,YAAY,MAAM,UAAU;AAC1C,YAAM,QACJ,MAAM,iBAAiB,IAAI,GAAG,YAAY,MAAM,cAAc,CAAC,OAAO;AACxE,YAAM,MAAM,MAAM,aAAa,IAAI,OAAO,MAAM,UAAU,MAAM;AAChE,cAAQ,OAAO;AAAA,QACb,OAAO,GAAG,KAAK,MAAM,OAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,MACvE;AAAA,IACF,IACA;AAEJ,QAAI,SAAS,OAAO,GAAG;AACrB,UAAI;AACF,cAAM,SAAS,MAAM,cAAc,QAAQ,aAAa,MAAM;AAAA,UAC5D,OAAO,QAAQ;AAAA,UACf,cAAc,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI,MAAM;AAAA,UAChE,QAAQ;AAAA,QACV,CAAC;AACD,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C,SAAS,OAAO;AACd,gBAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAEA,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,QACE;AAAA,QACA,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ;AAAA,MACnB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,UAAU,cAAc,aAAa,SAAS,IAAI,CAAC,KAAK,MAAM,SAAS;AAC7E,QAAI,CAAC,aAAc,SAAQ,MAAM;AAEjC,QAAI;AACF,UAAI;AACJ,UAAI,QAAQ,eAAe;AACzB,uBAAe,MAAM,kBAAkB,QAAQ,aAAa,QAAQ,aAAa;AAAA,MACnF,WAAW,QAAQ,cAAc;AAC/B,cAAM,WAAW,MAAM,qBAAqB,EAAE,OAAO,QAAQ,MAAM,CAAC;AACpE,uBAAe,CAAC,EAAE,UAAU,SAAS,UAAU,MAAM,SAAS,KAAK,CAAC;AAAA,MACtE,WAAW,QAAQ,UAAU;AAC3B,uBAAe,MAAM,wBAAwB,QAAQ,QAAQ;AAAA,MAC/D,WAAW,QAAQ,OAAO;AACxB,uBAAe,CAAC,EAAE,UAAU,SAAS,MAAM,QAAQ,MAAM,CAAC;AAAA,MAC5D;AAEA,YAAM,SAAS,MAAM,cAAc,QAAQ,aAAa,MAAM;AAAA,QAC5D,OAAO,QAAQ;AAAA,QACf,cAAc,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI,MAAM;AAAA,QAChE;AAAA,QACA,aAAa,QAAQ;AAAA,QACrB,aAAa,QAAQ;AAAA,QACrB;AAAA,MACF,CAAC;AACD,UAAI,cAAc;AAChB,gBAAQ,OAAO,MAAM,uBAAkB,SAAS,IAAI,CAAC,KAAK,MAAM;AAAA,CAAa;AAAA,MAC/E;AACA,cAAQ,KAAK,iBAAiB;AAC9B,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AACxC,iBAAW,UAAU;AAAA,IACvB,SAAS,OAAO;AACd,UAAI,cAAc;AAChB,gBAAQ,OAAO,MAAM,IAAI;AAAA,MAC3B;AACA,cAAQ,KAAK,eAAe;AAC5B,iBAAW,UAAU;AACrB,iBAAW,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACxE,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB,UAAE;AACA,iBAAW,aAAa,KAAK,IAAI,IAAI,IAAI,KAAK,WAAW,SAAS,EAAE,QAAQ;AAC5E,oBAAc,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC1C;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,QAAQ,EAChB,YAAY,2CAA2C,EACvD,OAAO,mBAAmB,iBAAiB,EAC3C,OAAO,kBAAkB,8CAA8C,EACvE,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,cAAc,CAAC,cAAc,QAAQ,SAAS,UAAU;AAC9D,YAAM,cAAc,MAAM,kBAAkB,QAAQ,aAAa,QAAQ,KAAK;AAC9E,YAAM,WAAW,QAAQ,QACrB,MAAM,QAAQ,WAAW,IACvB,YAAY,OAAO,CAAC,MAAW,EAAE,UAAU,QAAQ,KAAK,IACxD,cACF;AACJ,YAAM,SAAS,MAAM,QAAQ,QAAQ,IACjC,QAAQ,OACN,YAAY,UAAU,QAAQ,IAAI,IAClC,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM;AAC3B,cAAM,KAAK,YAAY;AAAA,UACrB,OAAQ,EAAyC,OAAO,KAAK,EAAE;AAAA,QACjE;AACA,cAAM,KAAK,YAAY;AAAA,UACrB,OAAQ,EAAyC,OAAO,KAAK,EAAE;AAAA,QACjE;AACA,gBAAQ,OAAO,KAAK,KAAK,OAAO,OAAO,KAAK,KAAK;AAAA,MACnD,CAAC,IACH;AACJ,UAAI,WAAW,UAAU,MAAM,QAAQ,MAAM,GAAG;AAC9C,cAAM,OAAO,OAAO,IAAI,CAAC,MAAe;AACtC,gBAAM,KAAK;AACX,iBAAO;AAAA,YACL,OAAO,GAAG,OAAO,KAAK;AAAA,YACtB,QAAQ,GAAG,QAAQ,KAAK;AAAA,YACxB,MAAM,GAAG,MAAM,KAAK;AAAA,YACpB,cAAc,MAAM,QAAQ,GAAG,cAAc,CAAC,IACzC,GAAG,cAAc,EAAgB,KAAK,IAAI,IAC3C;AAAA,YACJ,cACE,GAAG,cAAc,MAAM,SACnB,GAAG,KAAK,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,CAAC,MAC/C;AAAA,UACR;AAAA,QACF,CAAC;AACD,gBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,MACxC,OAAO;AACL,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,SAAS,EACjB,YAAY,6CAA6C,EACzD,OAAO,kBAAkB,cAAc,EACvC,OAAO,gBAAgB,cAAc,EACrC,OAAO,uBAAuB,2BAA2B,EACzD,OAAO,kBAAkB,eAAe,EACxC,OAAO,6BAA6B,uCAAuC,EAC3E,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,UAAM,cAAc,cAAc,OAAO;AACzC,UAAM,SAAS,CAAC,YAAY,SAAS,QAAQ,YAAY;AAEzD,YAAQ,OAAO,MAAM;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAEA,YAAQ,KAAK,MAAM;AAAA,MACjB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,SAAS;AAAA,QACT,SAAS,OAAO,OAAO,CAAC,MAAc,MAAM,QAAQ,IAAI;AAAA,MAC1D;AAAA,MACA;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS,QAAQ,IAAI;AAC/B,cAAQ;AAAA,QACN,8DAA8D,QAAQ,IAAI;AAAA,MAC5E;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,QAAQ,YAAY,QAAW;AACjC,YAAMA,WAAU,OAAO,QAAQ,OAAO;AACtC,UAAI,CAAC,OAAO,SAASA,QAAO,KAAKA,WAAU,KAAKA,WAAU,KAAK;AAC7D,gBAAQ;AAAA,UACN,6DAA6D,QAAQ,OAAO;AAAA,QAC9E;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAEA,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,GAAG,QAAQ,IAAI,WAAM,QAAQ,EAAE;AAAA,UACvC,SAAS,EAAE,SAAS,QAAQ,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,UAAI;AACJ,UAAI,QAAQ,eAAe;AACzB,uBAAe,MAAM,kBAAkB,QAAQ,aAAa,QAAQ,aAAa;AAAA,MACnF,WAAW,QAAQ,OAAO;AACxB,uBAAe,CAAC,EAAE,UAAU,SAAS,MAAM,QAAQ,MAAM,CAAC;AAAA,MAC5D;AAEA,YAAM,SAAS,MAAM,eAAe,QAAQ,aAAa,QAAQ,MAAM,QAAQ,IAAI;AAAA,QACjF,cAAc,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI,MAAM;AAAA,QAChE;AAAA,MACF,CAAC;AACD,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,QAAM,UAAU,SAAS,QAAQ,SAAS,EAAE,YAAY,wBAAwB;AAEhF,aAAW,UAAU,CAAC,YAAY,QAAQ,UAAU,UAAU,GAAY;AACxE,UAAM,MAAM,QACT,QAAQ,MAAM,EACd,YAAY,GAAG,OAAO,OAAO,CAAC,EAAE,YAAY,IAAI,OAAO,MAAM,CAAC,CAAC,mBAAmB,EAClF,OAAO,mBAAmB,YAAY;AAEzC,QAAI,WAAW,YAAY;AACzB,UAAI,OAAO,kBAAkB,wBAAwB;AACrD,UAAI,OAAO,iBAAiB,yDAAyD;AAAA,IACvF;AAEA,QAAI,OAAO,OAAO,YAAY;AAC5B,YAAM,SAAS,MAAM,WAAW;AAChC,YAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,YAAM,cAAc,cAAc,OAAO;AACzC,YAAM,SAAS,CAAC,YAAY,SAAS,QAAQ,YAAY;AAEzD,cAAQ,QAAQ,MAAM;AAAA,QACpB;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,UACE,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAEA,UAAI,WAAW,YAAY;AACzB,gBAAQ,KAAK,MAAM;AAAA,UACjB;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,YACE,SAAS;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,UAAI,WAAW,cAAc,QAAQ,OAAO,QAAW;AACrD,cAAM,KAAK,OAAO,QAAQ,EAAE;AAC5B,YAAI,CAAC,OAAO,SAAS,EAAE,KAAK,KAAK,KAAK,KAAK,KAAK;AAC9C,kBAAQ,MAAM,wDAAwD,QAAQ,EAAE,GAAG;AACnF,kBAAQ,KAAK,CAAC;AAAA,QAChB;AAAA,MACF;AAGA,UAAI,WAAW,QAAQ;AACrB,cAAM;AAAA,UACJ,0BAA0B,QAAQ,KAAK,SAAS,WAAW;AAAA,UAC3D;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,OAAO,GAAG;AACrB,YAAI,WAAW,cAAc,QAAQ,YAAY;AAC/C,kBAAQ;AAAA,YACN;AAAA,UACF;AAAA,QACF;AACA;AAAA,UACE;AAAA,YACE,SAAS,oBAAoB,MAAM;AAAA,YACnC;AAAA,YACA,QAAQ,QAAQ;AAAA,YAChB,SAAS,EAAE,YAAY,QAAQ,OAAO,SAAY,GAAG,QAAQ,EAAE,MAAM,OAAU;AAAA,UACjF;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,UAAU,MAAM;AAErC,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA,QAAQ,KAAK,OAAO,QAAQ,EAAE,IAAI,MAAM;AAAA,QAC1C;AAGA,YAAI,WAAW,cAAc,QAAQ,YAAY;AAC/C,gBAAM,YAAa,OAAe,QAAQ,YAAY;AACtD,cAAI,CAAC,WAAW;AACd,oBAAQ;AAAA,cACN;AAAA,YACF;AAAA,UACF,OAAO;AACL,gBAAI;AACF,oBAAM,EAAE,MAAM,WAAW,IAAI;AAC7B,oBAAM,aAAa,MAAM,YAAY;AAAA,gBACnC,oBAAoB,YAAY;AAAA,cAClC,CAAC;AACD,oBAAM,kBAAkB,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAClE,oBAAM,eAAe,MAAM,iBAAiB,iBAAiB,aAAa;AAAA,gBACxE,MAAM;AAAA,cACR,CAAC;AACD,oBAAM,SAAU,aAAqB,OAAO,CAAC,GAAG;AAChD,oBAAM,QAAQ,eAAe,QAAQ,SAAS;AAC9C,kBAAI,MAAM,UAAU;AAClB,sBAAM,cAAc,QAAQ,aAAa,QAAQ,OAAO,MAAM;AAC9D,wBAAQ;AAAA,kBACN,2BAA2B,OAAO,MAAM,CAAC,iBAAiB,OAAO,SAAS,CAAC;AAAA,gBAC7E;AACA,wBAAQ,KAAK,CAAC;AAAA,cAChB;AAAA,YACF,SAAS,WAAW;AAClB,sBAAQ;AAAA,gBACN,sCAAsC,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,cAC1G;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,gBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,MAC1C,SAAS,OAAO;AACd,gBAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACH;AAGA,WACG,QAAQ,OAAO,EACf,YAAY,0BAA0B,EACtC,SAAS,YAAY,iBAAiB,EACtC,OAAO,mBAAmB,YAAY,EACtC,OAAO,qBAAqB,iBAAiB,OAAO,EACpD,OAAO,kBAAkB,oBAAoB,EAC7C,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,OAAO,QAAgB,YAAY;AACzC,QAAI,WAAW,OAAO;AACpB,cAAQ;AAAA,QACN;AAAA,MAIF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,WAAW,OAAO;AACpB,YAAM,SAAS,MAAM,WAAW;AAChC,YAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,YAAM,SAAS,MAAM,UAAU,MAAM;AACrC,YAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,YAAM,QAAQ,QAAQ,SAAS;AAC/B,YAAM,WAAW,MAAM,kBAAkB,QAAQ,aAAa,KAAK;AACnE,YAAM,QAAQ,MAAM,QAAQ,QAAQ,IAChC,SAAS,QAAQ,CAAC,MAAW,EAAE,gBAAgB,CAAC,CAAC,IAC/C,SAAiB,gBAAgB,CAAC;AACxC,UAAI,MAAM,WAAW,GAAG;AACtB,cAAM,eACJ,MAAM,QAAQ,QAAQ,KAAK,SAAS,KAAK,CAAC,MAAW,EAAE,WAAW,WAAW;AAC/E,YAAI,cAAc;AAChB,kBAAQ;AAAA,YACN,oCAAoC,KAAK,sFAAsF,KAAK,SAAS,KAAK;AAAA,UACpJ;AAAA,QACF,OAAO;AACL,kBAAQ,IAAI,yBAAyB;AAAA,QACvC;AACA;AAAA,MACF;AACA,cAAQ,IAAI,aAAa,OAAO,MAAM,CAAC;AACvC;AAAA,IACF;AAEA,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGH,WACG,QAAQ,iBAAiB,EACzB,YAAY,+CAA+C,EAC3D,eAAe,eAAe,sCAAsC,EACpE,eAAe,mBAAmB,4CAA4C,EAC9E,OAAO,OAAO,YAA2C;AACxD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,aAAkB;AACpD,YAAM,MAAM,MAAM,SAAS,QAAQ,MAAM,OAAO;AAChD,YAAM,YAAY,KAAK,MAAM,GAAG;AAGhC,gBAAU,qBAAqB,IAAI,QAAQ;AAE3C,YAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,YAAM,SAAS,gBAAgB,EAAE,KAAK,CAAC;AACvC,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,MAAM,EACd,YAAY,qCAAqC,EACjD,OAAO,kBAAkB,gBAAgB,UAAU,EACnD,OAAO,gBAAgB,gBAAgB,YAAY,EACnD,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,QAAQ,aAAa,QAAQ,MAAM,QAAQ,EAAE;AAC/E,UAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,gBAAQ,IAAI,0BAA0B,OAAO,SAAS,QAAQ,OAAO,OAAO,GAAG;AAAA,MACjF,OAAO;AACL,YAAI,WAAW,QAAQ;AACrB,kBAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,QAC1C,OAAO;AACL,kBAAQ,IAAI,gBAAgB,OAAO,SAAS,OAAO,OAAO,OAAO;AAAA,CAAI;AACrE,kBAAQ,IAAI,aAAa,OAAO,OAAO,MAAM,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,OAAO,EACf,YAAY,0BAA0B,EACtC,OAAO,mBAAmB,4BAA4B,EACtD,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,MAAM,UAAU,MAAM;AACrC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkB,QAAQ,aAAa,QAAQ,KAAK;AAG3E,YAAM,cAAc,oBAAI,IAAiE;AACzF,iBAAW,KAAK,UAAU;AACxB,cAAM,QAAQ,YAAY,IAAI,EAAE,KAAK,KAAK,EAAE,OAAO,GAAG,UAAU,CAAC,EAAE;AACnE,cAAM;AACN,cAAM,SAAS,EAAE,MAAM,KAAK,MAAM,SAAS,EAAE,MAAM,KAAK,KAAK;AAC7D,oBAAY,IAAI,EAAE,OAAO,KAAK;AAAA,MAChC;AAEA,UAAI,WAAW,QAAQ;AACrB,cAAM,OAAO,OAAO;AAAA,UAClB,CAAC,GAAG,YAAY,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC;AAAA,QACjE;AACA,gBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,MACxC,OAAO;AACL,cAAM,OAAO,CAAC,GAAG,YAAY,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO;AAAA,UAC9D;AAAA,UACA,UAAU,KAAK;AAAA,UACf,GAAG,KAAK;AAAA,QACV,EAAE;AACF,YAAI,KAAK,WAAW,GAAG;AACrB,kBAAQ,IAAI,oBAAoB;AAAA,QAClC,OAAO;AACL,kBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,QACxC;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;","names":["rollout"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/reviews.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport {\n listReviews,\n getReview,\n replyToReview,\n exportReviews,\n analyzeReviews,\n formatOutput,\n maybePaginate,\n sortResults,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { isInteractive, requireOption } from \"../prompt.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerReviewsCommands(program: Command): void {\n const reviews = program.command(\"reviews\").description(\"Manage user reviews and ratings\");\n\n reviews\n .command(\"list\")\n .description(\"List user reviews\")\n .option(\"--stars <n>\", \"Filter by star rating (1-5)\", parseInt)\n .option(\"--lang <code>\", \"Filter by reviewer language\")\n .option(\"--since <date>\", \"Filter reviews after date (ISO 8601)\")\n .option(\"--translate-to <lang>\", \"Translate reviews to language\")\n .option(\"--max <n>\", \"Maximum number of reviews per page\", parseInt)\n .option(\"--limit <n>\", \"Maximum total results\", parseInt)\n .option(\"--next-page <token>\", \"Resume from page token\")\n .option(\"--sort <field>\", \"Sort by field (prefix with - for descending)\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await listReviews(client, packageName, {\n stars: options.stars,\n language: options.lang,\n since: options.since,\n translationLanguage: options.translateTo,\n maxResults: options.max,\n limit: options.limit,\n nextPage: options.nextPage,\n });\n if (Array.isArray(result) && result.length === 0 && format !== \"json\") {\n console.log(\"No reviews found.\");\n return;\n }\n const sorted = sortResults(result, options.sort);\n if (format !== \"json\" && Array.isArray(sorted)) {\n const rows = sorted.map((r: unknown) => {\n const rv = r as Record<string, unknown>;\n const comments = rv[\"comments\"] as Record<string, unknown>[] | undefined;\n const userComment = comments?.[0]?.[\"userComment\"] as\n | Record<string, unknown>\n | undefined;\n return {\n reviewId: rv[\"reviewId\"] || \"-\",\n author: rv[\"authorName\"] || \"-\",\n stars: userComment?.[\"starRating\"] || \"-\",\n text: String(userComment?.[\"text\"] || \"-\").slice(0, 80),\n lastModified: userComment?.[\"lastModified\"]\n ? String(\n (userComment[\"lastModified\"] as Record<string, unknown>)?.[\"seconds\"] || \"-\",\n )\n : \"-\",\n thumbsUp: userComment?.[\"thumbsUpCount\"] || 0,\n };\n });\n await maybePaginate(formatOutput(rows, format));\n } else {\n await maybePaginate(formatOutput(sorted, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n reviews\n .command(\"get <review-id>\")\n .description(\"Get a single review\")\n .option(\"--translate-to <lang>\", \"Translate review to language\")\n .action(async (reviewId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getReview(client, packageName, reviewId, options.translateTo);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n reviews\n .command(\"reply <review-id>\")\n .description(\"Reply to a review\")\n .option(\"--text <text>\", \"Reply text (max 350 chars)\")\n .action(async (reviewId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n const interactive = isInteractive(program);\n\n options.text = await requireOption(\n \"text\",\n options.text,\n {\n message: \"Reply text (max 350 chars):\",\n },\n interactive,\n );\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"reviews reply\",\n action: \"reply to\",\n target: reviewId,\n details: { text: options.text },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await replyToReview(client, packageName, reviewId, options.text);\n if (format !== \"json\") {\n const charCount = options.text.length;\n console.log(`Reply sent (${charCount}/350 chars)`);\n }\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n reviews\n .command(\"analyze\")\n .description(\"Analyze reviews: sentiment, topics, keywords, rating distribution\")\n .option(\"--stars <n>\", \"Filter by star rating (1-5)\", parseInt)\n .option(\"--lang <code>\", \"Filter by reviewer language\")\n .option(\"--since <date>\", \"Filter reviews after date (ISO 8601)\")\n .option(\"--max <n>\", \"Maximum number of reviews to analyze\", parseInt)\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await analyzeReviews(client, packageName, {\n stars: options.stars,\n language: options.lang,\n since: options.since,\n maxResults: options.max,\n });\n\n if (format === \"json\") {\n console.log(formatOutput(result, format));\n return;\n }\n\n console.log(`\\nReview Analysis — ${packageName}`);\n console.log(`${\"─\".repeat(50)}`);\n console.log(`Total reviews: ${result.totalReviews}`);\n console.log(\n `Average rating: ${result.avgRating > 0 ? result.avgRating.toFixed(2) + \" ★\" : \"N/A\"}`,\n );\n console.log(`\\nSentiment:`);\n console.log(\n ` Positive: ${result.sentiment.positive} Negative: ${result.sentiment.negative} Neutral: ${result.sentiment.neutral}`,\n );\n console.log(` Avg score: ${result.sentiment.avgScore} (range -1 to +1)`);\n\n if (result.topics.length > 0) {\n console.log(`\\nTop topics:`);\n for (const t of result.topics.slice(0, 8)) {\n const bar = t.avgScore > 0.1 ? \"+\" : t.avgScore < -0.1 ? \"-\" : \"~\";\n console.log(` [${bar}] ${t.topic.padEnd(20)} ${t.count} reviews`);\n }\n }\n\n if (result.keywords.length > 0) {\n console.log(\n `\\nTop keywords: ${result.keywords\n .slice(0, 10)\n .map((k) => k.word)\n .join(\", \")}`,\n );\n }\n\n if (Object.keys(result.ratingDistribution).length > 0) {\n console.log(`\\nRating distribution:`);\n for (let star = 5; star >= 1; star--) {\n const count = result.ratingDistribution[star] ?? 0;\n if (count > 0) console.log(` ${star}★ ${count}`);\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n reviews\n .command(\"export\")\n .description(\"Export reviews to JSON or CSV\")\n .option(\"--format <type>\", \"Output format: json or csv\", \"json\")\n .option(\"--stars <n>\", \"Filter by star rating (1-5)\", parseInt)\n .option(\"--lang <code>\", \"Filter by reviewer language\")\n .option(\"--since <date>\", \"Filter reviews after date (ISO 8601)\")\n .option(\"--translate-to <lang>\", \"Translate reviews to language\")\n .option(\"--output <file>\", \"Write output to file instead of stdout\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n\n try {\n const result = await exportReviews(client, packageName, {\n format: options.format as \"json\" | \"csv\",\n stars: options.stars,\n language: options.lang,\n since: options.since,\n translationLanguage: options.translateTo,\n });\n\n if (options.output) {\n const { writeFile } = await import(\"node:fs/promises\");\n await writeFile(options.output, result, \"utf-8\");\n console.log(`Reviews exported to ${options.output}`);\n } else {\n console.log(result);\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient, createReportingClient } from \"@gpc-cli/api\";\nimport {\n getAppStatus,\n formatStatusTable,\n formatStatusSummary,\n formatStatusDiff,\n computeStatusDiff,\n loadStatusCache,\n saveStatusCache,\n statusHasBreach,\n runWatchLoop,\n trackBreachState,\n sendNotification,\n formatOutput,\n createSpinner,\n} from \"@gpc-cli/core\";\nimport type { AppStatus } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { green, red, dim, gray } from \"../colors.js\";\n\nconst VALID_SECTIONS = new Set([\"releases\", \"vitals\", \"reviews\"]);\nconst VALID_FORMATS = new Set([\"table\", \"summary\"]);\n\nfunction parseSections(raw: string): string[] {\n const sections = raw.split(\",\").map((s) => s.trim().toLowerCase());\n for (const s of sections) {\n if (!VALID_SECTIONS.has(s)) {\n console.error(`Error: Unknown section \"${s}\". Valid sections: releases, vitals, reviews`);\n process.exit(2);\n }\n }\n return sections;\n}\n\nfunction resolveVitalThresholds(config: Record<string, unknown>) {\n const vitals = config[\"vitals\"] as Record<string, unknown> | undefined;\n const t = vitals?.[\"thresholds\"] as Record<string, unknown> | undefined;\n if (!t) return undefined;\n const toN = (v: unknown): number | undefined => {\n if (v === undefined || v === null) return undefined;\n const n = Number(v);\n return isNaN(n) ? undefined : n;\n };\n return {\n crashRate: toN(t[\"crashRate\"]),\n anrRate: toN(t[\"anrRate\"]),\n slowStartRate: toN(t[\"slowStartRate\"]),\n slowRenderingRate: toN(t[\"slowRenderingRate\"]),\n };\n}\n\nconst THRESHOLD_KEYS: Record<string, string> = {\n crashes: \"crashRate\",\n crash: \"crashRate\",\n anr: \"anrRate\",\n \"slow-starts\": \"slowStartRate\",\n \"slow-start\": \"slowStartRate\",\n \"slow-render\": \"slowRenderingRate\",\n \"slow-rendering\": \"slowRenderingRate\",\n};\n\nfunction parseThresholdOverrides(raw: string): Record<string, number> {\n const result: Record<string, number> = {};\n for (const pair of raw.split(\",\")) {\n const [key, val] = pair.split(\"=\").map((s) => s.trim());\n if (!key || !val) continue;\n const mapped = THRESHOLD_KEYS[key.toLowerCase()];\n if (!mapped) {\n console.error(\n `Error: Unknown threshold \"${key}\". Valid: crashes, anr, slow-starts, slow-render`,\n );\n process.exit(2);\n }\n const n = parseFloat(val);\n if (isNaN(n) || n < 0) {\n console.error(\n `Error: Invalid threshold value \"${val}\" for ${key}. Must be a positive number (percent).`,\n );\n process.exit(2);\n }\n result[mapped] = n / 100; // Convert percent to decimal\n }\n return result;\n}\n\nfunction resolvePackages(\n program: Command,\n config: { app?: string; profiles?: Record<string, { app?: string }> },\n allApps?: boolean,\n): string[] {\n const rootApp = (program.opts()[\"app\"] || config.app) as string | undefined;\n if (!allApps) return rootApp ? [rootApp] : [];\n\n const seen = new Set<string>();\n const result: string[] = [];\n if (rootApp) {\n seen.add(rootApp);\n result.push(rootApp);\n }\n for (const profile of Object.values(config.profiles ?? {})) {\n if (profile.app && !seen.has(profile.app)) {\n seen.add(profile.app);\n result.push(profile.app);\n }\n }\n return result;\n}\n\nfunction resolveWatchInterval(watch: string | boolean | undefined): number | null {\n if (watch === undefined) return null;\n if (watch === true || watch === \"\") return 30;\n const n = parseInt(String(watch), 10);\n return isNaN(n) ? 30 : n;\n}\n\nfunction colorizeTrackStatus(s: string): string {\n switch (s) {\n case \"inProgress\":\n case \"completed\":\n return green(s);\n case \"halted\":\n return red(s);\n case \"draft\":\n return dim(s);\n default:\n return gray(s);\n }\n}\n\nfunction applyStatusColors(status: AppStatus): AppStatus {\n if (!status.releases || status.releases.length === 0) return status;\n return {\n ...status,\n releases: status.releases.map((r) => ({\n ...r,\n status: colorizeTrackStatus(r.status),\n })),\n };\n}\n\nfunction makeRenderer(format: string, displayFormat: string): (status: AppStatus) => string {\n return (status: AppStatus): string => {\n if (format === \"json\") {\n const sectionSet = new Set(status.sections);\n const filtered: Record<string, unknown> = {\n packageName: status.packageName,\n fetchedAt: status.fetchedAt,\n cached: status.cached,\n sections: status.sections,\n };\n if (sectionSet.has(\"releases\")) filtered[\"releases\"] = status.releases;\n if (sectionSet.has(\"vitals\")) filtered[\"vitals\"] = status.vitals;\n if (sectionSet.has(\"reviews\")) filtered[\"reviews\"] = status.reviews;\n return formatOutput(filtered, \"json\");\n }\n const colorized = applyStatusColors(status);\n if (displayFormat === \"summary\") return formatStatusSummary(colorized);\n return formatStatusTable(colorized);\n };\n}\n\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Unified app health snapshot: releases, vitals, and reviews\")\n .option(\"--days <n>\", \"Vitals window in days\", (v) => parseInt(v, 10), 7)\n .option(\"--review-days <n>\", \"Reviews window in days\", (v) => parseInt(v, 10), 30)\n .option(\"--cached\", \"Use last fetched data, skip API calls\")\n .option(\"--refresh\", \"Force live fetch, ignore cache TTL\")\n .option(\"--ttl <seconds>\", \"Cache TTL in seconds\", (v) => parseInt(v, 10), 3600)\n .option(\"--format <fmt>\", \"Display style: table (default) or summary\", \"table\")\n .option(\n \"--sections <list>\",\n \"Comma-separated sections: releases,vitals,reviews\",\n \"releases,vitals,reviews\",\n )\n .option(\"--watch [seconds]\", \"Poll every N seconds (min 10, default 30)\")\n .option(\"--since-last\", \"Show diff from last cached status\")\n .option(\"--all-apps\", \"Run status for all configured app profiles (max 5)\")\n .option(\"--notify\", \"Send desktop notification on threshold breach or clear\")\n .option(\"--threshold <overrides>\", \"Override vitals thresholds: crashes=1.5,anr=0.5 (percent)\")\n .action(\n async (opts: {\n days: number;\n reviewDays: number;\n cached?: boolean;\n refresh?: boolean;\n ttl: number;\n format: string;\n sections: string;\n watch?: string | boolean;\n sinceLast?: boolean;\n allApps?: boolean;\n threshold?: string;\n notify?: boolean;\n }) => {\n if (!VALID_FORMATS.has(opts.format)) {\n console.error(`Error: Unknown format \"${opts.format}\". Valid: table, summary`);\n process.exit(2);\n }\n\n const sections = parseSections(opts.sections);\n\n if (!Number.isFinite(opts.days) || opts.days < 1) {\n console.error(`Error: --days must be a positive integer (got: ${opts.days})`);\n process.exit(2);\n }\n\n if (!Number.isFinite(opts.reviewDays) || opts.reviewDays < 1) {\n console.error(\n `Error: --review-days must be a positive integer (got: ${opts.reviewDays})`,\n );\n process.exit(2);\n }\n\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n const render = makeRenderer(format, opts.format);\n let vitalThresholds = resolveVitalThresholds(config as unknown as Record<string, unknown>);\n if (opts.threshold) {\n const overrides = parseThresholdOverrides(opts.threshold);\n vitalThresholds = { ...vitalThresholds, ...overrides };\n }\n const watchInterval = resolveWatchInterval(opts.watch);\n const packages = resolvePackages(program, config, opts.allApps);\n\n if (packages.length === 0) {\n console.error(\n \"Error: No package name. Use --app <package> or gpc config set app <package>\",\n );\n process.exit(2);\n }\n if (opts.allApps && packages.length > 5) {\n console.error(\n `Error: --all-apps found ${packages.length} apps (max 5). Use --app to target a specific app.`,\n );\n process.exit(2);\n }\n\n const authConfig = (config as unknown as Record<string, unknown>)[\"auth\"] as\n | Record<string, string>\n | undefined;\n\n const makeClients = async () => {\n const auth = await resolveAuth({ serviceAccountPath: authConfig?.[\"serviceAccount\"] });\n return {\n client: createApiClient({ auth }),\n reporting: createReportingClient({ auth }),\n };\n };\n\n let anyBreach = false;\n\n for (const packageName of packages) {\n if (packages.length > 1) console.log(`\\n=== ${packageName} ===`);\n\n try {\n const breach = await runStatusForPackage({\n packageName,\n opts,\n sections,\n format,\n vitalThresholds,\n watchInterval,\n render,\n makeClients,\n });\n if (breach) anyBreach = true;\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n if (packages.length === 1) process.exit(4);\n // For --all-apps, continue to next app on error\n }\n }\n\n if (anyBreach) process.exit(6);\n },\n );\n}\n\ninterface RunCtx {\n packageName: string;\n opts: {\n days: number;\n cached?: boolean;\n refresh?: boolean;\n ttl: number;\n sinceLast?: boolean;\n notify?: boolean;\n };\n sections: string[];\n format: string;\n vitalThresholds:\n | {\n crashRate?: number;\n anrRate?: number;\n slowStartRate?: number;\n slowRenderingRate?: number;\n }\n | undefined;\n watchInterval: number | null;\n render: (status: AppStatus) => string;\n makeClients: () => Promise<{\n client: ReturnType<typeof createApiClient>;\n reporting: ReturnType<typeof createReportingClient>;\n }>;\n}\n\n/** Override sections on a cached AppStatus with the user-requested sections for display. */\nfunction applyDisplaySections(status: AppStatus, requestedSections: string[]): AppStatus {\n const requested = new Set(requestedSections);\n const filtered = status.sections.filter((s) => requested.has(s));\n if (filtered.length === status.sections.length) return status;\n return { ...status, sections: filtered };\n}\n\n/** Returns true if a breach was detected. */\nasync function runStatusForPackage(ctx: RunCtx): Promise<boolean> {\n const { packageName, opts, sections, vitalThresholds, watchInterval, render } = ctx;\n\n const fetchLive = async (): Promise<AppStatus> => {\n const { client, reporting } = await ctx.makeClients();\n return getAppStatus(client, reporting, packageName, {\n days: opts.days,\n reviewDays: opts.reviewDays,\n sections,\n vitalThresholds: vitalThresholds ?? undefined,\n });\n };\n\n const save = (status: AppStatus) => saveStatusCache(packageName, status, opts.ttl);\n\n if (watchInterval !== null && opts.sinceLast) {\n process.stderr.write(\n \"Warning: --since-last is not supported with --watch and will be ignored.\\n\",\n );\n }\n\n // --watch: hand off entirely to runWatchLoop\n if (watchInterval !== null) {\n await runWatchLoop({ intervalSeconds: watchInterval, render, fetch: fetchLive, save });\n return false;\n }\n\n // --cached: serve from cache only\n if (opts.cached) {\n const cached = await loadStatusCache(packageName, opts.ttl);\n if (!cached) {\n console.error(\"Error: No cached status found. Run without --cached to fetch live data.\");\n process.exit(1);\n }\n const display = applyDisplaySections(cached, sections);\n console.log(render(display));\n await handleNotify(packageName, cached, opts.notify);\n return statusHasBreach(cached);\n }\n\n // Capture prev status before fetching (for --since-last)\n const prevStatus = opts.sinceLast ? await loadStatusCache(packageName, Infinity) : null;\n\n // Try cache (unless --refresh)\n if (!opts.refresh) {\n const cached = await loadStatusCache(packageName, opts.ttl);\n if (cached) {\n const display = applyDisplaySections(cached, sections);\n if (ctx.format !== \"json\" && display.sections.length < cached.sections.length) {\n process.stderr.write(\n `Tip: cache contains all sections. Add --refresh to fetch only the requested sections and reduce API calls.\\n`,\n );\n }\n printWithDiff(display, prevStatus, opts.sinceLast, render, ctx.format);\n await handleNotify(packageName, cached, opts.notify);\n return statusHasBreach(cached);\n }\n }\n\n // Live fetch (with spinner in TTY mode)\n const spinner = createSpinner(\"Fetching app status...\");\n if (ctx.format !== \"json\") spinner.start();\n let status: AppStatus;\n try {\n status = await fetchLive();\n } catch (err) {\n spinner.fail(\"Failed to fetch app status\");\n throw err;\n }\n spinner.stop(\"Done\");\n await save(status);\n\n printWithDiff(status, prevStatus, opts.sinceLast, render, ctx.format);\n await handleNotify(packageName, status, opts.notify);\n return statusHasBreach(status);\n}\n\nfunction relativeTime(isoString: string): string {\n const diffMs = Date.now() - new Date(isoString).getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return \"just now\";\n if (diffMin < 60) return `${diffMin} min ago`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24) return `${diffHr}h ago`;\n return `${Math.floor(diffHr / 24)}d ago`;\n}\n\nfunction printWithDiff(\n status: AppStatus,\n prevStatus: AppStatus | null,\n sinceLast: boolean | undefined,\n render: (s: AppStatus) => string,\n format: string,\n): void {\n console.log(render(status));\n\n if (sinceLast && prevStatus) {\n const since = relativeTime(prevStatus.fetchedAt);\n console.log(\"\");\n console.log(formatStatusDiff(computeStatusDiff(prevStatus, status), since));\n } else if (sinceLast && !prevStatus && format !== \"json\") {\n console.log(\"\\n(No prior cached status to diff against)\");\n }\n}\n\nasync function handleNotify(\n packageName: string,\n status: AppStatus,\n notify: boolean | undefined,\n): Promise<void> {\n if (!notify) return;\n const breaching = statusHasBreach(status);\n const changed = await trackBreachState(packageName, breaching);\n if (changed) {\n const title = breaching ? \"GPC Alert\" : \"GPC Status\";\n const body = breaching\n ? `${packageName}: vitals threshold breached`\n : `${packageName}: vitals back to normal`;\n sendNotification(title, body);\n }\n}\n"],"mappings":"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","names":["status"]}