@gpc-cli/cli 0.9.40 → 0.9.42

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 (78) 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-AA577WVQ.js +23 -0
  5. package/dist/chunk-AA577WVQ.js.map +1 -0
  6. package/dist/{chunk-FIW4GLGP.js → chunk-MO6QT7L2.js} +27 -27
  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-EQOPWX3M.js → diff-V77SMKAQ.js} +4 -9
  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-2V6MLXZH.js → feedback-J7LXP3AW.js} +2 -2
  18. package/dist/{games-X57AGM3E.js → games-BT777WUO.js} +8 -13
  19. package/dist/games-BT777WUO.js.map +1 -0
  20. package/dist/{generated-apks-KB2PLWDI.js → generated-apks-RJWTIX7L.js} +5 -15
  21. package/dist/generated-apks-RJWTIX7L.js.map +1 -0
  22. package/dist/{iap-BNIAHBDN.js → iap-QTO5Q6SN.js} +5 -15
  23. package/dist/iap-QTO5Q6SN.js.map +1 -0
  24. package/dist/index.js +1 -1
  25. package/dist/{internal-sharing-M74VNIQ2.js → internal-sharing-DEOI3ENH.js} +5 -15
  26. package/dist/internal-sharing-DEOI3ENH.js.map +1 -0
  27. package/dist/{listings-JOFAZBKU.js → listings-NJWIKN4J.js} +5 -15
  28. package/dist/listings-NJWIKN4J.js.map +1 -0
  29. package/dist/{one-time-products-3WNXDKE3.js → one-time-products-6CQNNTBX.js} +5 -15
  30. package/dist/one-time-products-6CQNNTBX.js.map +1 -0
  31. package/dist/{pricing-OZO66DVL.js → pricing-XQSDTTK5.js} +5 -15
  32. package/dist/pricing-XQSDTTK5.js.map +1 -0
  33. package/dist/{publish-EPZXLGKZ.js → publish-OJJFPDRM.js} +4 -9
  34. package/dist/publish-OJJFPDRM.js.map +1 -0
  35. package/dist/{purchases-7ZPVCN6D.js → purchases-745IKIG5.js} +5 -15
  36. package/dist/purchases-745IKIG5.js.map +1 -0
  37. package/dist/{recovery-YGPOVUFD.js → recovery-BBF6NQ4C.js} +5 -15
  38. package/dist/recovery-BBF6NQ4C.js.map +1 -0
  39. package/dist/{releases-FXVNP6VK.js → releases-C2WC2K4E.js} +8 -9
  40. package/dist/{releases-FXVNP6VK.js.map → releases-C2WC2K4E.js.map} +1 -1
  41. package/dist/{reviews-MOVGATUI.js → reviews-2B5SKNXW.js} +5 -15
  42. package/dist/reviews-2B5SKNXW.js.map +1 -0
  43. package/dist/{subscriptions-LSOJID6H.js → subscriptions-4XKNTOOL.js} +12 -17
  44. package/dist/subscriptions-4XKNTOOL.js.map +1 -0
  45. package/dist/{testers-SDLVWQ2Z.js → testers-3Y753PFW.js} +5 -15
  46. package/dist/testers-3Y753PFW.js.map +1 -0
  47. package/dist/{train-PX5Z26PQ.js → train-YA4KGVGZ.js} +4 -9
  48. package/dist/train-YA4KGVGZ.js.map +1 -0
  49. package/dist/{update-EXLKAKOD.js → update-W6INIH3K.js} +2 -2
  50. package/dist/{version-3O6R66WZ.js → version-FXP5WOBO.js} +2 -2
  51. package/dist/{vitals-6EYDCGQK.js → vitals-A4CS4MSS.js} +4 -9
  52. package/dist/vitals-A4CS4MSS.js.map +1 -0
  53. package/package.json +4 -4
  54. package/dist/anomalies-KRRA75MJ.js.map +0 -1
  55. package/dist/config-36GIPNWX.js.map +0 -1
  56. package/dist/data-safety-JR6PZ2BD.js.map +0 -1
  57. package/dist/device-tiers-5SGJPSYG.js.map +0 -1
  58. package/dist/diff-EQOPWX3M.js.map +0 -1
  59. package/dist/external-transactions-O5P4QBIT.js.map +0 -1
  60. package/dist/games-X57AGM3E.js.map +0 -1
  61. package/dist/generated-apks-KB2PLWDI.js.map +0 -1
  62. package/dist/iap-BNIAHBDN.js.map +0 -1
  63. package/dist/internal-sharing-M74VNIQ2.js.map +0 -1
  64. package/dist/listings-JOFAZBKU.js.map +0 -1
  65. package/dist/one-time-products-3WNXDKE3.js.map +0 -1
  66. package/dist/pricing-OZO66DVL.js.map +0 -1
  67. package/dist/publish-EPZXLGKZ.js.map +0 -1
  68. package/dist/purchases-7ZPVCN6D.js.map +0 -1
  69. package/dist/recovery-YGPOVUFD.js.map +0 -1
  70. package/dist/reviews-MOVGATUI.js.map +0 -1
  71. package/dist/subscriptions-LSOJID6H.js.map +0 -1
  72. package/dist/testers-SDLVWQ2Z.js.map +0 -1
  73. package/dist/train-PX5Z26PQ.js.map +0 -1
  74. package/dist/vitals-6EYDCGQK.js.map +0 -1
  75. /package/dist/{chunk-FIW4GLGP.js.map → chunk-MO6QT7L2.js.map} +0 -0
  76. /package/dist/{feedback-2V6MLXZH.js.map → feedback-J7LXP3AW.js.map} +0 -0
  77. /package/dist/{update-EXLKAKOD.js.map → update-W6INIH3K.js.map} +0 -0
  78. /package/dist/{version-3O6R66WZ.js.map → version-FXP5WOBO.js.map} +0 -0
