@gpc-cli/cli 0.9.52 → 0.9.54

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 (160) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -19
  3. package/dist/{anomalies-V3AFS4LD.js → anomalies-I5P6QQAZ.js} +14 -5
  4. package/dist/anomalies-I5P6QQAZ.js.map +1 -0
  5. package/dist/{apps-4GP3FD7O.js → apps-WPUQOQLL.js} +2 -2
  6. package/dist/{audit-VTWXTXC6.js → audit-M7GYID74.js} +2 -2
  7. package/dist/{auth-BA4FE2PO.js → auth-4DRT7ZH2.js} +10 -4
  8. package/dist/auth-4DRT7ZH2.js.map +1 -0
  9. package/dist/bin.js +2 -2
  10. package/dist/{bundle-F7MUVC5J.js → bundle-PFTE7XMU.js} +4 -2
  11. package/dist/bundle-PFTE7XMU.js.map +1 -0
  12. package/dist/{cache-XKPLZYEB.js → cache-FGNP7Y37.js} +6 -2
  13. package/dist/cache-FGNP7Y37.js.map +1 -0
  14. package/dist/changelog-QLDFG5TV.js +0 -0
  15. package/dist/{chunk-SLNJEAMK.js → chunk-22XCOLZX.js} +4 -2
  16. package/dist/chunk-22XCOLZX.js.map +1 -0
  17. package/dist/chunk-3SJ6OXCZ.js +0 -0
  18. package/dist/{chunk-FXOWADQD.js → chunk-6HIY4IGM.js} +48 -45
  19. package/dist/chunk-6HIY4IGM.js.map +1 -0
  20. package/dist/{chunk-BCBXQC7J.js → chunk-E7SVZ7RF.js} +1 -1
  21. package/dist/chunk-E7SVZ7RF.js.map +1 -0
  22. package/dist/chunk-ELXAK7GI.js +0 -0
  23. package/dist/chunk-FAN4ZITI.js +0 -0
  24. package/dist/{chunk-NQH4G7BI.js → chunk-JDRY7HK5.js} +6 -9
  25. package/dist/chunk-JDRY7HK5.js.map +1 -0
  26. package/dist/{chunk-YFUBD2XB.js → chunk-RZQSEDKI.js} +6 -9
  27. package/dist/chunk-RZQSEDKI.js.map +1 -0
  28. package/dist/{chunk-A7VRCCNS.js → chunk-WSLFHX5X.js} +3 -3
  29. package/dist/chunk-WSLFHX5X.js.map +1 -0
  30. package/dist/chunk-Y3QZDAKS.js +0 -0
  31. package/dist/completion-BCHRJSAT.js +0 -0
  32. package/dist/{config-BLMJ35J2.js → config-PUINDZON.js} +3 -3
  33. package/dist/{data-safety-AFMD6MYI.js → data-safety-46VY64OO.js} +12 -4
  34. package/dist/data-safety-46VY64OO.js.map +1 -0
  35. package/dist/{device-tiers-AQAMUQXI.js → device-tiers-MNZYMG3Y.js} +2 -2
  36. package/dist/device-tiers-MNZYMG3Y.js.map +1 -0
  37. package/dist/{diff-6EO4ID6W.js → diff-OBSHUSTL.js} +2 -2
  38. package/dist/diff-OBSHUSTL.js.map +1 -0
  39. package/dist/{docs-GMFN6V4K.js → docs-GP6AEX4N.js} +8 -4
  40. package/dist/docs-GP6AEX4N.js.map +1 -0
  41. package/dist/{doctor-7LQWPY5P.js → doctor-T3QFYBRV.js} +5 -5
  42. package/dist/doctor-T3QFYBRV.js.map +1 -0
  43. package/dist/enterprise-7PWXMSUN.js +0 -0
  44. package/dist/{external-transactions-LCZALS3V.js → external-transactions-JL3G4IG5.js} +11 -5
  45. package/dist/external-transactions-JL3G4IG5.js.map +1 -0
  46. package/dist/{feedback-7ADYSGRD.js → feedback-AULXQLJ7.js} +3 -3
  47. package/dist/feedback-AULXQLJ7.js.map +1 -0
  48. package/dist/{games-ZSNGEI7A.js → games-SVFN2YIS.js} +2 -2
  49. package/dist/games-SVFN2YIS.js.map +1 -0
  50. package/dist/{generated-apks-RX2IUWSF.js → generated-apks-TC33S2YN.js} +2 -2
  51. package/dist/generated-apks-TC33S2YN.js.map +1 -0
  52. package/dist/{grants-EBPECI26.js → grants-UHNBPIFD.js} +10 -3
  53. package/dist/grants-UHNBPIFD.js.map +1 -0
  54. package/dist/{iap-OUI5YYN4.js → iap-ETOL7OAC.js} +4 -4
  55. package/dist/iap-ETOL7OAC.js.map +1 -0
  56. package/dist/index.js +1 -1
  57. package/dist/{init-WSTQTJOD.js → init-6CCSVJC2.js} +7 -3
  58. package/dist/init-6CCSVJC2.js.map +1 -0
  59. package/dist/{install-skills-JKPYZHYS.js → install-skills-S342NJ7T.js} +7 -3
  60. package/dist/{install-skills-JKPYZHYS.js.map → install-skills-S342NJ7T.js.map} +1 -1
  61. package/dist/{internal-sharing-ONNIWIAT.js → internal-sharing-KW6YENDG.js} +2 -2
  62. package/dist/internal-sharing-KW6YENDG.js.map +1 -0
  63. package/dist/{listings-LNX6MQYN.js → listings-MOHHHNE6.js} +27 -7
  64. package/dist/listings-MOHHHNE6.js.map +1 -0
  65. package/dist/migrate-ZQCJGQQS.js +0 -0
  66. package/dist/one-time-products-Y5RNIPV2.js +472 -0
  67. package/dist/one-time-products-Y5RNIPV2.js.map +1 -0
  68. package/dist/{preflight-W3JAJ4GO.js → preflight-KIWZPFTX.js} +4 -13
  69. package/dist/preflight-KIWZPFTX.js.map +1 -0
  70. package/dist/{pricing-JJZFICFL.js → pricing-HYQRXKNO.js} +3 -3
  71. package/dist/pricing-HYQRXKNO.js.map +1 -0
  72. package/dist/{prompt-GXC2JSLA.js → prompt-HJXNXXAR.js} +2 -2
  73. package/dist/{publish-P5KIGSLI.js → publish-26SSZ2W3.js} +11 -5
  74. package/dist/publish-26SSZ2W3.js.map +1 -0
  75. package/dist/purchase-options-KFWW4JW2.js +0 -0
  76. package/dist/{purchases-UBFLNYZC.js → purchases-AHWGLW6V.js} +35 -32
  77. package/dist/purchases-AHWGLW6V.js.map +1 -0
  78. package/dist/{quickstart-Z5Y3FYJU.js → quickstart-FOWM3OKT.js} +7 -2
  79. package/dist/quickstart-FOWM3OKT.js.map +1 -0
  80. package/dist/quota-MZRWYJGR.js +0 -0
  81. package/dist/{recovery-YE3Z7NIN.js → recovery-B7DZQ6XG.js} +28 -12
  82. package/dist/recovery-B7DZQ6XG.js.map +1 -0
  83. package/dist/{releases-LUAHKIMY.js → releases-XY57V22L.js} +33 -8
  84. package/dist/releases-XY57V22L.js.map +1 -0
  85. package/dist/reports-CIB2T3XT.js +0 -0
  86. package/dist/{reviews-YCBBM656.js → reviews-P4M6BEJI.js} +4 -6
  87. package/dist/reviews-P4M6BEJI.js.map +1 -0
  88. package/dist/{rtdn-LID2B7XZ.js → rtdn-YII4H6SD.js} +29 -21
  89. package/dist/rtdn-YII4H6SD.js.map +1 -0
  90. package/dist/{status-3HXBBXG6.js → status-32AJ7A6O.js} +13 -32
  91. package/dist/status-32AJ7A6O.js.map +1 -0
  92. package/dist/{subscriptions-LURZFPGJ.js → subscriptions-GTVJB4HX.js} +49 -12
  93. package/dist/subscriptions-GTVJB4HX.js.map +1 -0
  94. package/dist/system-apks-L6M7QYB3.js +111 -0
  95. package/dist/system-apks-L6M7QYB3.js.map +1 -0
  96. package/dist/{testers-6CQL4KQV.js → testers-QUWZHO6M.js} +25 -7
  97. package/dist/testers-QUWZHO6M.js.map +1 -0
  98. package/dist/{tracks-I4QZNZ3M.js → tracks-RH3RKVFB.js} +9 -3
  99. package/dist/{tracks-I4QZNZ3M.js.map → tracks-RH3RKVFB.js.map} +1 -1
  100. package/dist/{train-MDD2EBHS.js → train-D2NKUW3M.js} +3 -3
  101. package/dist/train-D2NKUW3M.js.map +1 -0
  102. package/dist/{update-FZ3MNLOH.js → update-73YOR4GP.js} +8 -11
  103. package/dist/{update-FZ3MNLOH.js.map → update-73YOR4GP.js.map} +1 -1
  104. package/dist/{users-UKG7VIQH.js → users-FOMAT7DY.js} +2 -2
  105. package/dist/{validate-QIYSA3N7.js → validate-RHWEZYD2.js} +6 -2
  106. package/dist/validate-RHWEZYD2.js.map +1 -0
  107. package/dist/{verify-UUQNQMPG.js → verify-H4ZUVHMZ.js} +5 -13
  108. package/dist/verify-H4ZUVHMZ.js.map +1 -0
  109. package/dist/{version-VHQBXU2I.js → version-RXLEX62V.js} +3 -3
  110. package/dist/{vitals-PJEQUUAK.js → vitals-PRBPNMVC.js} +26 -8
  111. package/dist/vitals-PRBPNMVC.js.map +1 -0
  112. package/package.json +18 -18
  113. package/dist/anomalies-V3AFS4LD.js.map +0 -1
  114. package/dist/auth-BA4FE2PO.js.map +0 -1
  115. package/dist/bundle-F7MUVC5J.js.map +0 -1
  116. package/dist/cache-XKPLZYEB.js.map +0 -1
  117. package/dist/chunk-A7VRCCNS.js.map +0 -1
  118. package/dist/chunk-BCBXQC7J.js.map +0 -1
  119. package/dist/chunk-FXOWADQD.js.map +0 -1
  120. package/dist/chunk-NQH4G7BI.js.map +0 -1
  121. package/dist/chunk-SLNJEAMK.js.map +0 -1
  122. package/dist/chunk-YFUBD2XB.js.map +0 -1
  123. package/dist/data-safety-AFMD6MYI.js.map +0 -1
  124. package/dist/device-tiers-AQAMUQXI.js.map +0 -1
  125. package/dist/diff-6EO4ID6W.js.map +0 -1
  126. package/dist/docs-GMFN6V4K.js.map +0 -1
  127. package/dist/doctor-7LQWPY5P.js.map +0 -1
  128. package/dist/external-transactions-LCZALS3V.js.map +0 -1
  129. package/dist/feedback-7ADYSGRD.js.map +0 -1
  130. package/dist/games-ZSNGEI7A.js.map +0 -1
  131. package/dist/generated-apks-RX2IUWSF.js.map +0 -1
  132. package/dist/grants-EBPECI26.js.map +0 -1
  133. package/dist/iap-OUI5YYN4.js.map +0 -1
  134. package/dist/init-WSTQTJOD.js.map +0 -1
  135. package/dist/internal-sharing-ONNIWIAT.js.map +0 -1
  136. package/dist/listings-LNX6MQYN.js.map +0 -1
  137. package/dist/one-time-products-MGZTU7OM.js +0 -254
  138. package/dist/one-time-products-MGZTU7OM.js.map +0 -1
  139. package/dist/preflight-W3JAJ4GO.js.map +0 -1
  140. package/dist/pricing-JJZFICFL.js.map +0 -1
  141. package/dist/publish-P5KIGSLI.js.map +0 -1
  142. package/dist/purchases-UBFLNYZC.js.map +0 -1
  143. package/dist/quickstart-Z5Y3FYJU.js.map +0 -1
  144. package/dist/recovery-YE3Z7NIN.js.map +0 -1
  145. package/dist/releases-LUAHKIMY.js.map +0 -1
  146. package/dist/reviews-YCBBM656.js.map +0 -1
  147. package/dist/rtdn-LID2B7XZ.js.map +0 -1
  148. package/dist/status-3HXBBXG6.js.map +0 -1
  149. package/dist/subscriptions-LURZFPGJ.js.map +0 -1
  150. package/dist/testers-6CQL4KQV.js.map +0 -1
  151. package/dist/train-MDD2EBHS.js.map +0 -1
  152. package/dist/validate-QIYSA3N7.js.map +0 -1
  153. package/dist/verify-UUQNQMPG.js.map +0 -1
  154. package/dist/vitals-PJEQUUAK.js.map +0 -1
  155. /package/dist/{apps-4GP3FD7O.js.map → apps-WPUQOQLL.js.map} +0 -0
  156. /package/dist/{audit-VTWXTXC6.js.map → audit-M7GYID74.js.map} +0 -0
  157. /package/dist/{config-BLMJ35J2.js.map → config-PUINDZON.js.map} +0 -0
  158. /package/dist/{prompt-GXC2JSLA.js.map → prompt-HJXNXXAR.js.map} +0 -0
  159. /package/dist/{users-UKG7VIQH.js.map → users-FOMAT7DY.js.map} +0 -0
  160. /package/dist/{version-VHQBXU2I.js.map → version-RXLEX62V.js.map} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  requireConfirm,
8
8
  requireOption,
9
9
  skipConfirm
10
- } from "./chunk-YFUBD2XB.js";
10
+ } from "./chunk-RZQSEDKI.js";
11
11
  export {
12
12
  isInteractive,
13
13
  promptConfirm,
@@ -17,4 +17,4 @@ export {
17
17
  requireOption,
18
18
  skipConfirm
19
19
  };
20
- //# sourceMappingURL=prompt-GXC2JSLA.js.map
20
+ //# sourceMappingURL=prompt-HJXNXXAR.js.map
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildCommitOptions
4
- } from "./chunk-A7VRCCNS.js";
4
+ } from "./chunk-WSLFHX5X.js";
5
5
  import {
6
6
  resolvePackageName
7
- } from "./chunk-NQH4G7BI.js";
7
+ } from "./chunk-JDRY7HK5.js";
8
8
  import {
9
9
  isDryRun
10
10
  } from "./chunk-Y3QZDAKS.js";
@@ -15,7 +15,7 @@ import {
15
15
  isInteractive,
16
16
  promptInput,
17
17
  promptSelect
18
- } from "./chunk-YFUBD2XB.js";
18
+ } from "./chunk-RZQSEDKI.js";
19
19
 
20
20
  // src/commands/publish.ts
21
21
  import { appendFile, stat } from "fs/promises";