@@ -3,6 +3,10 @@ import {
3
3
  isDryRun,
4
4
  printDryRun
5
5
  } from "./chunk-Y3QZDAKS.js";
6
+ import {
7
+ getClient,
8
+ resolvePackageName
9
+ } from "./chunk-AA577WVQ.js";
6
10
  import {
7
11
  getOutputFormat
8
12
  } from "./chunk-ELXAK7GI.js";
@@ -12,8 +16,6 @@ import {
12
16
 
13
17
  // src/commands/recovery.ts
14
18
  import { loadConfig } from "@gpc-cli/config";
15
- import { resolveAuth } from "@gpc-cli/auth";
16
- import { createApiClient } from "@gpc-cli/api";
17
19
  import {
18
20
  listRecoveryActions,
19
21
  cancelRecoveryAction,
@@ -23,18 +25,6 @@ import {
23
25
  formatOutput
24
26
  } from "@gpc-cli/core";
25
27
  import { readFileSync } from "fs";
26
- function resolvePackageName(packageArg, config) {
27
- const name = packageArg || config.app;
28
- if (!name) {
29
- console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
30
- process.exit(2);
31
- }
32
- return name;
33
- }
34
- async function getClient(config) {
35
- const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
36
- return createApiClient({ auth });
37
- }
38
28
  function registerRecoveryCommands(program) {
39
29
  const recovery = program.command("recovery").description("Manage app recovery actions");
40
30
  recovery.command("list").description("List app recovery actions").option("--version-code <code>", "Filter by version code", parseInt).option("--limit <n>", "Maximum results to return").option("--next-page <token>", "Pagination token for next page").action(async (options) => {
@@ -193,4 +183,4 @@ function registerRecoveryCommands(program) {
193
183
  export {
194
184
  registerRecoveryCommands
195
185
  };
196
- //# sourceMappingURL=recovery-YGPOVUFD.js.map
186
+ //# sourceMappingURL=recovery-BBF6NQ4C.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\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":";;;;;;;;;;;;;;;;;AAEA,SAAS,kBAAkB;AAE3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,oBAAoB;AAItB,SAAS,yBAAyB,SAAwB;AAC/D,QAAM,WAAW,QAAQ,QAAQ,UAAU,EAAE,YAAY,6BAA6B;AAEtF,WACG,QAAQ,MAAM,EACd,YAAY,2BAA2B,EACvC,OAAO,yBAAyB,0BAA0B,QAAQ,EAClE,OAAO,eAAe,2BAA2B,EACjD,OAAO,uBAAuB,gCAAgC,EAC9D,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,UAAI,QAAQ,gBAAgB,QAAW;AACrC,gBAAQ;AAAA,UACN;AAAA,QACF;AACA,gBAAQ,MAAM,gDAAgD;AAC9D,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,YAAM,SAAS,MAAM,oBAAoB,QAAQ,aAAa,QAAQ,WAAW;AACjF,UAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;AAChD,YAAI,WAAW,QAAQ;AACrB,kBAAQ,IAAI,aAAa,CAAC,GAAG,MAAM,CAAC;AAAA,QACtC,OAAO;AACL,kBAAQ,IAAI,4BAA4B;AAAA,QAC1C;AACA;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;AAEH,WACG,QAAQ,aAAa,EACrB,YAAY,0BAA0B,EACtC,OAAO,OAAO,OAAe;AAC5B,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,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,eAAe,0BAA0B,EAAE,KAAK,OAAO;AAE7D,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,qBAAqB,QAAQ,aAAa,EAAE;AAClD,cAAQ;AAAA,QACN,aAAa,EAAE,SAAS,MAAM,eAAe,IAAI,QAAQ,YAAY,GAAG,MAAM;AAAA,MAChF;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,WACG,QAAQ,aAAa,EACrB,YAAY,0BAA0B,EACtC,OAAO,OAAO,OAAe;AAC5B,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,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,eAAe,0BAA0B,EAAE,KAAK,OAAO;AAE7D,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,qBAAqB,QAAQ,aAAa,EAAE;AAClD,cAAQ,IAAI,aAAa,EAAE,SAAS,MAAM,eAAe,IAAI,QAAQ,WAAW,GAAG,MAAM,CAAC;AAAA,IAC5F,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,WACG,QAAQ,QAAQ,EAChB,YAAY,kCAAkC,EAC9C,eAAe,iBAAiB,6CAA6C,EAC7E,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,MAAM,aAAa,QAAQ,MAAM,OAAO,CAAC;AAAA,IACvD,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN,mDAAmD,QAAQ,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACtH;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,SAAS,MAAM,qBAAqB,QAAQ,aAAa,IAAI;AACnE,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,WACG,QAAQ,2BAA2B,EACnC,YAAY,oDAAoD,EAChE,eAAe,iBAAiB,uCAAuC,EACvE,OAAO,OAAO,UAAkB,YAAY;AAC3C,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACJ,QAAI;AACF,kBAAY,KAAK,MAAM,aAAa,QAAQ,MAAM,OAAO,CAAC;AAAA,IAC5D,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN,6CAA6C,QAAQ,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAChH;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,SAAS,MAAM,qBAAqB,QAAQ,aAAa,UAAU,SAAS;AAClF,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;AACL;","names":[]}
@@ -444,16 +444,15 @@ function registerReleasesCommands(program) {
444
444
  const format = getOutputFormat(program, config);
445
445
  const track = options.track ?? "internal";
446
446
  const statuses = await getReleasesStatus(client, packageName, track);
447
- const notes = Array.isArray(statuses) ? statuses.flatMap((s) => s.releaseNotes ?? []) : statuses.releaseNotes ?? [];
447
+ let notes = Array.isArray(statuses) ? statuses.flatMap((s) => s.releaseNotes ?? []) : statuses.releaseNotes ?? [];
448
448
  if (notes.length === 0) {
449
- const hasCompleted = Array.isArray(statuses) && statuses.some((s) => s.status === "completed");
450
- if (hasCompleted) {
451
- console.log(
452
- `No release notes found on track "${track}". Notes may not be retained for completed releases. Try: gpc releases diff --from ${track} --to ${track}`
453
- );
454
- } else {
455
- console.log("No release notes found.");
449
+ try {
450
+ notes = await fetchReleaseNotes(client, packageName, track);
451
+ } catch {
456
452
  }
453
+ }
454
+ if (notes.length === 0) {
455
+ console.log(`No release notes found on track "${track}".`);
457
456
  return;
458
457
  }
459
458
  console.log(formatOutput(notes, format));
@@ -547,4 +546,4 @@ function registerReleasesCommands(program) {
547
546
  export {
548
547
  registerReleasesCommands
549
548
  };
550
- //# sourceMappingURL=releases-FXVNP6VK.js.map
549
+ //# sourceMappingURL=releases-C2WC2K4E.js.map
@@ -1 +1 @@
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 if (options.notes && options.copyNotesFrom) {\n console.error(\"Error: Cannot combine --notes and --copy-notes-from. Use only one.\");\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 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,QAAI,QAAQ,SAAS,QAAQ,eAAe;AAC1C,cAAQ,MAAM,oEAAoE;AAClF,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;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
+ {"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 if (options.notes && options.copyNotesFrom) {\n console.error(\"Error: Cannot combine --notes and --copy-notes-from. Use only one.\");\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 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\n // Try getReleasesStatus first (has all releases), then fallback to fetchReleaseNotes\n const statuses = await getReleasesStatus(client, packageName, track);\n let notes = Array.isArray(statuses)\n ? statuses.flatMap((s: any) => s.releaseNotes ?? [])\n : ((statuses as any).releaseNotes ?? []);\n\n // Fallback: fetchReleaseNotes reads the raw track data which may have notes\n // even when getReleasesStatus doesn't (e.g. completed releases)\n if (notes.length === 0) {\n try {\n notes = await fetchReleaseNotes(client, packageName, track);\n } catch {\n // No release found on track — fall through to empty message\n }\n }\n\n if (notes.length === 0) {\n console.log(`No release notes found on track \"${track}\".`);\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,QAAI,QAAQ,SAAS,QAAQ,eAAe;AAC1C,cAAQ,MAAM,oEAAoE;AAClF,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;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;AAG/B,YAAM,WAAW,MAAM,kBAAkB,QAAQ,aAAa,KAAK;AACnE,UAAI,QAAQ,MAAM,QAAQ,QAAQ,IAC9B,SAAS,QAAQ,CAAC,MAAW,EAAE,gBAAgB,CAAC,CAAC,IAC/C,SAAiB,gBAAgB,CAAC;AAIxC,UAAI,MAAM,WAAW,GAAG;AACtB,YAAI;AACF,kBAAQ,MAAM,kBAAkB,QAAQ,aAAa,KAAK;AAAA,QAC5D,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI,MAAM,WAAW,GAAG;AACtB,gBAAQ,IAAI,oCAAoC,KAAK,IAAI;AACzD;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"]}
@@ -3,6 +3,10 @@ import {
3
3
  isDryRun,
4
4
  printDryRun
5
5
  } from "./chunk-Y3QZDAKS.js";
6
+ import {
7
+ getClient,
8
+ resolvePackageName
9
+ } from "./chunk-AA577WVQ.js";
6
10
  import {
7
11
  getOutputFormat
8
12
  } from "./chunk-ELXAK7GI.js";
@@ -13,8 +17,6 @@ import {
13
17
 
14
18
  // src/commands/reviews.ts
15
19
  import { loadConfig } from "@gpc-cli/config";
16
- import { resolveAuth } from "@gpc-cli/auth";
17
- import { createApiClient } from "@gpc-cli/api";
18
20
  import {
19
21
  listReviews,
20
22
  getReview,
@@ -25,18 +27,6 @@ import {
25
27
  maybePaginate,
26
28
  sortResults
27
29
  } from "@gpc-cli/core";
28
- function resolvePackageName(packageArg, config) {
29
- const name = packageArg || config.app;
30
- if (!name) {
31
- console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
32
- process.exit(2);
33
- }
34
- return name;
35
- }
36
- async function getClient(config) {
37
- const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
38
- return createApiClient({ auth });
39
- }
40
30
  function registerReviewsCommands(program) {
41
31
  const reviews = program.command("reviews").description("Manage user reviews and ratings");
42
32
  reviews.command("list").description("List user reviews").option("--stars <n>", "Filter by star rating (1-5)", parseInt).option("--lang <code>", "Filter by reviewer language").option("--since <date>", "Filter reviews after date (ISO 8601)").option("--translate-to <lang>", "Translate reviews to language").option("--max <n>", "Maximum number of reviews per page", parseInt).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").option("--sort <field>", "Sort by field (prefix with - for descending)").action(async (options) => {
@@ -220,4 +210,4 @@ Rating distribution:`);
220
210
  export {
221
211
  registerReviewsCommands
222
212
  };
223
- //# sourceMappingURL=reviews-MOVGATUI.js.map
213
+ //# sourceMappingURL=reviews-2B5SKNXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/reviews.ts"],"sourcesContent":["import { resolvePackageName, getClient } from \"../resolve.js\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\n\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\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":[]}
@@ -6,6 +6,10 @@ import {
6
6
  isDryRun,
7
7
  printDryRun
8
8
  } from "./chunk-Y3QZDAKS.js";
9
+ import {
10
+ getClient,
11
+ resolvePackageName
12
+ } from "./chunk-AA577WVQ.js";
9
13
  import {
10
14
  getOutputFormat
11
15
  } from "./chunk-ELXAK7GI.js";
@@ -16,8 +20,6 @@ import {
16
20
  // src/commands/subscriptions.ts
17
21
  import { Option } from "commander";
18
22
  import { loadConfig } from "@gpc-cli/config";
19
- import { resolveAuth } from "@gpc-cli/auth";
20
- import { createApiClient } from "@gpc-cli/api";
21
23
  import {
22
24
  listSubscriptions,
23
25
  getSubscription,
@@ -40,18 +42,6 @@ import {
40
42
  formatOutput,
41
43
  sortResults
42
44
  } from "@gpc-cli/core";
43
- function resolvePackageName(packageArg, config) {
44
- const name = packageArg || config.app;
45
- if (!name) {
46
- console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
47
- process.exit(2);
48
- }
49
- return name;
50
- }
51
- async function getClient(config) {
52
- const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
53
- return createApiClient({ auth });
54
- }
55
45
  function registerSubscriptionsCommands(program) {
56
46
  const subs = program.command("subscriptions").description("Manage subscriptions and base plans");
57
47
  subs.command("list").description("List subscriptions").addOption(new Option("--page-size <n>", "Results per page").argParser(parseInt).hideHelp()).addOption(new Option("--page-token <token>", "Page token").hideHelp()).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").option("--sort <field>", "Sort by field (prefix with - for descending)").action(async (options) => {
@@ -69,8 +59,13 @@ function registerSubscriptionsCommands(program) {
69
59
  if (options.sort) {
70
60
  result.subscriptions = sortResults(result.subscriptions, options.sort);
71
61
  }
62
+ const subs2 = result.subscriptions || [];
72
63
  if (format !== "json") {
73
- const summary = (result.subscriptions || []).map((s) => ({
64
+ if (subs2.length === 0) {
65
+ console.log("No subscriptions found.");
66
+ return;
67
+ }
68
+ const summary = subs2.map((s) => ({
74
69
  productId: s.productId,
75
70
  basePlans: s.basePlans?.length || 0,
76
71
  listings: s.listings ? Object.keys(s.listings).length : 0,
@@ -78,7 +73,7 @@ function registerSubscriptionsCommands(program) {
78
73
  }));
79
74
  console.log(formatOutput(summary, format));
80
75
  } else {
81
- console.log(formatOutput(result, format));
76
+ console.log(formatOutput(subs2.length === 0 ? { subscriptions: [] } : result, format));
82
77
  }
83
78
  } catch (error) {
84
79
  console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
@@ -564,4 +559,4 @@ By product:`);
564
559
  export {
565
560
  registerSubscriptionsCommands
566
561
  };
567
- //# sourceMappingURL=subscriptions-LSOJID6H.js.map
562
+ //# sourceMappingURL=subscriptions-4XKNTOOL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/subscriptions.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 type { Subscription } from \"@gpc-cli/api\";\nimport {\n listSubscriptions,\n getSubscription,\n createSubscription,\n updateSubscription,\n deleteSubscription,\n activateBasePlan,\n deactivateBasePlan,\n deleteBasePlan,\n migratePrices,\n listOffers,\n getOffer,\n createOffer,\n updateOffer,\n deleteOffer,\n activateOffer,\n deactivateOffer,\n diffSubscription,\n getSubscriptionAnalytics,\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\n\n\nexport function registerSubscriptionsCommands(program: Command): void {\n const subs = program.command(\"subscriptions\").description(\"Manage subscriptions and base plans\");\n\n subs\n .command(\"list\")\n .description(\"List subscriptions\")\n .addOption(new Option(\"--page-size <n>\", \"Results per page\").argParser(parseInt).hideHelp())\n .addOption(new Option(\"--page-token <token>\", \"Page token\").hideHelp())\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 listSubscriptions(client, packageName, {\n pageSize: options.pageSize,\n pageToken: options.pageToken,\n limit: options.limit,\n nextPage: options.nextPage,\n });\n if (options.sort) {\n result.subscriptions = sortResults(result.subscriptions, options.sort);\n }\n const subs = result.subscriptions || [];\n if (format !== \"json\") {\n if (subs.length === 0) {\n console.log(\"No subscriptions found.\");\n return;\n }\n const summary = subs.map((s: Subscription) => ({\n productId: s.productId,\n basePlans: s.basePlans?.length || 0,\n listings: s.listings ? Object.keys(s.listings).length : 0,\n firstBasePlanState: s.basePlans?.[0]?.state || \"-\",\n }));\n console.log(formatOutput(summary, format));\n } else {\n console.log(formatOutput(subs.length === 0 ? { subscriptions: [] } : 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 subs\n .command(\"get <product-id>\")\n .description(\"Get a subscription\")\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 getSubscription(client, packageName, productId);\n if (format !== \"json\") {\n const s = result as unknown as Record<string, unknown>;\n const basePlans = s[\"basePlans\"] as Array<Record<string, unknown>> | undefined;\n const listings = s[\"listings\"] as\n | Record<string, unknown>\n | Array<Record<string, unknown>>\n | undefined;\n const listingLanguages = listings\n ? Array.isArray(listings)\n ? listings.map((l) => l[\"languageCode\"] || l[\"language\"] || \"?\").join(\", \")\n : Object.keys(listings).join(\", \")\n : \"-\";\n const listingCount = listings\n ? Array.isArray(listings)\n ? listings.length\n : Object.keys(listings).length\n : 0;\n const summary = {\n productId: s[\"productId\"],\n basePlans: basePlans?.length || 0,\n basePlanIds: basePlans?.map((bp) => bp[\"basePlanId\"]).join(\", \") || \"-\",\n listings: listingCount,\n listingLanguages,\n taxCategory: (s[\"taxAndComplianceSettings\"] as Record<string, unknown>)?.[\n \"taxRateInfoByRegionCode\"\n ]\n ? \"configured\"\n : \"-\",\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 subs\n .command(\"create\")\n .description(\"Create a subscription from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with subscription data\")\n .option(\"--activate\", \"Activate all base plans after creation\")\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: \"subscriptions create\",\n action: \"create\",\n target: `subscription from ${options.file}`,\n details: options.activate ? { activate: true } : undefined,\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 createSubscription(client, packageName, data as any);\n\n if (options.activate && result.basePlans) {\n for (const bp of result.basePlans) {\n if (bp.state === \"DRAFT\") {\n await activateBasePlan(client, packageName, result.productId, bp.basePlanId);\n console.error(`Activated base plan: ${bp.basePlanId}`);\n }\n }\n // Re-fetch to get updated state\n const updated = await getSubscription(client, packageName, result.productId);\n console.log(formatOutput(updated, 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 subs\n .command(\"update <product-id>\")\n .description(\"Update a subscription from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with subscription 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: \"subscriptions update\",\n action: \"update\",\n target: productId,\n details: { file: options.file, updateMask: options.updateMask },\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 updateSubscription(\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 subs\n .command(\"delete <product-id>\")\n .description(\"Delete a subscription\")\n .action(async (productId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Delete subscription \"${productId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"subscriptions 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 deleteSubscription(client, packageName, productId);\n console.log(`Subscription ${productId} deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Base Plans ---\n const basePlans = subs.command(\"base-plans\").description(\"Manage base plans\");\n\n basePlans\n .command(\"activate <product-id> <base-plan-id>\")\n .description(\"Activate a base plan\")\n .action(async (productId: string, basePlanId: 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: \"subscriptions base-plans activate\",\n action: \"activate\",\n target: `${productId}/${basePlanId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await activateBasePlan(client, packageName, productId, basePlanId);\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 basePlans\n .command(\"deactivate <product-id> <base-plan-id>\")\n .description(\"Deactivate a base plan\")\n .action(async (productId: string, basePlanId: 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: \"subscriptions base-plans deactivate\",\n action: \"deactivate\",\n target: `${productId}/${basePlanId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await deactivateBasePlan(client, packageName, productId, basePlanId);\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 basePlans\n .command(\"delete <product-id> <base-plan-id>\")\n .description(\"Delete a base plan\")\n .action(async (productId: string, basePlanId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(\n `Delete base plan \"${basePlanId}\" from subscription \"${productId}\"?`,\n program,\n );\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"subscriptions base-plans delete\",\n action: \"delete\",\n target: `${productId}/${basePlanId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await deleteBasePlan(client, packageName, productId, basePlanId);\n console.log(`Base plan ${basePlanId} deleted.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n basePlans\n .command(\"migrate-prices <product-id> <base-plan-id>\")\n .description(\"Migrate base plan prices\")\n .requiredOption(\"--file <path>\", \"JSON file with migration data\")\n .action(\n async (\n productId: string,\n basePlanId: 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: \"subscriptions base-plans migrate-prices\",\n action: \"migrate prices for\",\n target: `${productId}/${basePlanId}`,\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 migratePrices(\n client,\n packageName,\n productId,\n basePlanId,\n data as any,\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 const offers = subs.command(\"offers\").description(\"Manage subscription offers\");\n\n offers\n .command(\"list <product-id> <base-plan-id>\")\n .description(\"List offers for a base plan\")\n .action(async (productId: string, basePlanId: 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 listOffers(client, packageName, productId, basePlanId);\n const offers_list = (result as unknown as Record<string, unknown>)[\"subscriptionOffers\"] as\n | Array<Record<string, unknown>>\n | undefined;\n if (format !== \"json\") {\n if (!offers_list || offers_list.length === 0) {\n console.log(\"No offers found.\");\n return;\n }\n const summary = offers_list.map((o) => ({\n offerId: o[\"offerId\"],\n basePlanId: o[\"basePlanId\"],\n state: o[\"state\"] || \"-\",\n phases: (o[\"phases\"] as unknown[])?.length || 0,\n regionalConfigs: (o[\"regionalConfigs\"] as unknown[])?.length || 0,\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 offers\n .command(\"get <product-id> <base-plan-id> <offer-id>\")\n .description(\"Get an offer\")\n .action(async (productId: string, basePlanId: 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 getOffer(client, packageName, productId, basePlanId, 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> <base-plan-id>\")\n .description(\"Create an offer from JSON file\")\n .requiredOption(\"--file <path>\", \"JSON file with offer data\")\n .action(\n async (\n productId: string,\n basePlanId: 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: \"subscriptions offers create\",\n action: \"create offer for\",\n target: `${productId}/${basePlanId}`,\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 createOffer(client, packageName, productId, basePlanId, 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\n offers\n .command(\"update <product-id> <base-plan-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 basePlanId: 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: \"subscriptions offers update\",\n action: \"update offer\",\n target: `${productId}/${basePlanId}/${offerId}`,\n details: { file: options.file, updateMask: options.updateMask },\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 updateOffer(\n client,\n packageName,\n productId,\n basePlanId,\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> <base-plan-id> <offer-id>\")\n .description(\"Delete an offer\")\n .action(async (productId: string, basePlanId: string, offerId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n\n await requireConfirm(`Delete offer \"${offerId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = getOutputFormat(program, config);\n printDryRun(\n {\n command: \"subscriptions offers delete\",\n action: \"delete offer\",\n target: `${productId}/${basePlanId}/${offerId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await deleteOffer(client, packageName, productId, basePlanId, 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 offers\n .command(\"activate <product-id> <base-plan-id> <offer-id>\")\n .description(\"Activate an offer\")\n .action(async (productId: string, basePlanId: string, offerId: 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: \"subscriptions offers activate\",\n action: \"activate offer\",\n target: `${productId}/${basePlanId}/${offerId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await activateOffer(client, packageName, productId, basePlanId, 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(\"deactivate <product-id> <base-plan-id> <offer-id>\")\n .description(\"Deactivate an offer\")\n .action(async (productId: string, basePlanId: string, offerId: 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: \"subscriptions offers deactivate\",\n action: \"deactivate offer\",\n target: `${productId}/${basePlanId}/${offerId}`,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await deactivateOffer(client, packageName, productId, basePlanId, 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 // --- Diff ---\n subs\n .command(\"diff <product-id>\")\n .description(\"Compare local JSON file against remote subscription\")\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 Subscription;\n const diffs = await diffSubscription(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 subs\n .command(\"analytics\")\n .description(\"Subscription catalog analytics: active plans, offer counts, state breakdown\")\n .action(async () => {\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 getSubscriptionAnalytics(client, packageName);\n\n if (format === \"json\") {\n console.log(formatOutput(result, format));\n return;\n }\n\n console.log(`\\nSubscription Analytics — ${packageName}`);\n console.log(`${\"─\".repeat(50)}`);\n console.log(`Total subscriptions: ${result.totalSubscriptions}`);\n console.log(`Active subscriptions: ${result.activeCount}`);\n console.log(`Active base plans: ${result.activeBasePlans}`);\n console.log(`Draft base plans: ${result.trialBasePlans}`);\n console.log(`Inactive base plans: ${result.pausedBasePlans}`);\n console.log(`Total offers: ${result.offerCount}`);\n\n if (result.byProductId.length > 0) {\n console.log(`\\nBy product:`);\n console.log(formatOutput(result.byProductId, 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":["subs","basePlans"]}
@@ -3,6 +3,10 @@ import {
3
3
  isDryRun,
4
4
  printDryRun
5
5
  } from "./chunk-Y3QZDAKS.js";
6
+ import {
7
+ getClient,
8
+ resolvePackageName
9
+ } from "./chunk-AA577WVQ.js";
6
10
  import {
7
11
  getOutputFormat
8
12
  } from "./chunk-ELXAK7GI.js";
@@ -14,8 +18,6 @@ import {
14
18
 
15
19
  // src/commands/testers.ts
16
20
  import { loadConfig } from "@gpc-cli/config";
17
- import { resolveAuth } from "@gpc-cli/auth";
18
- import { createApiClient } from "@gpc-cli/api";
19
21
  import {
20
22
  listTesters,
21
23
  addTesters,
@@ -23,18 +25,6 @@ import {
23
25
  importTestersFromCsv,
24
26
  formatOutput
25
27
  } from "@gpc-cli/core";
26
- function resolvePackageName(packageArg, config) {
27
- const name = packageArg || config.app;
28
- if (!name) {
29
- console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
30
- process.exit(2);
31
- }
32
- return name;
33
- }
34
- async function getClient(config) {
35
- const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
36
- return createApiClient({ auth });
37
- }
38
28
  function registerTestersCommands(program) {
39
29
  const testers = program.command("testers").description("Manage testers and tester groups");
40
30
  testers.command("list").description("List testers for a track").option("--track <track>", "Track name (e.g., internal, alpha, beta)").option("--sort <field>", "Sort by field (prefix with - for descending, e.g., email or -email)").option("--limit <n>", "Maximum results to return").option("--next-page <token>", "Pagination token for next page").action(async (options) => {
@@ -197,4 +187,4 @@ function registerTestersCommands(program) {
197
187
  export {
198
188
  registerTestersCommands
199
189
  };
200
- //# sourceMappingURL=testers-SDLVWQ2Z.js.map
190
+ //# sourceMappingURL=testers-3Y753PFW.js.map