@@ -83,7 +83,13 @@ function formatDryRunOutput(result, format) {
83
83
  return lines.join("\n");
84
84
  }
85
85
  function registerPublishCommand(program) {
86
- program.command("publish <file>").description("Validate, upload, and release in one step").option("--track <track>", "Target track", "internal").option("--rollout <percent>", "Staged rollout percentage (1-100)").option("--notes <text>", "Release notes (en-US)").option("--notes-dir <dir>", "Read release notes from directory (<dir>/<lang>.txt)").option("--notes-from-git", "Generate release notes from git commit history").option("--since <ref>", "Git ref to start from (tag, SHA) \u2014 used with --notes-from-git").option("--name <name>", "Release name").option("--mapping <file>", "ProGuard/R8 mapping file for deobfuscation").option("--retry-log <path>", "Write retry log entries to file (JSONL)").option("--mapping-type <type>", "Deobfuscation file type: proguard or nativeCode", "proguard").option("--device-tier-config <id>", "Device tier config ID (or LATEST)").option("--changes-not-sent-for-review", "Commit changes without sending for review (required for rejected apps)").option("--error-if-in-review", "Fail if changes are already in review instead of cancelling them").action(async (file, options) => {
86
+ program.command("publish <file>").description("Validate, upload, and release in one step").option("--track <track>", "Target track", "internal").option("--rollout <percent>", "Staged rollout percentage (1-100)").option("--notes <text>", "Release notes (en-US)").option("--notes-dir <dir>", "Read release notes from directory (<dir>/<lang>.txt)").option("--notes-from-git", "Generate release notes from git commit history").option("--since <ref>", "Git ref to start from (tag, SHA) \u2014 used with --notes-from-git").option("--name <name>", "Release name").option("--mapping <file>", "ProGuard/R8 mapping file for deobfuscation").option("--retry-log <path>", "Write retry log entries to file (JSONL)").option("--mapping-type <type>", "Deobfuscation file type: proguard or nativeCode", "proguard").option("--device-tier-config <id>", "Device tier config ID (or LATEST)").option(
87
+ "--changes-not-sent-for-review",
88
+ "Commit changes without sending for review (required for rejected apps)"
89
+ ).option(
90
+ "--error-if-in-review",
91
+ "Fail if changes are already in review instead of cancelling them"
92
+ ).action(async (file, options) => {
87
93
  try {
88
94
  await stat(file);
89
95
  } catch {
@@ -221,4 +227,4 @@ function registerPublishCommand(program) {
221
227
  export {
222
228
  registerPublishCommand
223
229
  };
224
- //# sourceMappingURL=publish-P5KIGSLI.js.map
230
+ //# sourceMappingURL=publish-26SSZ2W3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { resolvePackageName } from \"../resolve.js\";\nimport { appendFile, stat } from \"node:fs/promises\";\nimport type { 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 GpcError,\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\";\nimport { buildCommitOptions } from \"../commit-options.js\";\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\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 .option(\"--mapping-type <type>\", \"Deobfuscation file type: proguard or nativeCode\", \"proguard\")\n .option(\"--device-tier-config <id>\", \"Device tier config ID (or LATEST)\")\n .option(\n \"--changes-not-sent-for-review\",\n \"Commit changes without sending for review (required for rejected apps)\",\n )\n .option(\n \"--error-if-in-review\",\n \"Fail if changes are already in review instead of cancelling them\",\n )\n .action(async (file: string, options) => {\n try {\n await stat(file);\n } catch {\n throw new GpcError(\n `File not found: ${file}`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Check the file path and try again.\",\n );\n }\n\n const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);\n if (noteSources.length > 1) {\n throw new GpcError(\n \"Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.\",\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Pick one release notes source.\",\n );\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 throw new GpcError(\n `--rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Use a percentage between 1 and 100.\",\n );\n }\n }\n\n if (\n options.mappingType &&\n options.mappingType !== \"proguard\" &&\n options.mappingType !== \"nativeCode\"\n ) {\n throw new GpcError(\n `--mapping-type must be \"proguard\" or \"nativeCode\" (got: \"${options.mappingType}\")`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Use --mapping-type proguard (default) for ProGuard/R8 maps, or --mapping-type nativeCode for native debug symbols.\",\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 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 mappingFileType: options.mappingType,\n deviceTierConfigId: options.deviceTierConfig,\n commitOptions: buildCommitOptions(options),\n dryRun: true,\n });\n console.log(formatDryRunOutput(result as DryRunPublishResult, format));\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 mappingFileType: options.mappingType,\n deviceTierConfigId: options.deviceTierConfig,\n commitOptions: buildCommitOptions(options),\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.exitCode = 1;\n return;\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 throw error;\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n}\n"],"mappings":"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","names":[]}
File without changes
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getClient,
4
4
  resolvePackageName
5
- } from "./chunk-NQH4G7BI.js";
5
+ } from "./chunk-JDRY7HK5.js";
6
6
  import {
7
7
  isDryRun,
8
8
  printDryRun
@@ -14,7 +14,7 @@ import {
14
14
  isInteractive,
15
15
  requireConfirm,
16
16
  requireOption
17
- } from "./chunk-YFUBD2XB.js";
17
+ } from "./chunk-RZQSEDKI.js";
18
18
 
19
19
  // src/commands/purchases.ts
20
20
  import { Option } from "commander";
@@ -30,7 +30,6 @@ import {
30
30
  deferSubscriptionPurchase,
31
31
  deferSubscriptionV2,
32
32
  revokeSubscriptionPurchase,
33
- refundSubscriptionV2,
34
33
  listVoidedPurchases,
35
34
  refundOrder,
36
35
  getOrderDetails,
@@ -183,26 +182,16 @@ function registerPurchasesCommands(program) {
183
182
  );
184
183
  console.log(formatOutput(result, format));
185
184
  });
186
- sub.command("refund <token>").description("Refund a subscription purchase (v2)").action(async (token) => {
187
- const config = await loadConfig();
188
- const packageName = resolvePackageName(program.opts()["app"], config);
189
- await requireConfirm(`Refund subscription for token "${token.slice(0, 16)}..."?`, program);
190
- if (isDryRun(program)) {
191
- const format = getOutputFormat(program, config);
192
- printDryRun(
193
- {
194
- command: "purchases subscription refund",
195
- action: "refund subscription",
196
- target: token
197
- },
198
- format,
199
- formatOutput
200
- );
201
- return;
202
- }
203
- const client = await getClient(config);
204
- await refundSubscriptionV2(client, packageName, token);
205
- console.log(`Subscription refunded.`);
185
+ sub.command("refund <token>").description("Refund a subscription purchase (use 'orders refund' instead)").action(async () => {
186
+ console.error(
187
+ `The subscriptionsv2 refund endpoint does not exist in the Google Play API.
188
+
189
+ Use one of these instead:
190
+ gpc purchases orders refund <order-id> Refund via orders API
191
+ gpc purchases subscription revoke <token> Revoke + refund via v2 API
192
+ `
193
+ );
194
+ process.exit(2);
206
195
  });
207
196
  sub.command("revoke <token>").description("Revoke a subscription (v2)").action(async (token) => {
208
197
  const config = await loadConfig();
@@ -220,15 +209,16 @@ function registerPurchasesCommands(program) {
220
209
  );
221
210
  return;
222
211
  }
223
- await requireConfirm(
224
- `Revoke subscription and refund? This cannot be undone.`,
225
- program
226
- );
212
+ await requireConfirm(`Revoke subscription and refund? This cannot be undone.`, program);
227
213
  const client = await getClient(config);
228
214
  await revokeSubscriptionPurchase(client, packageName, token);
229
215
  console.log(`Subscription revoked.`);
230
216
  });
231
- purchases.command("voided").description("List voided purchases").option("--start-time <time>", "Start time (milliseconds)").option("--end-time <time>", "End time (milliseconds)").option("--type <n>", "Purchase type: 0=in-app only (default), 1=in-app + subscriptions", parseInt).option("--include-partial-refunds", "Include quantity-based partial refunds").addOption(
217
+ purchases.command("voided").description("List voided purchases").option("--start-time <time>", "Start time (milliseconds)").option("--end-time <time>", "End time (milliseconds)").option(
218
+ "--type <n>",
219
+ "Purchase type: 0=in-app only (default), 1=in-app + subscriptions",
220
+ parseInt
221
+ ).option("--include-partial-refunds", "Include quantity-based partial refunds").addOption(
232
222
  new Option("--max-results <n>", "Maximum results per page").argParser(parseInt).hideHelp()
233
223
  ).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").action(async (options) => {
234
224
  const config = await loadConfig();
@@ -352,13 +342,21 @@ function registerPurchasesCommands(program) {
352
342
  console.log(formatOutput(result, format));
353
343
  }
354
344
  });
355
- sub.command("cancel-v2 <token>").description("Cancel a subscription (v2 \u2014 supports cancellation types)").option("--type <cancellationType>", "Cancellation type (e.g., USER_CANCELED, SYSTEM_CANCELED, DEVELOPER_CANCELED, REPLACED)").action(async (token, options) => {
345
+ sub.command("cancel-v2 <token>").description("Cancel a subscription (v2 \u2014 supports cancellation types)").option(
346
+ "--type <cancellationType>",
347
+ "Cancellation type (e.g., USER_CANCELED, SYSTEM_CANCELED, DEVELOPER_CANCELED, REPLACED)"
348
+ ).action(async (token, options) => {
356
349
  const config = await loadConfig();
357
350
  const packageName = resolvePackageName(program.opts()["app"], config);
358
351
  if (isDryRun(program)) {
359
352
  const format = getOutputFormat(program, config);
360
353
  printDryRun(
361
- { command: "purchases subscription cancel-v2", action: "cancel", target: token.slice(0, 16) + "...", details: { cancellationType: options.type } },
354
+ {
355
+ command: "purchases subscription cancel-v2",
356
+ action: "cancel",
357
+ target: token.slice(0, 16) + "...",
358
+ details: { cancellationType: options.type }
359
+ },
362
360
  format,
363
361
  formatOutput
364
362
  );
@@ -379,7 +377,12 @@ function registerPurchasesCommands(program) {
379
377
  if (isDryRun(program)) {
380
378
  const format = getOutputFormat(program, config);
381
379
  printDryRun(
382
- { command: "purchases subscription defer-v2", action: "defer", target: token.slice(0, 16) + "...", details: { desiredExpiryTime: options.until } },
380
+ {
381
+ command: "purchases subscription defer-v2",
382
+ action: "defer",
383
+ target: token.slice(0, 16) + "...",
384
+ details: { desiredExpiryTime: options.until }
385
+ },
383
386
  format,
384
387
  formatOutput
385
388
  );
@@ -393,4 +396,4 @@ function registerPurchasesCommands(program) {
393
396
  export {
394
397
  registerPurchasesCommands
395
398
  };
396
- //# sourceMappingURL=purchases-UBFLNYZC.js.map
399
+ //# sourceMappingURL=purchases-AHWGLW6V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/purchases.ts"],"sourcesContent":["import { resolvePackageName, getClient } from \"../resolve.js\";\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\n\nimport {\n getProductPurchase,\n getProductPurchaseV2,\n acknowledgeProductPurchase,\n consumeProductPurchase,\n getSubscriptionPurchase,\n cancelSubscriptionPurchase,\n cancelSubscriptionV2,\n deferSubscriptionPurchase,\n deferSubscriptionV2,\n revokeSubscriptionPurchase,\n listVoidedPurchases,\n refundOrder,\n getOrderDetails,\n batchGetOrders,\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\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 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 });\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 await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);\n console.log(`Purchase acknowledged.`);\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 await consumeProductPurchase(client, packageName, productId, token);\n console.log(`Purchase consumed.`);\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 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 });\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 await requireConfirm(\n `Cancel subscription ${subscriptionId}? This cannot be undone.`,\n program,\n );\n\n const client = await getClient(config);\n\n await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);\n console.log(`Subscription cancelled.`);\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 const result = await deferSubscriptionPurchase(\n client,\n packageName,\n subscriptionId,\n token,\n options.expiry,\n );\n console.log(formatOutput(result, format));\n });\n\n sub\n .command(\"refund <token>\")\n .description(\"Refund a subscription purchase (use 'orders refund' instead)\")\n .action(async () => {\n console.error(\n `The subscriptionsv2 refund endpoint does not exist in the Google Play API.\\n\\n` +\n `Use one of these instead:\\n` +\n ` gpc purchases orders refund <order-id> Refund via orders API\\n` +\n ` gpc purchases subscription revoke <token> Revoke + refund via v2 API\\n`,\n );\n process.exit(2);\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 await requireConfirm(`Revoke subscription and refund? This cannot be undone.`, program);\n\n const client = await getClient(config);\n\n await revokeSubscriptionPurchase(client, packageName, token);\n console.log(`Subscription revoked.`);\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 .option(\n \"--type <n>\",\n \"Purchase type: 0=in-app only (default), 1=in-app + subscriptions\",\n parseInt,\n )\n .option(\"--include-partial-refunds\", \"Include quantity-based partial refunds\")\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 const result = await listVoidedPurchases(client, packageName, {\n startTime: options.startTime,\n endTime: options.endTime,\n type: options.type,\n includeQuantityBasedPartialRefund: options.includePartialRefunds,\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 });\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 await refundOrder(client, packageName, orderId, {\n fullRefund: options.fullRefund,\n proratedRefund: options.proratedRefund,\n });\n console.log(`Order ${orderId} refunded.`);\n });\n\n orders\n .command(\"get <order-id>\")\n .description(\"Get order details\")\n .action(async (orderId: 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 const result = await getOrderDetails(client, packageName, orderId);\n if (format !== \"json\") {\n const row = {\n orderId: result.orderId,\n state: result.state,\n purchaseToken: result.purchaseToken ? result.purchaseToken.slice(0, 16) + \"...\" : \"-\",\n createTime: result.createTime || \"-\",\n total: result.total\n ? `${result.total.units || \"0\"}.${String(result.total.nanos || 0)\n .padStart(9, \"0\")\n .slice(0, 2)} ${result.total.currencyCode}`\n : \"-\",\n lineItems: result.lineItems?.length || 0,\n };\n console.log(formatOutput(row, format));\n } else {\n console.log(formatOutput(result, format));\n }\n });\n\n orders\n .command(\"batch-get\")\n .description(\"Get multiple orders at once\")\n .requiredOption(\"--ids <order-ids>\", \"Comma-separated order IDs (max 1000)\")\n .action(async (options: { ids: 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 const orderIds = options.ids\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n\n const result = await batchGetOrders(client, packageName, orderIds);\n if (format !== \"json\") {\n if (result.length === 0) {\n console.log(\"No orders found.\");\n } else {\n const rows = result.map((o) => ({\n orderId: o.orderId,\n state: o.state,\n createTime: o.createTime || \"-\",\n lineItems: o.lineItems?.length || 0,\n }));\n console.log(formatOutput(rows, format));\n }\n } else {\n console.log(formatOutput(result, format));\n }\n });\n\n // --- Product purchases V2 (Jun 2025) ---\n const product = purchases.command(\"product\").description(\"Product purchase operations\");\n\n product\n .command(\"get-v2 <token>\")\n .description(\"Get product purchase details (v2 — supports multi-offer OTPs)\")\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 const result = await getProductPurchaseV2(client, packageName, token);\n if (format !== \"json\") {\n const row = {\n orderId: result.orderId || \"-\",\n state: result.purchaseStateContext?.state || \"-\",\n regionCode: result.regionCode || \"-\",\n completionTime: result.purchaseCompletionTime || \"-\",\n acknowledgement: result.acknowledgementState || \"-\",\n lineItems: result.productLineItem?.length || 0,\n };\n console.log(formatOutput(row, format));\n } else {\n console.log(formatOutput(result, format));\n }\n });\n\n // --- Subscription V2 cancel/defer (Sep 2025 / Jan 2026) ---\n // Added to existing `sub` group alongside v1 cancel/defer\n\n sub\n .command(\"cancel-v2 <token>\")\n .description(\"Cancel a subscription (v2 — supports cancellation types)\")\n .option(\n \"--type <cancellationType>\",\n \"Cancellation type (e.g., USER_CANCELED, SYSTEM_CANCELED, DEVELOPER_CANCELED, REPLACED)\",\n )\n .action(async (token: string, options: { type?: 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-v2\",\n action: \"cancel\",\n target: token.slice(0, 16) + \"...\",\n details: { cancellationType: options.type },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n await requireConfirm(\n `Cancel subscription (token: ${token.slice(0, 16)}...)? This cannot be undone.`,\n program,\n );\n\n const client = await getClient(config);\n\n await cancelSubscriptionV2(client, packageName, token, options.type);\n console.log(\"Subscription cancelled.\");\n });\n\n sub\n .command(\"defer-v2 <token>\")\n .description(\"Defer a subscription renewal (v2 — supports add-on subscriptions)\")\n .requiredOption(\"--until <date>\", \"Desired expiry time (ISO 8601 date)\")\n .action(async (token: string, options: { until: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Defer subscription renewal to ${options.until}?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"purchases subscription defer-v2\",\n action: \"defer\",\n target: token.slice(0, 16) + \"...\",\n details: { desiredExpiryTime: options.until },\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n const result = await deferSubscriptionV2(client, packageName, token, options.until);\n console.log(`Subscription deferred. New expiry: ${result.newExpiryTime}`);\n });\n}\n"],"mappings":"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","names":["purchases"]}
@@ -52,7 +52,12 @@ function registerQuickstartCommand(program) {
52
52
  encoding: "utf-8"
53
53
  });
54
54
  if (result.error) {
55
- step(4, total, "Running doctor...", "\u2014 could not find gpc in PATH. Run: gpc doctor");
55
+ step(
56
+ 4,
57
+ total,
58
+ "Running doctor...",
59
+ "\u2014 could not find gpc in PATH. Run: gpc doctor"
60
+ );
56
61
  } else if (result.status === 0) {
57
62
  step(4, total, "Running doctor...", "\u2713 All checks passed");
58
63
  } else {
@@ -86,4 +91,4 @@ function registerQuickstartCommand(program) {
86
91
  export {
87
92
  registerQuickstartCommand
88
93
  };
89
- //# sourceMappingURL=quickstart-Z5Y3FYJU.js.map
94
+ //# sourceMappingURL=quickstart-FOWM3OKT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/quickstart.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\n\nfunction step(n: number, total: number, label: string, status: string): void {\n const padded = `Step ${n}/${total} ${label}`.padEnd(48);\n console.log(`${padded}${status}`);\n}\n\nexport function registerQuickstartCommand(program: Command): void {\n program\n .command(\"quickstart\")\n .description(\"Guided setup: verify credentials, config, and show next steps\")\n .action(async () => {\n console.log(\"\\nGPC — Quick Start Setup\");\n console.log(\"══════════════════════════════════════════════════════\");\n\n const total = 4;\n let allPassed = true;\n\n // Step 1: Check for existing config\n let config: Awaited<ReturnType<typeof loadConfig>> | null = null;\n try {\n config = await loadConfig();\n const profileInfo = config.profile ? `profile \"${config.profile}\"` : \"default config\";\n step(1, total, \"Checking for existing config...\", `✓ Found ${profileInfo}`);\n } catch {\n step(1, total, \"Checking for existing config...\", \"⚠ No config found\");\n console.log(\"\\n Run: gpc config init\");\n console.log(\" Or: gpc auth login (runs interactive setup)\");\n allPassed = false;\n }\n\n // Step 2: Verify credentials\n if (config) {\n try {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n const email = auth.getClientEmail();\n step(2, total, \"Verifying credentials...\", `✓ ${email}`);\n } catch {\n step(2, total, \"Verifying credentials...\", \"✗ Auth failed — run: gpc auth login\");\n allPassed = false;\n }\n } else {\n step(2, total, \"Verifying credentials...\", \"— skipped (no config)\");\n allPassed = false;\n }\n\n // Step 3: Check package name\n const packageName = config?.app ?? (program.opts()[\"app\"] as string | undefined);\n if (packageName) {\n step(3, total, \"Checking package name...\", `✓ ${packageName}`);\n } else {\n step(3, total, \"Checking package name...\", \"⚠ Not set — run: gpc config set app <package>\");\n allPassed = false;\n }\n\n // Step 4: Run doctor inline\n if (allPassed) {\n try {\n const { spawnSync } = await import(\"node:child_process\");\n // Use \"gpc\" directly — process.execPath + process.argv[1] fails when\n // installed via Homebrew (Bun-compiled binary can't be re-run under Node)\n const result = spawnSync(\"gpc\", [\"doctor\"], {\n stdio: \"pipe\",\n encoding: \"utf-8\",\n });\n if (result.error) {\n // gpc not found in PATH (ENOENT) or other spawn error\n step(\n 4,\n total,\n \"Running doctor...\",\n \"\\u2014 could not find gpc in PATH. Run: gpc doctor\",\n );\n } else if (result.status === 0) {\n step(4, total, \"Running doctor...\", \"\\u2713 All checks passed\");\n } else {\n step(4, total, \"Running doctor...\", \"\\u26A0 Some checks failed \\u2014 run: gpc doctor\");\n allPassed = false;\n }\n } catch {\n step(4, total, \"Running doctor...\", \"\\u2014 run: gpc doctor\");\n }\n } else {\n step(4, total, \"Running doctor...\", \"\\u2014 skipped\");\n }\n\n console.log(\"\");\n\n if (allPassed) {\n console.log(\"Ready. Here's what you can do next:\");\n console.log(\"\");\n console.log(\" gpc status → app health snapshot\");\n console.log(\" gpc releases list → current tracks and versions\");\n console.log(\" gpc reviews list → recent user reviews\");\n console.log(\" gpc vitals overview → crash and ANR rates\");\n console.log(\" gpc publish app.aab → end-to-end upload and release\");\n console.log(\"\");\n console.log(\"Docs: https://yasserstudio.github.io/gpc/\");\n } else {\n console.log(\"Fix the issues above, then run 'gpc quickstart' again.\");\n console.log(\"Need help? Run 'gpc doctor' for detailed diagnostics.\");\n process.exitCode = 1;\n }\n });\n}\n"],"mappings":";;;AACA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAE5B,SAAS,KAAK,GAAW,OAAe,OAAe,QAAsB;AAC3E,QAAM,SAAS,QAAQ,CAAC,IAAI,KAAK,KAAK,KAAK,GAAG,OAAO,EAAE;AACvD,UAAQ,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE;AAClC;AAEO,SAAS,0BAA0B,SAAwB;AAChE,UACG,QAAQ,YAAY,EACpB,YAAY,+DAA+D,EAC3E,OAAO,YAAY;AAClB,YAAQ,IAAI,gCAA2B;AACvC,YAAQ,IAAI,sUAAwD;AAEpE,UAAM,QAAQ;AACd,QAAI,YAAY;AAGhB,QAAI,SAAwD;AAC5D,QAAI;AACF,eAAS,MAAM,WAAW;AAC1B,YAAM,cAAc,OAAO,UAAU,YAAY,OAAO,OAAO,MAAM;AACrE,WAAK,GAAG,OAAO,mCAAmC,gBAAW,WAAW,EAAE;AAAA,IAC5E,QAAQ;AACN,WAAK,GAAG,OAAO,mCAAmC,wBAAmB;AACrE,cAAQ,IAAI,0BAA0B;AACtC,cAAQ,IAAI,iDAAiD;AAC7D,kBAAY;AAAA,IACd;AAGA,QAAI,QAAQ;AACV,UAAI;AACF,cAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,cAAM,QAAQ,KAAK,eAAe;AAClC,aAAK,GAAG,OAAO,4BAA4B,UAAK,KAAK,EAAE;AAAA,MACzD,QAAQ;AACN,aAAK,GAAG,OAAO,4BAA4B,+CAAqC;AAChF,oBAAY;AAAA,MACd;AAAA,IACF,OAAO;AACL,WAAK,GAAG,OAAO,4BAA4B,4BAAuB;AAClE,kBAAY;AAAA,IACd;AAGA,UAAM,cAAc,QAAQ,OAAQ,QAAQ,KAAK,EAAE,KAAK;AACxD,QAAI,aAAa;AACf,WAAK,GAAG,OAAO,4BAA4B,UAAK,WAAW,EAAE;AAAA,IAC/D,OAAO;AACL,WAAK,GAAG,OAAO,4BAA4B,yDAA+C;AAC1F,kBAAY;AAAA,IACd;AAGA,QAAI,WAAW;AACb,UAAI;AACF,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,eAAoB;AAGvD,cAAM,SAAS,UAAU,OAAO,CAAC,QAAQ,GAAG;AAAA,UAC1C,OAAO;AAAA,UACP,UAAU;AAAA,QACZ,CAAC;AACD,YAAI,OAAO,OAAO;AAEhB;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,WAAW,OAAO,WAAW,GAAG;AAC9B,eAAK,GAAG,OAAO,qBAAqB,0BAA0B;AAAA,QAChE,OAAO;AACL,eAAK,GAAG,OAAO,qBAAqB,kDAAkD;AACtF,sBAAY;AAAA,QACd;AAAA,MACF,QAAQ;AACN,aAAK,GAAG,OAAO,qBAAqB,wBAAwB;AAAA,MAC9D;AAAA,IACF,OAAO;AACL,WAAK,GAAG,OAAO,qBAAqB,gBAAgB;AAAA,IACtD;AAEA,YAAQ,IAAI,EAAE;AAEd,QAAI,WAAW;AACb,cAAQ,IAAI,qCAAqC;AACjD,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,sDAAiD;AAC7D,cAAQ,IAAI,8DAAyD;AACrE,cAAQ,IAAI,sDAAiD;AAC7D,cAAQ,IAAI,sDAAiD;AAC7D,cAAQ,IAAI,gEAA2D;AACvE,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,2CAA2C;AAAA,IACzD,OAAO;AACL,cAAQ,IAAI,wDAAwD;AACpE,cAAQ,IAAI,uDAAuD;AACnE,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,CAAC;AACL;","names":[]}
File without changes
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getClient,
4
4
  resolvePackageName
5
- } from "./chunk-NQH4G7BI.js";
5
+ } from "./chunk-JDRY7HK5.js";
6
6
  import {
7
7
  isDryRun,
8
8
  printDryRun
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-ELXAK7GI.js";
13
13
  import {
14
14
  requireConfirm
15
- } from "./chunk-YFUBD2XB.js";
15
+ } from "./chunk-RZQSEDKI.js";
16
16
 
17
17
  // src/commands/recovery.ts
18
18
  import { loadConfig } from "@gpc-cli/config";
@@ -33,8 +33,14 @@ function registerRecoveryCommands(program) {
33
33
  const client = await getClient(config);
34
34
  const format = getOutputFormat(program, config);
35
35
  if (options.versionCode === void 0) {
36
- const err = new Error("--version-code is required. The API requires a version code to filter recovery actions.");
37
- Object.assign(err, { code: "USAGE_ERROR", exitCode: 2, suggestion: "Usage: gpc recovery list --version-code <code>" });
36
+ const err = new Error(
37
+ "--version-code is required. The API requires a version code to filter recovery actions."
38
+ );
39
+ Object.assign(err, {
40
+ code: "USAGE_ERROR",
41
+ exitCode: 2,
42
+ suggestion: "Usage: gpc recovery list --version-code <code>"
43
+ });
38
44
  throw err;
39
45
  }
40
46
  const result = await listRecoveryActions(client, packageName, options.versionCode);
@@ -67,9 +73,7 @@ function registerRecoveryCommands(program) {
67
73
  await requireConfirm(`Cancel recovery action ${id}?`, program);
68
74
  const client = await getClient(config);
69
75
  await cancelRecoveryAction(client, packageName, id);
70
- console.log(
71
- formatOutput({ success: true, appRecoveryId: id, action: "cancelled" }, format)
72
- );
76
+ console.log(formatOutput({ success: true, appRecoveryId: id, action: "cancelled" }, format));
73
77
  });
74
78
  recovery.command("deploy <id>").description("Deploy a recovery action").action(async (id) => {
75
79
  const config = await loadConfig();
@@ -100,8 +104,14 @@ function registerRecoveryCommands(program) {
100
104
  try {
101
105
  data = JSON.parse(readFileSync(options.file, "utf-8"));
102
106
  } catch (err) {
103
- const error = new Error(`Could not read recovery action data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`);
104
- Object.assign(error, { code: "INVALID_INPUT", exitCode: 2, suggestion: "Check the file path and ensure it contains valid JSON." });
107
+ const error = new Error(
108
+ `Could not read recovery action data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`
109
+ );
110
+ Object.assign(error, {
111
+ code: "INVALID_INPUT",
112
+ exitCode: 2,
113
+ suggestion: "Check the file path and ensure it contains valid JSON."
114
+ });
105
115
  throw error;
106
116
  }
107
117
  if (isDryRun(program)) {
@@ -129,8 +139,14 @@ function registerRecoveryCommands(program) {
129
139
  try {
130
140
  targeting = JSON.parse(readFileSync(options.file, "utf-8"));
131
141
  } catch (err) {
132
- const error = new Error(`Could not read targeting data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`);
133
- Object.assign(error, { code: "INVALID_INPUT", exitCode: 2, suggestion: "Check the file path and ensure it contains valid JSON." });
142
+ const error = new Error(
143
+ `Could not read targeting data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`
144
+ );
145
+ Object.assign(error, {
146
+ code: "INVALID_INPUT",
147
+ exitCode: 2,
148
+ suggestion: "Check the file path and ensure it contains valid JSON."
149
+ });
134
150
  throw error;
135
151
  }
136
152
  if (isDryRun(program)) {
@@ -154,4 +170,4 @@ function registerRecoveryCommands(program) {
154
170
  export {
155
171
  registerRecoveryCommands
156
172
  };
157
- //# sourceMappingURL=recovery-YE3Z7NIN.js.map
173
+ //# sourceMappingURL=recovery-B7DZQ6XG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/recovery.ts"],"sourcesContent":["import { resolvePackageName, getClient } from \"../resolve.js\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\n\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\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 if (options.versionCode === undefined) {\n const err = new Error(\n \"--version-code is required. The API requires a version code to filter recovery actions.\",\n );\n Object.assign(err, {\n code: \"USAGE_ERROR\",\n exitCode: 2,\n suggestion: \"Usage: gpc recovery list --version-code <code>\",\n });\n throw err;\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 });\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 await cancelRecoveryAction(client, packageName, id);\n console.log(formatOutput({ success: true, appRecoveryId: id, action: \"cancelled\" }, format));\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 await deployRecoveryAction(client, packageName, id);\n console.log(formatOutput({ success: true, appRecoveryId: id, action: \"deployed\" }, format));\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 const error = new Error(\n `Could not read recovery action data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n Object.assign(error, {\n code: \"INVALID_INPUT\",\n exitCode: 2,\n suggestion: \"Check the file path and ensure it contains valid JSON.\",\n });\n throw error;\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 const result = await createRecoveryAction(client, packageName, data);\n console.log(formatOutput(result, format));\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 const error = new Error(\n `Could not read targeting data from ${options.file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n Object.assign(error, {\n code: \"INVALID_INPUT\",\n exitCode: 2,\n suggestion: \"Check the file path and ensure it contains valid JSON.\",\n });\n throw error;\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 const result = await addRecoveryTargeting(client, packageName, actionId, targeting);\n console.log(formatOutput(result, format));\n });\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildCommitOptions
4
- } from "./chunk-A7VRCCNS.js";
4
+ } from "./chunk-WSLFHX5X.js";
5
5
  import {
6
6
  isDryRun,
7
7
  printDryRun
@@ -15,7 +15,7 @@ import {
15
15
  promptSelect,
16
16
  requireConfirm,
17
17
  requireOption
18
- } from "./chunk-YFUBD2XB.js";
18
+ } from "./chunk-RZQSEDKI.js";
19
19
 
20
20
  // src/commands/releases.ts
21
21
  import { appendFile, stat } from "fs/promises";
@@ -70,7 +70,17 @@ function registerReleasesCommands(program) {
70
70
  "--timeout <ms>",
71
71
  "Upload timeout in milliseconds (auto-scales with file size by default)",
72
72
  parseInt
73
- ).option("--status <status>", "Release status: completed, inProgress, draft, halted", "completed").option("--mapping-type <type>", "Deobfuscation file type: proguard or nativeCode", "proguard").option("--device-tier-config <id>", "Device tier config ID (or LATEST)").option("--changes-not-sent-for-review", "Commit changes without sending for review (required for rejected apps)").option("--error-if-in-review", "Fail if changes are already in review instead of cancelling them").action(async (file, options) => {
73
+ ).option(
74
+ "--status <status>",
75
+ "Release status: completed, inProgress, draft, halted",
76
+ "completed"
77
+ ).option("--mapping-type <type>", "Deobfuscation file type: proguard or nativeCode", "proguard").option("--device-tier-config <id>", "Device tier config ID (or LATEST)").option(
78
+ "--changes-not-sent-for-review",
79
+ "Commit changes without sending for review (required for rejected apps)"
80
+ ).option(
81
+ "--error-if-in-review",
82
+ "Fail if changes are already in review instead of cancelling them"
83
+ ).action(async (file, options) => {
74
84
  try {
75
85
  await stat(file);
76
86
  } catch {
@@ -269,7 +279,13 @@ function registerReleasesCommands(program) {
269
279
  await maybePaginate(formatOutput(sorted, format));
270
280
  }
271
281
  });
272
- releases.command("promote").description("Promote a release from one track to another").option("--from <track>", "Source track").option("--to <track>", "Target track").option("--rollout <percent>", "Staged rollout percentage").option("--notes <text>", "Release notes").option("--copy-notes-from <track>", "Copy release notes from another track").option("--status <status>", "Release status: completed, inProgress, draft, halted").option("--changes-not-sent-for-review", "Commit changes without sending for review (required for rejected apps)").option("--error-if-in-review", "Fail if changes are already in review instead of cancelling them").action(async (options) => {
282
+ releases.command("promote").description("Promote a release from one track to another").option("--from <track>", "Source track").option("--to <track>", "Target track").option("--rollout <percent>", "Staged rollout percentage").option("--notes <text>", "Release notes").option("--copy-notes-from <track>", "Copy release notes from another track").option("--status <status>", "Release status: completed, inProgress, draft, halted").option(
283
+ "--changes-not-sent-for-review",
284
+ "Commit changes without sending for review (required for rejected apps)"
285
+ ).option(
286
+ "--error-if-in-review",
287
+ "Fail if changes are already in review instead of cancelling them"
288
+ ).action(async (options) => {
273
289
  if (options.notes && options.copyNotesFrom) {
274
290
  throw new GpcError(
275
291
  "Cannot combine --notes and --copy-notes-from. Use only one.",
@@ -326,7 +342,10 @@ function registerReleasesCommands(program) {
326
342
  command: "releases promote",
327
343
  action: "promote",
328
344
  target: `${options.from} \u2192 ${options.to}`,
329
- details: { rollout: options.rollout, ...options.status && { status: options.status } }
345
+ details: {
346
+ rollout: options.rollout,
347
+ ...options.status && { status: options.status }
348
+ }
330
349
  },
331
350
  format,
332
351
  formatOutput
@@ -355,8 +374,14 @@ function registerReleasesCommands(program) {
355
374
  cmd.option("--to <percent>", "New rollout percentage");
356
375
  cmd.option("--vitals-gate", "Halt rollout if crash rate exceeds configured threshold");
357
376
  }
358
- cmd.option("--changes-not-sent-for-review", "Commit changes without sending for review (required for rejected apps)");
359
- cmd.option("--error-if-in-review", "Fail if changes are already in review instead of cancelling them");
377
+ cmd.option(
378
+ "--changes-not-sent-for-review",
379
+ "Commit changes without sending for review (required for rejected apps)"
380
+ );
381
+ cmd.option(
382
+ "--error-if-in-review",
383
+ "Fail if changes are already in review instead of cancelling them"
384
+ );
360
385
  cmd.action(async (options) => {
361
386
  const config = await loadConfig();
362
387
  const packageName = resolvePackageName(program.opts()["app"], config);
@@ -568,4 +593,4 @@ function registerReleasesCommands(program) {
568
593
  export {
569
594
  registerReleasesCommands
570
595
  };
571
- //# sourceMappingURL=releases-LUAHKIMY.js.map
596
+ //# sourceMappingURL=releases-XY57V22L.js.map