@gpc-cli/cli 0.9.34 → 0.9.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -15
- package/dist/{anomalies-QZJGQXTZ.js → anomalies-KRRA75MJ.js} +1 -1
- package/dist/anomalies-KRRA75MJ.js.map +1 -0
- package/dist/{apps-CVBURB5V.js → apps-J2446UDA.js} +2 -2
- package/dist/{apps-CVBURB5V.js.map → apps-J2446UDA.js.map} +1 -1
- package/dist/{audit-A4BP27DN.js → audit-N2CRHWUN.js} +21 -6
- package/dist/audit-N2CRHWUN.js.map +1 -0
- package/dist/{auth-QSDK7NUO.js → auth-XGSTT5G5.js} +107 -9
- package/dist/auth-XGSTT5G5.js.map +1 -0
- package/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/{bundle-7IF5FIB4.js → bundle-F43TD2BQ.js} +48 -14
- package/dist/bundle-F43TD2BQ.js.map +1 -0
- package/dist/{cache-SHWAVON6.js → cache-SLNFRTI2.js} +7 -3
- package/dist/cache-SLNFRTI2.js.map +1 -0
- package/dist/{chunk-7BXCQKJG.js → chunk-P5GF73XK.js} +1 -1
- package/dist/chunk-P5GF73XK.js.map +1 -0
- package/dist/{chunk-UH3TMIAL.js → chunk-U6ZTQ34I.js} +15 -17
- package/dist/chunk-U6ZTQ34I.js.map +1 -0
- package/dist/{chunk-5CSWPQO6.js → chunk-XHI7VKWZ.js} +113 -43
- package/dist/chunk-XHI7VKWZ.js.map +1 -0
- package/dist/{completion-C3PPWNS7.js → completion-BCHRJSAT.js} +23 -10
- package/dist/completion-BCHRJSAT.js.map +1 -0
- package/dist/{config-7EOY5HGL.js → config-F2U3KUHX.js} +4 -4
- package/dist/config-F2U3KUHX.js.map +1 -0
- package/dist/{data-safety-GDPKV5PN.js → data-safety-JR6PZ2BD.js} +11 -8
- package/dist/data-safety-JR6PZ2BD.js.map +1 -0
- package/dist/{device-tiers-GHIYJPMB.js → device-tiers-5SGJPSYG.js} +7 -8
- package/dist/device-tiers-5SGJPSYG.js.map +1 -0
- package/dist/{docs-HIGQU4UL.js → docs-7DUXIKA3.js} +3 -3
- package/dist/docs-7DUXIKA3.js.map +1 -0
- package/dist/{doctor-UKKOK55S.js → doctor-4BUPAVFT.js} +51 -7
- package/dist/doctor-4BUPAVFT.js.map +1 -0
- package/dist/enterprise-7THXNBTC.js +69 -0
- package/dist/enterprise-7THXNBTC.js.map +1 -0
- package/dist/{external-transactions-HCL7ROMN.js → external-transactions-O5P4QBIT.js} +5 -8
- package/dist/external-transactions-O5P4QBIT.js.map +1 -0
- package/dist/{feedback-W5MZMRF2.js → feedback-BZWHEADD.js} +3 -3
- package/dist/feedback-BZWHEADD.js.map +1 -0
- package/dist/games-X57AGM3E.js +80 -0
- package/dist/games-X57AGM3E.js.map +1 -0
- package/dist/{generated-apks-VX7HYZDU.js → generated-apks-KB2PLWDI.js} +2 -6
- package/dist/generated-apks-KB2PLWDI.js.map +1 -0
- package/dist/grants-TKQJ3IER.js +142 -0
- package/dist/grants-TKQJ3IER.js.map +1 -0
- package/dist/{iap-6XHJV5HX.js → iap-BNIAHBDN.js} +7 -3
- package/dist/iap-BNIAHBDN.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{internal-sharing-E7SJYDW3.js → internal-sharing-M74VNIQ2.js} +7 -7
- package/dist/internal-sharing-M74VNIQ2.js.map +1 -0
- package/dist/{listings-TOYS6XBU.js → listings-IVHZJNES.js} +91 -10
- package/dist/listings-IVHZJNES.js.map +1 -0
- package/dist/{migrate-OHN2FDY6.js → migrate-SQT6RD6T.js} +2 -4
- package/dist/migrate-SQT6RD6T.js.map +1 -0
- package/dist/{one-time-products-2PK4QKWE.js → one-time-products-3WNXDKE3.js} +43 -28
- package/dist/one-time-products-3WNXDKE3.js.map +1 -0
- package/dist/{pricing-BYZSLN74.js → pricing-HMHZD44S.js} +1 -1
- package/dist/pricing-HMHZD44S.js.map +1 -0
- package/dist/{publish-I6WJGR4S.js → publish-EPZXLGKZ.js} +4 -9
- package/dist/publish-EPZXLGKZ.js.map +1 -0
- package/dist/{purchases-YRO6B7M6.js → purchases-7ZPVCN6D.js} +31 -2
- package/dist/purchases-7ZPVCN6D.js.map +1 -0
- package/dist/quickstart-EYNNOTVD.js +87 -0
- package/dist/quickstart-EYNNOTVD.js.map +1 -0
- package/dist/quota-UHIQQYOY.js +58 -0
- package/dist/quota-UHIQQYOY.js.map +1 -0
- package/dist/{recovery-S5UNJDBO.js → recovery-YGPOVUFD.js} +7 -3
- package/dist/recovery-YGPOVUFD.js.map +1 -0
- package/dist/{releases-ANC54YWF.js → releases-I7QQVKPJ.js} +62 -22
- package/dist/releases-I7QQVKPJ.js.map +1 -0
- package/dist/{reports-N5X66IUN.js → reports-2YX3RDOS.js} +1 -1
- package/dist/reports-2YX3RDOS.js.map +1 -0
- package/dist/{reviews-UHK4FGK6.js → reviews-MOVGATUI.js} +68 -7
- package/dist/reviews-MOVGATUI.js.map +1 -0
- package/dist/{status-DQYZ7A6Y.js → status-G3AMJ34G.js} +17 -5
- package/dist/status-G3AMJ34G.js.map +1 -0
- package/dist/{subscriptions-Z5ZPVUFM.js → subscriptions-LSOJID6H.js} +39 -2
- package/dist/subscriptions-LSOJID6H.js.map +1 -0
- package/dist/{testers-UWSUGGVT.js → testers-SDLVWQ2Z.js} +1 -1
- package/dist/testers-SDLVWQ2Z.js.map +1 -0
- package/dist/{tracks-XFUN7JJX.js → tracks-NERFFEDT.js} +2 -2
- package/dist/tracks-NERFFEDT.js.map +1 -0
- package/dist/train-PX5Z26PQ.js +150 -0
- package/dist/train-PX5Z26PQ.js.map +1 -0
- package/dist/{update-NIVJLUNH.js → update-ARIQO53C.js} +6 -12
- package/dist/update-ARIQO53C.js.map +1 -0
- package/dist/{users-JASXONRY.js → users-2YTC4Q36.js} +1 -1
- package/dist/users-2YTC4Q36.js.map +1 -0
- package/dist/{validate-MHLPENCM.js → validate-VNIS6OEB.js} +9 -4
- package/dist/validate-VNIS6OEB.js.map +1 -0
- package/dist/{version-7AI5IHVK.js → version-MXIJ4BUH.js} +11 -9
- package/dist/version-MXIJ4BUH.js.map +1 -0
- package/dist/{vitals-QGWOFH7E.js → vitals-H7DCI6JJ.js} +125 -7
- package/dist/vitals-H7DCI6JJ.js.map +1 -0
- package/package.json +7 -3
- package/dist/anomalies-QZJGQXTZ.js.map +0 -1
- package/dist/audit-A4BP27DN.js.map +0 -1
- package/dist/auth-QSDK7NUO.js.map +0 -1
- package/dist/bundle-7IF5FIB4.js.map +0 -1
- package/dist/cache-SHWAVON6.js.map +0 -1
- package/dist/chunk-5CSWPQO6.js.map +0 -1
- package/dist/chunk-7BXCQKJG.js.map +0 -1
- package/dist/chunk-UH3TMIAL.js.map +0 -1
- package/dist/completion-C3PPWNS7.js.map +0 -1
- package/dist/config-7EOY5HGL.js.map +0 -1
- package/dist/data-safety-GDPKV5PN.js.map +0 -1
- package/dist/device-tiers-GHIYJPMB.js.map +0 -1
- package/dist/docs-HIGQU4UL.js.map +0 -1
- package/dist/doctor-UKKOK55S.js.map +0 -1
- package/dist/external-transactions-HCL7ROMN.js.map +0 -1
- package/dist/feedback-W5MZMRF2.js.map +0 -1
- package/dist/generated-apks-VX7HYZDU.js.map +0 -1
- package/dist/iap-6XHJV5HX.js.map +0 -1
- package/dist/internal-sharing-E7SJYDW3.js.map +0 -1
- package/dist/listings-TOYS6XBU.js.map +0 -1
- package/dist/migrate-OHN2FDY6.js.map +0 -1
- package/dist/one-time-products-2PK4QKWE.js.map +0 -1
- package/dist/pricing-BYZSLN74.js.map +0 -1
- package/dist/publish-I6WJGR4S.js.map +0 -1
- package/dist/purchases-YRO6B7M6.js.map +0 -1
- package/dist/recovery-S5UNJDBO.js.map +0 -1
- package/dist/releases-ANC54YWF.js.map +0 -1
- package/dist/reports-N5X66IUN.js.map +0 -1
- package/dist/reviews-UHK4FGK6.js.map +0 -1
- package/dist/status-DQYZ7A6Y.js.map +0 -1
- package/dist/subscriptions-Z5ZPVUFM.js.map +0 -1
- package/dist/testers-UWSUGGVT.js.map +0 -1
- package/dist/tracks-XFUN7JJX.js.map +0 -1
- package/dist/update-NIVJLUNH.js.map +0 -1
- package/dist/users-JASXONRY.js.map +0 -1
- package/dist/validate-MHLPENCM.js.map +0 -1
- package/dist/version-7AI5IHVK.js.map +0 -1
- package/dist/vitals-QGWOFH7E.js.map +0 -1
package/README.md
CHANGED
|
@@ -27,12 +27,17 @@ brew install yasserstudio/tap/gpc
|
|
|
27
27
|
curl -fsSL https://raw.githubusercontent.com/yasserstudio/gpc/main/scripts/install.sh | sh
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Free. Open-source. No account required beyond your existing Google Play service account.
|
|
31
|
+
|
|
30
32
|
## Quick Start
|
|
31
33
|
|
|
32
34
|
```bash
|
|
33
35
|
# Authenticate
|
|
34
36
|
gpc auth login --service-account path/to/key.json
|
|
35
37
|
|
|
38
|
+
# Verify your setup
|
|
39
|
+
gpc doctor
|
|
40
|
+
|
|
36
41
|
# App health at a glance — releases, vitals, and reviews in one command
|
|
37
42
|
gpc status
|
|
38
43
|
|
|
@@ -72,23 +77,23 @@ REVIEWS (last 30 days)
|
|
|
72
77
|
|
|
73
78
|
187 API endpoints across these command groups:
|
|
74
79
|
|
|
75
|
-
| Group |
|
|
76
|
-
| ----------------- |
|
|
77
|
-
| **Releases** |
|
|
78
|
-
| **Listings** |
|
|
79
|
-
| **Reviews** |
|
|
80
|
-
| **Vitals** |
|
|
81
|
-
| **Bundle** |
|
|
82
|
-
| **Subscriptions** |
|
|
83
|
-
| **IAP** |
|
|
84
|
-
| **Purchases** |
|
|
85
|
-
| **Reports** |
|
|
86
|
-
| **Testers** |
|
|
87
|
-
| **Users** |
|
|
80
|
+
| Group | What you can do |
|
|
81
|
+
| ----------------- | -------------------------------------------------------------------------------------- |
|
|
82
|
+
| **Releases** | Upload, promote, rollout increase/halt/resume, end-to-end `publish` |
|
|
83
|
+
| **Listings** | Pull and push store listings, upload screenshots — works with Fastlane metadata format |
|
|
84
|
+
| **Reviews** | Filter by stars, reply to users, export to CSV |
|
|
85
|
+
| **Vitals** | Crash rates, ANR, startup, rendering — with CI threshold gates |
|
|
86
|
+
| **Bundle** | Per-module size breakdown, build-to-build diff, size CI gates |
|
|
87
|
+
| **Subscriptions** | Create and manage base plans, offers, and pricing |
|
|
88
|
+
| **IAP** | Sync products from local files, batch get and update |
|
|
89
|
+
| **Purchases** | Verify, acknowledge, cancel, refund, list voided |
|
|
90
|
+
| **Reports** | Download financial and stats reports |
|
|
91
|
+
| **Testers** | Add, remove, import from CSV |
|
|
92
|
+
| **Users** | Invite, update, remove, manage per-app grants |
|
|
88
93
|
|
|
89
94
|
## CI/CD Ready
|
|
90
95
|
|
|
91
|
-
JSON output when piped. Formatted tables in your terminal. Semantic exit codes (0
|
|
96
|
+
JSON output when piped. Formatted tables in your terminal. Semantic exit codes (0–6) your CI can react to. Every write operation supports `--dry-run`.
|
|
92
97
|
|
|
93
98
|
```yaml
|
|
94
99
|
- name: Upload
|
|
@@ -100,7 +105,7 @@ JSON output when piped. Formatted tables in your terminal. Semantic exit codes (
|
|
|
100
105
|
gpc releases upload app.aab --track internal
|
|
101
106
|
```
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
Already on Fastlane? See the [migration guide](https://yasserstudio.github.io/gpc/migration/from-fastlane) — most commands map one-to-one.
|
|
104
109
|
|
|
105
110
|
## Part of the GPC Monorepo
|
|
106
111
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/anomalies.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createReportingClient } from \"@gpc-cli/api\";\nimport { getVitalsAnomalies, formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: GpcConfig): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getReportingClient(config: GpcConfig) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createReportingClient({ auth });\n}\n\nexport function registerAnomaliesCommands(program: Command): void {\n const anomalies = program.command(\"anomalies\").description(\"Detect and list vitals anomalies\");\n\n anomalies\n .command(\"list\")\n .description(\"List detected vitals anomalies\")\n .action(async () => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const reporting = await getReportingClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getVitalsAnomalies(reporting, packageName);\n const items = (result as unknown as Record<string, unknown>)[\"anomalies\"] as\n | unknown[]\n | undefined;\n\n if (format !== \"json\") {\n if (!items || items.length === 0) {\n console.log(\"No anomalies detected.\");\n return;\n }\n const rows = items.map((item) => {\n const a = item as Record<string, unknown>;\n return {\n name: String(a[\"name\"] ?? \"-\"),\n metricSet: String(a[\"metricSet\"] ?? \"-\"),\n aggregationPeriod: String(a[\"aggregationPeriod\"] ?? \"-\"),\n };\n });\n console.log(formatOutput(rows, 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"],"mappings":";;;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB,oBAAoB;AAGjD,SAAS,mBAAmB,YAAgC,QAA2B;AACrF,QAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,6EAA6E;AAC3F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,mBAAmB,QAAmB;AACnD,QAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,SAAO,sBAAsB,EAAE,KAAK,CAAC;AACvC;AAEO,SAAS,0BAA0B,SAAwB;AAChE,QAAM,YAAY,QAAQ,QAAQ,WAAW,EAAE,YAAY,kCAAkC;AAE7F,YACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,YAAY,MAAM,mBAAmB,MAAM;AACjD,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,WAAW,WAAW;AAC9D,YAAM,QAAS,OAA8C,WAAW;AAIxE,UAAI,WAAW,QAAQ;AACrB,YAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,kBAAQ,IAAI,wBAAwB;AACpC;AAAA,QACF;AACA,cAAM,OAAO,MAAM,IAAI,CAAC,SAAS;AAC/B,gBAAM,IAAI;AACV,iBAAO;AAAA,YACL,MAAM,OAAO,EAAE,MAAM,KAAK,GAAG;AAAA,YAC7B,WAAW,OAAO,EAAE,WAAW,KAAK,GAAG;AAAA,YACvC,mBAAmB,OAAO,EAAE,mBAAmB,KAAK,GAAG;AAAA,UACzD;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;AACL;","names":[]}
|
|
@@ -82,7 +82,7 @@ function registerAppsCommands(program) {
|
|
|
82
82
|
process.exit(4);
|
|
83
83
|
}
|
|
84
84
|
});
|
|
85
|
-
apps.command("list").description("List configured applications").option("--limit <n>", "Maximum results to return").option("--next-page <token>", "Pagination token for next page").action(async (
|
|
85
|
+
apps.command("list").description("List configured applications").option("--limit <n>", "Maximum results to return").option("--next-page <token>", "Pagination token for next page").action(async (_options) => {
|
|
86
86
|
const config = await loadConfig();
|
|
87
87
|
const format = getOutputFormat(program, config);
|
|
88
88
|
if (config.app) {
|
|
@@ -102,4 +102,4 @@ function registerAppsCommands(program) {
|
|
|
102
102
|
export {
|
|
103
103
|
registerAppsCommands
|
|
104
104
|
};
|
|
105
|
-
//# sourceMappingURL=apps-
|
|
105
|
+
//# sourceMappingURL=apps-J2446UDA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/apps.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport { getAppInfo, updateAppDetails } from \"@gpc-cli/core\";\nimport { formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\n\nexport function registerAppsCommands(program: Command): void {\n const apps = program.command(\"apps\").description(\"Manage applications\");\n\n apps\n .command(\"info [package]\")\n .description(\"Show app details\")\n .action(async (packageArg?: string) => {\n const config = await loadConfig();\n const packageName = packageArg || config.app;\n\n if (!packageName) {\n console.error(\"Error: No package name provided.\");\n console.error(\"Usage: gpc apps info <package>\");\n console.error(\"Or set a default: gpc config set app com.example.app\");\n process.exit(2);\n }\n\n try {\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n });\n const client = createApiClient({ auth });\n const info = await getAppInfo(client, packageName);\n const format = getOutputFormat(program, config);\n console.log(formatOutput(info, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n apps\n .command(\"update\")\n .description(\"Update app details\")\n .option(\"--email <email>\", \"Contact email\")\n .option(\"--phone <phone>\", \"Contact phone\")\n .option(\"--website <url>\", \"Contact website\")\n .option(\"--default-lang <lang>\", \"Default language\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = options[\"app\"] || program.opts()[\"app\"] || config.app;\n\n if (!packageName) {\n console.error(\"Error: No package name provided.\");\n console.error(\"Usage: gpc apps update --email user@example.com\");\n process.exit(2);\n }\n\n const data: Record<string, string> = {};\n if (options[\"email\"]) data[\"contactEmail\"] = options[\"email\"];\n if (options[\"phone\"]) data[\"contactPhone\"] = options[\"phone\"];\n if (options[\"website\"]) data[\"contactWebsite\"] = options[\"website\"];\n if (options[\"defaultLang\"]) data[\"defaultLanguage\"] = options[\"defaultLang\"];\n\n if (Object.keys(data).length === 0) {\n console.error(\n \"Error: Provide at least one field to update (--email, --phone, --website, --default-lang).\",\n );\n process.exit(2);\n }\n\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"apps update\",\n action: \"update app details for\",\n target: packageName,\n details: data,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n try {\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n });\n const client = createApiClient({ auth });\n const result = await updateAppDetails(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 apps\n .command(\"list\")\n .description(\"List configured applications\")\n .option(\"--limit <n>\", \"Maximum results to return\")\n .option(\"--next-page <token>\", \"Pagination token for next page\")\n .action(async (
|
|
1
|
+
{"version":3,"sources":["../src/commands/apps.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport { getAppInfo, updateAppDetails } from \"@gpc-cli/core\";\nimport { formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\n\nexport function registerAppsCommands(program: Command): void {\n const apps = program.command(\"apps\").description(\"Manage applications\");\n\n apps\n .command(\"info [package]\")\n .description(\"Show app details\")\n .action(async (packageArg?: string) => {\n const config = await loadConfig();\n const packageName = packageArg || config.app;\n\n if (!packageName) {\n console.error(\"Error: No package name provided.\");\n console.error(\"Usage: gpc apps info <package>\");\n console.error(\"Or set a default: gpc config set app com.example.app\");\n process.exit(2);\n }\n\n try {\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n });\n const client = createApiClient({ auth });\n const info = await getAppInfo(client, packageName);\n const format = getOutputFormat(program, config);\n console.log(formatOutput(info, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n apps\n .command(\"update\")\n .description(\"Update app details\")\n .option(\"--email <email>\", \"Contact email\")\n .option(\"--phone <phone>\", \"Contact phone\")\n .option(\"--website <url>\", \"Contact website\")\n .option(\"--default-lang <lang>\", \"Default language\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = options[\"app\"] || program.opts()[\"app\"] || config.app;\n\n if (!packageName) {\n console.error(\"Error: No package name provided.\");\n console.error(\"Usage: gpc apps update --email user@example.com\");\n process.exit(2);\n }\n\n const data: Record<string, string> = {};\n if (options[\"email\"]) data[\"contactEmail\"] = options[\"email\"];\n if (options[\"phone\"]) data[\"contactPhone\"] = options[\"phone\"];\n if (options[\"website\"]) data[\"contactWebsite\"] = options[\"website\"];\n if (options[\"defaultLang\"]) data[\"defaultLanguage\"] = options[\"defaultLang\"];\n\n if (Object.keys(data).length === 0) {\n console.error(\n \"Error: Provide at least one field to update (--email, --phone, --website, --default-lang).\",\n );\n process.exit(2);\n }\n\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"apps update\",\n action: \"update app details for\",\n target: packageName,\n details: data,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n try {\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n });\n const client = createApiClient({ auth });\n const result = await updateAppDetails(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 apps\n .command(\"list\")\n .description(\"List configured applications\")\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 format = getOutputFormat(program, config);\n\n if (config.app) {\n const apps = [{ packageName: config.app, source: \"config\" }];\n console.log(formatOutput(apps, format));\n } else {\n console.log(\"No apps configured.\");\n console.log(\"\");\n console.log(\"Set a default app:\");\n console.log(\" gpc config set app com.example.myapp\");\n console.log(\"\");\n console.log(\"Or use the --app flag:\");\n console.log(\" gpc apps info --app com.example.myapp\");\n }\n });\n}\n"],"mappings":"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","names":["apps"]}
|
|
@@ -26,13 +26,27 @@ function formatAuditTimestamp(iso) {
|
|
|
26
26
|
const diffMin = Math.floor(diffMs / 6e4);
|
|
27
27
|
if (diffMin < 60) return diffMin < 1 ? "just now" : `${diffMin} min ago`;
|
|
28
28
|
const diffHr = Math.floor(diffMin / 60);
|
|
29
|
-
if (diffHr < 24)
|
|
29
|
+
if (diffHr < 24)
|
|
30
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
|
|
30
31
|
const diffDays = Math.floor(diffHr / 24);
|
|
31
32
|
if (diffDays < 7) {
|
|
32
33
|
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
33
34
|
return `${days[date.getDay()]} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
34
35
|
}
|
|
35
|
-
const months = [
|
|
36
|
+
const months = [
|
|
37
|
+
"Jan",
|
|
38
|
+
"Feb",
|
|
39
|
+
"Mar",
|
|
40
|
+
"Apr",
|
|
41
|
+
"May",
|
|
42
|
+
"Jun",
|
|
43
|
+
"Jul",
|
|
44
|
+
"Aug",
|
|
45
|
+
"Sep",
|
|
46
|
+
"Oct",
|
|
47
|
+
"Nov",
|
|
48
|
+
"Dec"
|
|
49
|
+
];
|
|
36
50
|
return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
|
|
37
51
|
}
|
|
38
52
|
function registerAuditCommands(program) {
|
|
@@ -96,8 +110,7 @@ function registerAuditCommands(program) {
|
|
|
96
110
|
});
|
|
97
111
|
audit.command("clear").description("Clear audit log entries").option("--before <date>", "Clear entries before date (ISO 8601)").option("--dry-run", "Preview what would be cleared").action(async (options, cmd) => {
|
|
98
112
|
const dryRun = options.dryRun || isDryRun(cmd);
|
|
99
|
-
|
|
100
|
-
const format = getOutputFormat(program, config);
|
|
113
|
+
await loadConfig();
|
|
101
114
|
initAudit(getConfigDir());
|
|
102
115
|
if (!dryRun && !options.before) {
|
|
103
116
|
await requireConfirm("Clear all audit log entries?", program);
|
|
@@ -108,7 +121,9 @@ function registerAuditCommands(program) {
|
|
|
108
121
|
dryRun
|
|
109
122
|
});
|
|
110
123
|
if (dryRun) {
|
|
111
|
-
console.log(
|
|
124
|
+
console.log(
|
|
125
|
+
`[dry-run] Would delete ${result.deleted} entries, ${result.remaining} would remain.`
|
|
126
|
+
);
|
|
112
127
|
} else {
|
|
113
128
|
console.log(`Deleted ${result.deleted} entries. ${result.remaining} remaining.`);
|
|
114
129
|
}
|
|
@@ -121,4 +136,4 @@ function registerAuditCommands(program) {
|
|
|
121
136
|
export {
|
|
122
137
|
registerAuditCommands
|
|
123
138
|
};
|
|
124
|
-
//# sourceMappingURL=audit-
|
|
139
|
+
//# sourceMappingURL=audit-N2CRHWUN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/audit.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { getConfigDir } from \"@gpc-cli/config\";\nimport {\n initAudit,\n listAuditEvents,\n searchAuditEvents,\n clearAuditLog,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun } from \"../dry-run.js\";\nimport { requireConfirm } from \"../prompt.js\";\n\nfunction formatAuditTimestamp(iso: string): string {\n const date = new Date(iso);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 60) return diffMin < 1 ? \"just now\" : `${diffMin} min ago`;\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return `${String(date.getHours()).padStart(2, \"0\")}:${String(date.getMinutes()).padStart(2, \"0\")}:${String(date.getSeconds()).padStart(2, \"0\")}`;\n const diffDays = Math.floor(diffHr / 24);\n if (diffDays < 7) {\n const days = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n return `${days[date.getDay()]} ${String(date.getHours()).padStart(2, \"0\")}:${String(date.getMinutes()).padStart(2, \"0\")}`;\n }\n const months = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n ];\n return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;\n}\n\nexport function registerAuditCommands(program: Command): void {\n const audit = program.command(\"audit\").description(\"Query and manage audit logs\");\n\n audit\n .command(\"list\")\n .description(\"List recent audit events\")\n .option(\"--limit <n>\", \"Maximum events to show\", parseInt, 50)\n .option(\"--since <date>\", \"Show events since date (ISO 8601)\")\n .option(\"--command <name>\", \"Filter by command name\")\n .action(async (options) => {\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n initAudit(getConfigDir());\n\n try {\n const events = await listAuditEvents({\n limit: options.limit,\n since: options.since,\n command: options.command,\n });\n if (events.length === 0 && format !== \"json\") {\n console.log(\"No audit events found.\");\n return;\n }\n if (format !== \"json\") {\n const rows = events.map((e) => ({\n timestamp: formatAuditTimestamp(e.timestamp),\n command: e.command,\n app: e.app || \"-\",\n success: e.success !== undefined ? String(e.success) : \"-\",\n durationMs: e.durationMs ?? \"-\",\n }));\n console.log(formatOutput(rows, format));\n } else {\n console.log(formatOutput(events, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n audit\n .command(\"search <query>\")\n .description(\"Search audit events by keyword\")\n .action(async (query: string) => {\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n initAudit(getConfigDir());\n\n try {\n const events = await searchAuditEvents(query);\n if (events.length === 0 && format !== \"json\") {\n console.log(`No audit events matching \"${query}\".`);\n return;\n }\n if (format !== \"json\") {\n const rows = events.map((e) => ({\n timestamp: formatAuditTimestamp(e.timestamp),\n command: e.command,\n app: e.app || \"-\",\n success: e.success !== undefined ? String(e.success) : \"-\",\n }));\n console.log(formatOutput(rows, format));\n } else {\n console.log(formatOutput(events, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n audit\n .command(\"clear\")\n .description(\"Clear audit log entries\")\n .option(\"--before <date>\", \"Clear entries before date (ISO 8601)\")\n .option(\"--dry-run\", \"Preview what would be cleared\")\n .action(async (options, cmd: Command) => {\n const dryRun = options.dryRun || isDryRun(cmd);\n await loadConfig();\n initAudit(getConfigDir());\n\n if (!dryRun && !options.before) {\n await requireConfirm(\"Clear all audit log entries?\", program);\n }\n\n try {\n const result = await clearAuditLog({\n before: options.before,\n dryRun,\n });\n if (dryRun) {\n console.log(\n `[dry-run] Would delete ${result.deleted} entries, ${result.remaining} would remain.`,\n );\n } else {\n console.log(`Deleted ${result.deleted} entries. ${result.remaining} remaining.`);\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,9 +4,76 @@ import {
|
|
|
4
4
|
} from "./chunk-ELXAK7GI.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/auth.ts
|
|
7
|
+
import { createInterface } from "readline";
|
|
8
|
+
import { access } from "fs/promises";
|
|
7
9
|
import { resolveAuth, loadServiceAccountKey, clearTokenCache, AuthError } from "@gpc-cli/auth";
|
|
8
10
|
import { loadConfig, getCacheDir } from "@gpc-cli/config";
|
|
9
11
|
import { formatOutput } from "@gpc-cli/core";
|
|
12
|
+
async function askQuestion(rl, question) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function runLoginWizard() {
|
|
18
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
19
|
+
try {
|
|
20
|
+
console.log("\nGPC Authentication Setup");
|
|
21
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
22
|
+
console.log("\nAuthentication methods:");
|
|
23
|
+
console.log(" 1. Service account key file (recommended)");
|
|
24
|
+
console.log(" 2. Application Default Credentials (ADC)");
|
|
25
|
+
const method = await askQuestion(rl, "\nSelect method [1]: ");
|
|
26
|
+
const useAdc = method === "2";
|
|
27
|
+
if (useAdc) {
|
|
28
|
+
const { resolveAuth: ra } = await import("@gpc-cli/auth");
|
|
29
|
+
const client = await ra();
|
|
30
|
+
console.log(`
|
|
31
|
+
Authenticated via Application Default Credentials`);
|
|
32
|
+
console.log(`Account: ${client.getClientEmail()}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let saPath = "";
|
|
36
|
+
while (!saPath) {
|
|
37
|
+
const input = await askQuestion(rl, "\nPath to service account JSON key: ");
|
|
38
|
+
if (!input) {
|
|
39
|
+
console.log(" Path is required.");
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
await access(input);
|
|
44
|
+
saPath = input;
|
|
45
|
+
} catch {
|
|
46
|
+
console.log(` File not found: ${input}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const profileName = await askQuestion(rl, "\nProfile name (optional, press Enter to skip): ");
|
|
50
|
+
const packageName = await askQuestion(
|
|
51
|
+
rl,
|
|
52
|
+
"Default package name (optional, e.g. com.example.app): "
|
|
53
|
+
);
|
|
54
|
+
const { loadServiceAccountKey: loadKey } = await import("@gpc-cli/auth");
|
|
55
|
+
const key = await loadKey(saPath);
|
|
56
|
+
if (profileName) {
|
|
57
|
+
const { setProfileConfig } = await import("@gpc-cli/config");
|
|
58
|
+
await setProfileConfig(profileName, {
|
|
59
|
+
auth: { serviceAccount: saPath },
|
|
60
|
+
...packageName && { app: packageName }
|
|
61
|
+
});
|
|
62
|
+
console.log(`
|
|
63
|
+
Profile "${profileName}" configured with ${key.client_email}`);
|
|
64
|
+
} else {
|
|
65
|
+
const { setConfigValue } = await import("@gpc-cli/config");
|
|
66
|
+
await setConfigValue("auth.serviceAccount", saPath);
|
|
67
|
+
if (packageName) await setConfigValue("app", packageName);
|
|
68
|
+
console.log(`
|
|
69
|
+
Authenticated as ${key.client_email}`);
|
|
70
|
+
}
|
|
71
|
+
console.log(`Project: ${key.project_id}`);
|
|
72
|
+
console.log("\nRun 'gpc doctor' to verify your setup.");
|
|
73
|
+
} finally {
|
|
74
|
+
rl.close();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
10
77
|
function registerAuthCommands(program) {
|
|
11
78
|
const auth = program.command("auth").description("Manage authentication");
|
|
12
79
|
auth.command("login").description("Authenticate with Google Play Developer API").option("--service-account <path>", "Path to service account JSON key file").option("--adc", "Use Application Default Credentials").option("--profile <name>", "Store credentials under a named profile").action(async (options) => {
|
|
@@ -30,14 +97,20 @@ function registerAuthCommands(program) {
|
|
|
30
97
|
console.log(`Authenticated via Application Default Credentials`);
|
|
31
98
|
console.log(`Account: ${client.getClientEmail()}`);
|
|
32
99
|
} else {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
100
|
+
const opts = program.opts();
|
|
101
|
+
const interactive = opts["interactive"] !== false && opts["ci"] !== true && process.stdin.isTTY;
|
|
102
|
+
if (interactive) {
|
|
103
|
+
await runLoginWizard();
|
|
104
|
+
} else {
|
|
105
|
+
console.log("Usage: gpc auth login --service-account <path>");
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log("Authentication methods:");
|
|
108
|
+
console.log(" --service-account <path> Service account JSON key file");
|
|
109
|
+
console.log(" --adc Application Default Credentials");
|
|
110
|
+
console.log("");
|
|
111
|
+
console.log("Options:");
|
|
112
|
+
console.log(" --profile <name> Store under a named profile");
|
|
113
|
+
}
|
|
41
114
|
}
|
|
42
115
|
} catch (error) {
|
|
43
116
|
if (error instanceof AuthError) {
|
|
@@ -127,6 +200,31 @@ function registerAuthCommands(program) {
|
|
|
127
200
|
throw error;
|
|
128
201
|
}
|
|
129
202
|
});
|
|
203
|
+
auth.command("setup-gcp").description("Step-by-step guide to create a Google Cloud service account").action(() => {
|
|
204
|
+
console.log("\nGPC \u2014 Google Cloud Service Account Setup");
|
|
205
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
206
|
+
console.log("\nStep 1: Open the Google Cloud Console");
|
|
207
|
+
console.log(" https://console.cloud.google.com/iam-admin/serviceaccounts");
|
|
208
|
+
console.log("\nStep 2: Create a service account");
|
|
209
|
+
console.log(" \u2022 Click 'Create Service Account'");
|
|
210
|
+
console.log(" \u2022 Name it: gpc-deploy (or any name you like)");
|
|
211
|
+
console.log(" \u2022 Description: GPC Google Play Console access");
|
|
212
|
+
console.log("\nStep 3: Grant roles");
|
|
213
|
+
console.log(" No GCP roles needed \u2014 permissions are managed in Google Play Console.");
|
|
214
|
+
console.log("\nStep 4: Download the JSON key");
|
|
215
|
+
console.log(" \u2022 Click your new service account \u2192 Keys \u2192 Add Key \u2192 Create new key \u2192 JSON");
|
|
216
|
+
console.log(" \u2022 Save as: ~/gpc-service-account.json");
|
|
217
|
+
console.log("\nStep 5: Add to Google Play Console");
|
|
218
|
+
console.log(" https://play.google.com/console/developers");
|
|
219
|
+
console.log(" \u2022 Users and Permissions \u2192 Invite new users");
|
|
220
|
+
console.log(" \u2022 Paste the service account email (ends with @...gserviceaccount.com)");
|
|
221
|
+
console.log(" \u2022 Grant: Release manager + View app info + Reply to reviews");
|
|
222
|
+
console.log("\nStep 6: Authenticate");
|
|
223
|
+
console.log(" gpc auth login --service-account ~/gpc-service-account.json");
|
|
224
|
+
console.log("\nStep 7: Verify");
|
|
225
|
+
console.log(" gpc doctor");
|
|
226
|
+
console.log("\nSee full docs: https://yasserstudio.github.io/gpc/guide/authentication");
|
|
227
|
+
});
|
|
130
228
|
auth.command("profiles").description("List configured profiles").action(async () => {
|
|
131
229
|
const { listProfiles } = await import("@gpc-cli/config");
|
|
132
230
|
const config = await loadConfig();
|
|
@@ -148,4 +246,4 @@ function registerAuthCommands(program) {
|
|
|
148
246
|
export {
|
|
149
247
|
registerAuthCommands
|
|
150
248
|
};
|
|
151
|
-
//# sourceMappingURL=auth-
|
|
249
|
+
//# sourceMappingURL=auth-XGSTT5G5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/auth.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { access } from \"node:fs/promises\";\nimport { resolveAuth, loadServiceAccountKey, clearTokenCache, AuthError } from \"@gpc-cli/auth\";\nimport { loadConfig, getCacheDir } from \"@gpc-cli/config\";\nimport { formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nasync function askQuestion(\n rl: ReturnType<typeof createInterface>,\n question: string,\n): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => resolve(answer.trim()));\n });\n}\n\nasync function runLoginWizard(): Promise<void> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n try {\n console.log(\"\\nGPC Authentication Setup\");\n console.log(\"─────────────────────────\");\n\n // Step 1: auth method\n console.log(\"\\nAuthentication methods:\");\n console.log(\" 1. Service account key file (recommended)\");\n console.log(\" 2. Application Default Credentials (ADC)\");\n const method = await askQuestion(rl, \"\\nSelect method [1]: \");\n const useAdc = method === \"2\";\n\n if (useAdc) {\n const { resolveAuth: ra } = await import(\"@gpc-cli/auth\");\n const client = await ra();\n console.log(`\\nAuthenticated via Application Default Credentials`);\n console.log(`Account: ${client.getClientEmail()}`);\n return;\n }\n\n // Step 2: credentials path with validation loop\n let saPath = \"\";\n while (!saPath) {\n const input = await askQuestion(rl, \"\\nPath to service account JSON key: \");\n if (!input) {\n console.log(\" Path is required.\");\n continue;\n }\n try {\n await access(input);\n saPath = input;\n } catch {\n console.log(` File not found: ${input}`);\n }\n }\n\n // Step 3: optional profile name\n const profileName = await askQuestion(rl, \"\\nProfile name (optional, press Enter to skip): \");\n\n // Step 4: default package name\n const packageName = await askQuestion(\n rl,\n \"Default package name (optional, e.g. com.example.app): \",\n );\n\n // Apply settings\n const { loadServiceAccountKey: loadKey } = await import(\"@gpc-cli/auth\");\n const key = await loadKey(saPath);\n\n if (profileName) {\n const { setProfileConfig } = await import(\"@gpc-cli/config\");\n await setProfileConfig(profileName, {\n auth: { serviceAccount: saPath },\n ...(packageName && { app: packageName }),\n });\n console.log(`\\nProfile \"${profileName}\" configured with ${key.client_email}`);\n } else {\n const { setConfigValue } = await import(\"@gpc-cli/config\");\n await setConfigValue(\"auth.serviceAccount\", saPath);\n if (packageName) await setConfigValue(\"app\", packageName);\n console.log(`\\nAuthenticated as ${key.client_email}`);\n }\n console.log(`Project: ${key.project_id}`);\n console.log(\"\\nRun 'gpc doctor' to verify your setup.\");\n } finally {\n rl.close();\n }\n}\n\nexport function registerAuthCommands(program: Command): void {\n const auth = program.command(\"auth\").description(\"Manage authentication\");\n\n auth\n .command(\"login\")\n .description(\"Authenticate with Google Play Developer API\")\n .option(\"--service-account <path>\", \"Path to service account JSON key file\")\n .option(\"--adc\", \"Use Application Default Credentials\")\n .option(\"--profile <name>\", \"Store credentials under a named profile\")\n .action(async (options: { serviceAccount?: string; adc?: boolean; profile?: string }) => {\n try {\n if (options.serviceAccount) {\n const key = await loadServiceAccountKey(options.serviceAccount);\n\n if (options.profile) {\n const { setProfileConfig } = await import(\"@gpc-cli/config\");\n await setProfileConfig(options.profile, {\n auth: { serviceAccount: options.serviceAccount },\n });\n console.log(`Profile \"${options.profile}\" configured with ${key.client_email}`);\n } else {\n const { setConfigValue } = await import(\"@gpc-cli/config\");\n await setConfigValue(\"auth.serviceAccount\", options.serviceAccount);\n console.log(`Authenticated as ${key.client_email}`);\n }\n console.log(`Project: ${key.project_id}`);\n } else if (options.adc) {\n const client = await resolveAuth();\n console.log(`Authenticated via Application Default Credentials`);\n console.log(`Account: ${client.getClientEmail()}`);\n } else {\n // Interactive wizard when no flags provided and in interactive mode\n const opts = program.opts();\n const interactive =\n opts[\"interactive\"] !== false && opts[\"ci\"] !== true && process.stdin.isTTY;\n if (interactive) {\n await runLoginWizard();\n } else {\n console.log(\"Usage: gpc auth login --service-account <path>\");\n console.log(\"\");\n console.log(\"Authentication methods:\");\n console.log(\" --service-account <path> Service account JSON key file\");\n console.log(\" --adc Application Default Credentials\");\n console.log(\"\");\n console.log(\"Options:\");\n console.log(\" --profile <name> Store under a named profile\");\n }\n }\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`Error: ${error.message}`);\n if (error.suggestion) console.error(`Suggestion: ${error.suggestion}`);\n process.exit(3);\n }\n throw error;\n }\n });\n\n auth\n .command(\"status\")\n .description(\"Show current authentication status\")\n .action(async () => {\n const config = await loadConfig();\n try {\n const client = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const format = getOutputFormat(program, config);\n const data = {\n authenticated: true,\n account: client.getClientEmail(),\n project: client.getProjectId(),\n ...(config.profile && { profile: config.profile }),\n };\n console.log(formatOutput(data, format));\n } catch (error) {\n if (error instanceof AuthError) {\n const format = getOutputFormat(program, config);\n const data = {\n authenticated: false,\n error: error.message,\n suggestion: error.suggestion,\n };\n console.log(formatOutput(data, format));\n process.exit(3);\n }\n throw error;\n }\n });\n\n auth\n .command(\"logout\")\n .description(\"Clear stored credentials and token cache\")\n .action(async () => {\n const { setConfigValue } = await import(\"@gpc-cli/config\");\n await setConfigValue(\"auth.serviceAccount\", \"\");\n await clearTokenCache(getCacheDir());\n console.log(\"Credentials and token cache cleared.\");\n });\n\n auth\n .command(\"whoami\")\n .description(\"Show current authenticated identity\")\n .action(async () => {\n try {\n const config = await loadConfig();\n const client = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n console.log(client.getClientEmail());\n } catch {\n console.error(\"Not authenticated. Run: gpc auth login\");\n process.exit(3);\n }\n });\n\n auth\n .command(\"switch <profile>\")\n .description(\"Switch to a named profile\")\n .action(async (profile: string) => {\n try {\n // Verify profile exists\n const config = await loadConfig({ profile });\n const { setConfigValue } = await import(\"@gpc-cli/config\");\n await setConfigValue(\"profile\", profile);\n console.log(`Switched to profile \"${profile}\"`);\n if (config.auth?.serviceAccount) {\n console.log(`Service account: ${config.auth.serviceAccount}`);\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(2);\n }\n });\n\n auth\n .command(\"token\")\n .description(\"Print the current access token (useful for manual API calls)\")\n .action(async () => {\n try {\n const config = await loadConfig();\n const authClient = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const token = await authClient.getAccessToken();\n console.log(token);\n } catch (error) {\n if (error instanceof AuthError) {\n console.error(`Error: ${error.message}`);\n process.exit(3);\n }\n throw error;\n }\n });\n\n auth\n .command(\"setup-gcp\")\n .description(\"Step-by-step guide to create a Google Cloud service account\")\n .action(() => {\n console.log(\"\\nGPC — Google Cloud Service Account Setup\");\n console.log(\"═══════════════════════════════════════════\");\n console.log(\"\\nStep 1: Open the Google Cloud Console\");\n console.log(\" https://console.cloud.google.com/iam-admin/serviceaccounts\");\n console.log(\"\\nStep 2: Create a service account\");\n console.log(\" • Click 'Create Service Account'\");\n console.log(\" • Name it: gpc-deploy (or any name you like)\");\n console.log(\" • Description: GPC Google Play Console access\");\n console.log(\"\\nStep 3: Grant roles\");\n console.log(\" No GCP roles needed — permissions are managed in Google Play Console.\");\n console.log(\"\\nStep 4: Download the JSON key\");\n console.log(\" • Click your new service account → Keys → Add Key → Create new key → JSON\");\n console.log(\" • Save as: ~/gpc-service-account.json\");\n console.log(\"\\nStep 5: Add to Google Play Console\");\n console.log(\" https://play.google.com/console/developers\");\n console.log(\" • Users and Permissions → Invite new users\");\n console.log(\" • Paste the service account email (ends with @...gserviceaccount.com)\");\n console.log(\" • Grant: Release manager + View app info + Reply to reviews\");\n console.log(\"\\nStep 6: Authenticate\");\n console.log(\" gpc auth login --service-account ~/gpc-service-account.json\");\n console.log(\"\\nStep 7: Verify\");\n console.log(\" gpc doctor\");\n console.log(\"\\nSee full docs: https://yasserstudio.github.io/gpc/guide/authentication\");\n });\n\n auth\n .command(\"profiles\")\n .description(\"List configured profiles\")\n .action(async () => {\n const { listProfiles } = await import(\"@gpc-cli/config\");\n const config = await loadConfig();\n const profiles = await listProfiles();\n const format = getOutputFormat(program, config);\n\n if (profiles.length === 0) {\n console.log(\n \"No profiles configured. Use: gpc auth login --service-account <path> --profile <name>\",\n );\n return;\n }\n\n const data = profiles.map((name) => ({\n name,\n active: name === config.profile,\n }));\n console.log(formatOutput(data, format));\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createProgram,
|
|
4
4
|
handleCliError,
|
|
5
5
|
loadPlugins
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XHI7VKWZ.js";
|
|
7
7
|
import {
|
|
8
8
|
checkForUpdate,
|
|
9
9
|
formatUpdateNotification
|
|
@@ -44,7 +44,7 @@ if (!_isJsonMode && !_isQuiet && !existsSync(getUserConfigPath())) {
|
|
|
44
44
|
}
|
|
45
45
|
await setupNetworking();
|
|
46
46
|
initAudit(getConfigDir());
|
|
47
|
-
var currentVersion =
|
|
47
|
+
var currentVersion = "0.9.36";
|
|
48
48
|
var isUpdateCommand = process.argv[2] === "update";
|
|
49
49
|
var updateCheckPromise = isUpdateCommand ? Promise.resolve(null) : checkForUpdate(currentVersion);
|
|
50
50
|
if (process.argv.includes("--ci")) {
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bin.ts","../src/networking.ts"],"sourcesContent":["if (process.env[\"GPC_NO_COLOR\"] === \"1\") process.env[\"NO_COLOR\"] = \"1\";\nif (process.argv.includes(\"--no-color\")) {\n process.env[\"NO_COLOR\"] = \"1\";\n}\nimport { existsSync } from \"node:fs\";\nimport { setupNetworking } from \"./networking.js\";\nimport { createProgram } from \"./program.js\";\nimport { loadPlugins } from \"./plugins.js\";\nimport { handleCliError } from \"./error-handler.js\";\nimport { initAudit, sendWebhook } from \"@gpc-cli/core\";\nimport type { WebhookPayload } from \"@gpc-cli/core\";\nimport { getConfigDir, loadConfig, getUserConfigPath } from \"@gpc-cli/config\";\nimport { checkForUpdate, formatUpdateNotification } from \"./update-check.js\";\n\n// First-run banner\nconst _isJsonMode =\n process.argv.includes(\"--json\") ||\n process.argv.includes(\"-j\") ||\n process.argv.includes(\"--ci\") ||\n (process.argv.includes(\"--output\") &&\n process.argv[process.argv.indexOf(\"--output\") + 1] === \"json\") ||\n (process.argv.includes(\"-o\")
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts","../src/networking.ts"],"sourcesContent":["if (process.env[\"GPC_NO_COLOR\"] === \"1\") process.env[\"NO_COLOR\"] = \"1\";\nif (process.argv.includes(\"--no-color\")) {\n process.env[\"NO_COLOR\"] = \"1\";\n}\nimport { existsSync } from \"node:fs\";\nimport { setupNetworking } from \"./networking.js\";\nimport { createProgram } from \"./program.js\";\nimport { loadPlugins } from \"./plugins.js\";\nimport { handleCliError } from \"./error-handler.js\";\nimport { initAudit, sendWebhook } from \"@gpc-cli/core\";\nimport type { WebhookPayload } from \"@gpc-cli/core\";\nimport { getConfigDir, loadConfig, getUserConfigPath } from \"@gpc-cli/config\";\nimport { checkForUpdate, formatUpdateNotification } from \"./update-check.js\";\n\n// First-run banner\nconst _isJsonMode =\n process.argv.includes(\"--json\") ||\n process.argv.includes(\"-j\") ||\n process.argv.includes(\"--ci\") ||\n (process.argv.includes(\"--output\") &&\n process.argv[process.argv.indexOf(\"--output\") + 1] === \"json\") ||\n (process.argv.includes(\"-o\") && process.argv[process.argv.indexOf(\"-o\") + 1] === \"json\");\nconst _isQuiet = process.argv.includes(\"--quiet\") || process.argv.includes(\"-q\");\n\nif (!_isJsonMode && !_isQuiet && !existsSync(getUserConfigPath())) {\n process.stderr.write(\"✦ First time? Run gpc config init to get set up.\\n\\n\");\n}\n\nawait setupNetworking();\ninitAudit(getConfigDir());\n\nconst currentVersion = process.env[\"__GPC_VERSION\"] || \"0.0.0\";\n\n// Skip passive update check when the user is explicitly running `gpc update` —\n// that command does its own check against the GitHub Releases API.\nconst isUpdateCommand = process.argv[2] === \"update\";\n\n// Start update check before command execution (non-blocking)\nconst updateCheckPromise = isUpdateCommand ? Promise.resolve(null) : checkForUpdate(currentVersion);\n\n// Handle --ci and --json flags early (before command parsing)\nif (process.argv.includes(\"--ci\")) {\n process.env[\"CI\"] = \"1\";\n // --ci implies --output json --no-interactive --no-color\n if (!process.argv.some((a) => a.startsWith(\"--output\") || a.startsWith(\"-o\"))) {\n process.argv.push(\"--output\", \"json\");\n }\n if (!process.argv.includes(\"--no-interactive\")) {\n process.argv.push(\"--no-interactive\");\n }\n if (!process.argv.includes(\"--no-color\")) {\n process.argv.push(\"--no-color\");\n }\n}\nif (process.argv.includes(\"--json\") || process.argv.includes(\"-j\")) {\n if (!process.argv.some((a) => a.startsWith(\"--output\") || a.startsWith(\"-o\"))) {\n process.argv.push(\"--output\", \"json\");\n }\n}\n\nconst pluginManager = await loadPlugins();\nconst program = await createProgram(pluginManager);\n\n// GPC_DEBUG=1 enables verbose mode without mutating process.argv\nif (process.env[\"GPC_DEBUG\"] === \"1\") {\n program.setOptionValueWithSource(\"verbose\", true, \"env\");\n}\n\nconst startTime = Date.now();\nlet commandSuccess = true;\nlet commandError: string | undefined;\n\nawait program.parseAsync(process.argv).catch((error: unknown) => {\n commandSuccess = false;\n commandError = error instanceof Error ? error.message : String(error);\n const exitCode = handleCliError(error);\n process.exit(exitCode);\n});\n\n// Send webhook notification if --notify was set\nconst notifyOpt = program.opts()[\"notify\"] as string | boolean | undefined;\nif (notifyOpt !== undefined && notifyOpt !== false) {\n try {\n const config = await loadConfig();\n if (config.webhooks) {\n const commandName = process.argv\n .slice(2)\n .filter((a) => !a.startsWith(\"--notify\"))\n .join(\" \");\n const payload: WebhookPayload = {\n command: commandName || \"unknown\",\n success: commandSuccess,\n duration: Date.now() - startTime,\n app: program.opts()[\"app\"] as string | undefined,\n error: commandError,\n };\n\n const target = typeof notifyOpt === \"string\" ? notifyOpt : undefined;\n // Fire-and-forget — do not block exit\n sendWebhook(config.webhooks, payload, target).catch(() => {});\n }\n } catch {\n // Never let webhook logic break the CLI\n }\n}\n\n// After command completes, show update notification if available\n// isUpdateCommand is declared above — update check was skipped for this command\ntry {\n const result = await Promise.race([\n updateCheckPromise,\n new Promise<null>((resolve) => setTimeout(() => resolve(null), 3000)),\n ]);\n\n if (\n result &&\n result.updateAvailable &&\n !isUpdateCommand &&\n process.stdout.isTTY &&\n !process.argv.includes(\"--json\") &&\n program.opts()[\"output\"] !== \"json\"\n ) {\n process.stderr.write(`\\n${formatUpdateNotification(result)}\\n`);\n }\n} catch {\n // Silently ignore update check failures\n}\n","/**\n * Set up proxy and custom CA certificate support.\n * Must be called before any fetch() calls.\n */\nexport async function setupNetworking(): Promise<void> {\n // Map GPC_CA_CERT to NODE_EXTRA_CA_CERTS (works in both Node and Bun)\n const caCert = process.env[\"GPC_CA_CERT\"];\n if (caCert && !process.env[\"NODE_EXTRA_CA_CERTS\"]) {\n process.env[\"NODE_EXTRA_CA_CERTS\"] = caCert;\n }\n\n // In standalone binary mode, Bun handles HTTPS_PROXY/HTTP_PROXY natively\n if (process.env[\"__GPC_BINARY\"] === \"1\") return;\n\n const proxyUrl =\n process.env[\"HTTPS_PROXY\"] ||\n process.env[\"https_proxy\"] ||\n process.env[\"HTTP_PROXY\"] ||\n process.env[\"http_proxy\"];\n if (proxyUrl) {\n try {\n // @ts-expect-error undici types not available in all environments\n const { ProxyAgent, setGlobalDispatcher } = await import(\"undici\");\n setGlobalDispatcher(new ProxyAgent(proxyUrl));\n } catch {\n console.error(\"Warning: Proxy support requires Node.js 20+. HTTPS_PROXY will be ignored.\");\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
analyzeBundle,
|
|
9
9
|
compareBundles,
|
|
10
|
+
topFiles,
|
|
11
|
+
checkBundleSize,
|
|
10
12
|
formatOutput
|
|
11
13
|
} from "@gpc-cli/core";
|
|
12
14
|
function formatSize(bytes) {
|
|
@@ -22,7 +24,7 @@ function formatDelta(delta) {
|
|
|
22
24
|
}
|
|
23
25
|
function registerBundleCommands(program) {
|
|
24
26
|
const bundle = program.command("bundle").description("Analyze app bundles and APKs");
|
|
25
|
-
bundle.command("analyze <file>").description("Analyze size breakdown of an AAB or APK").option("--threshold <mb>", "Fail if compressed size exceeds threshold (MB)", parseFloat).action(async (file, opts) => {
|
|
27
|
+
bundle.command("analyze <file>").description("Analyze size breakdown of an AAB or APK").option("--threshold <mb>", "Fail if compressed size exceeds threshold (MB)", parseFloat).option("--top <n>", "Show top N largest files", (v) => parseInt(v, 10)).option("--config <file>", "Check against .bundlesize.json thresholds").action(async (file, opts) => {
|
|
26
28
|
const format = getOutputFormat(program, await getConfig());
|
|
27
29
|
try {
|
|
28
30
|
const analysis = await analyzeBundle(file);
|
|
@@ -82,6 +84,29 @@ File: ${analysis.filePath}`);
|
|
|
82
84
|
console.log("\nCategories:");
|
|
83
85
|
console.log(formatOutput(categoryRows, "table"));
|
|
84
86
|
}
|
|
87
|
+
if (opts.top !== void 0) {
|
|
88
|
+
const largest = topFiles(analysis, opts.top);
|
|
89
|
+
const topRows = largest.map((e) => ({
|
|
90
|
+
path: e.path,
|
|
91
|
+
compressed: formatSize(e.compressedSize),
|
|
92
|
+
uncompressed: formatSize(e.uncompressedSize),
|
|
93
|
+
category: e.category
|
|
94
|
+
}));
|
|
95
|
+
console.log(`
|
|
96
|
+
Top ${opts.top} files:`);
|
|
97
|
+
console.log(formatOutput(topRows, format === "json" ? "table" : format));
|
|
98
|
+
}
|
|
99
|
+
if (opts.config) {
|
|
100
|
+
const check = await checkBundleSize(analysis, opts.config);
|
|
101
|
+
if (!check.passed) {
|
|
102
|
+
console.error(`
|
|
103
|
+
Bundle size check failed:`);
|
|
104
|
+
for (const v of check.violations) {
|
|
105
|
+
console.error(` ${v.subject}: ${formatSize(v.actual)} > max ${formatSize(v.max)}`);
|
|
106
|
+
}
|
|
107
|
+
process.exit(6);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
85
110
|
if (opts.threshold !== void 0) {
|
|
86
111
|
const thresholdBytes = opts.threshold * 1024 * 1024;
|
|
87
112
|
if (analysis.totalCompressed > thresholdBytes) {
|
|
@@ -100,10 +125,7 @@ Threshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold}
|
|
|
100
125
|
bundle.command("compare <file1> <file2>").description("Compare size differences between two bundles or APKs").action(async (file1, file2) => {
|
|
101
126
|
const format = getOutputFormat(program, await getConfig());
|
|
102
127
|
try {
|
|
103
|
-
const [before, after] = await Promise.all([
|
|
104
|
-
analyzeBundle(file1),
|
|
105
|
-
analyzeBundle(file2)
|
|
106
|
-
]);
|
|
128
|
+
const [before, after] = await Promise.all([analyzeBundle(file1), analyzeBundle(file2)]);
|
|
107
129
|
const comparison = compareBundles(before, after);
|
|
108
130
|
if (format === "json") {
|
|
109
131
|
console.log(formatOutput(comparison, format));
|
|
@@ -125,9 +147,15 @@ Threshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold}
|
|
|
125
147
|
console.log();
|
|
126
148
|
console.log(`| | Path | Size |`);
|
|
127
149
|
console.log(`| --- | --- | --- |`);
|
|
128
|
-
console.log(
|
|
129
|
-
|
|
130
|
-
|
|
150
|
+
console.log(
|
|
151
|
+
`| Before | \`${comparison.before.path}\` | ${formatSize(comparison.before.totalCompressed)} |`
|
|
152
|
+
);
|
|
153
|
+
console.log(
|
|
154
|
+
`| After | \`${comparison.after.path}\` | ${formatSize(comparison.after.totalCompressed)} |`
|
|
155
|
+
);
|
|
156
|
+
console.log(
|
|
157
|
+
`| **Delta** | | **${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)** |`
|
|
158
|
+
);
|
|
131
159
|
if (moduleRows.length > 0) {
|
|
132
160
|
console.log();
|
|
133
161
|
console.log(`### Module Changes`);
|
|
@@ -142,11 +170,17 @@ Threshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold}
|
|
|
142
170
|
}
|
|
143
171
|
} else {
|
|
144
172
|
const sign = comparison.sizeDelta >= 0 ? "+" : "";
|
|
145
|
-
console.log(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
173
|
+
console.log(
|
|
174
|
+
`
|
|
175
|
+
Before: ${comparison.before.path} (${formatSize(comparison.before.totalCompressed)})`
|
|
176
|
+
);
|
|
177
|
+
console.log(
|
|
178
|
+
`After: ${comparison.after.path} (${formatSize(comparison.after.totalCompressed)})`
|
|
179
|
+
);
|
|
180
|
+
console.log(
|
|
181
|
+
`Delta: ${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)
|
|
182
|
+
`
|
|
183
|
+
);
|
|
150
184
|
const moduleRows = comparison.moduleDeltas.filter((m) => m.delta !== 0).map((m) => ({
|
|
151
185
|
module: m.module,
|
|
152
186
|
before: formatSize(m.before),
|
|
@@ -181,4 +215,4 @@ async function getConfig() {
|
|
|
181
215
|
export {
|
|
182
216
|
registerBundleCommands
|
|
183
217
|
};
|
|
184
|
-
//# sourceMappingURL=bundle-
|
|
218
|
+
//# sourceMappingURL=bundle-F43TD2BQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/bundle.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport {\n analyzeBundle,\n compareBundles,\n topFiles,\n checkBundleSize,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nfunction formatSize(bytes: number): string {\n const abs = Math.abs(bytes);\n const sign = bytes < 0 ? \"-\" : \"\";\n if (abs < 1024) return `${sign}${abs} B`;\n if (abs < 1024 * 1024) return `${sign}${(abs / 1024).toFixed(1)} KB`;\n return `${sign}${(abs / (1024 * 1024)).toFixed(2)} MB`;\n}\n\nfunction formatDelta(delta: number): string {\n const prefix = delta > 0 ? \"+\" : \"\";\n return `${prefix}${formatSize(delta)}`;\n}\n\nexport function registerBundleCommands(program: Command): void {\n const bundle = program.command(\"bundle\").description(\"Analyze app bundles and APKs\");\n\n bundle\n .command(\"analyze <file>\")\n .description(\"Analyze size breakdown of an AAB or APK\")\n .option(\"--threshold <mb>\", \"Fail if compressed size exceeds threshold (MB)\", parseFloat)\n .option(\"--top <n>\", \"Show top N largest files\", (v) => parseInt(v, 10))\n .option(\"--config <file>\", \"Check against .bundlesize.json thresholds\")\n .action(async (file: string, opts: { threshold?: number; top?: number; config?: string }) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const analysis = await analyzeBundle(file);\n\n if (format === \"json\") {\n console.log(formatOutput(analysis, format));\n } else if (format === \"markdown\") {\n const moduleRows = analysis.modules.map((m) => ({\n module: m.name,\n compressed: formatSize(m.compressedSize),\n uncompressed: formatSize(m.uncompressedSize),\n entries: m.entries,\n }));\n const categoryRows = analysis.categories.map((c) => ({\n category: c.name,\n compressed: formatSize(c.compressedSize),\n uncompressed: formatSize(c.uncompressedSize),\n entries: c.entries,\n }));\n console.log(`## Bundle Analysis: \\`${analysis.filePath}\\``);\n console.log();\n console.log(`| Property | Value |`);\n console.log(`| --- | --- |`);\n console.log(`| Type | ${analysis.fileType.toUpperCase()} |`);\n console.log(`| Total compressed | ${formatSize(analysis.totalCompressed)} |`);\n console.log(`| Total uncompressed | ${formatSize(analysis.totalUncompressed)} |`);\n console.log(`| Entries | ${analysis.entryCount} |`);\n console.log();\n console.log(`### Modules`);\n console.log();\n console.log(formatOutput(moduleRows, \"markdown\"));\n console.log();\n console.log(`### Categories`);\n console.log();\n console.log(formatOutput(categoryRows, \"markdown\"));\n } else {\n console.log(`\\nFile: ${analysis.filePath}`);\n console.log(`Type: ${analysis.fileType.toUpperCase()}`);\n console.log(`Total compressed: ${formatSize(analysis.totalCompressed)}`);\n console.log(`Total uncompressed: ${formatSize(analysis.totalUncompressed)}`);\n console.log(`Entries: ${analysis.entryCount}\\n`);\n\n // Modules table\n const moduleRows = analysis.modules.map((m) => ({\n module: m.name,\n compressed: formatSize(m.compressedSize),\n uncompressed: formatSize(m.uncompressedSize),\n entries: m.entries,\n }));\n console.log(\"Modules:\");\n console.log(formatOutput(moduleRows, \"table\"));\n\n // Categories table\n const categoryRows = analysis.categories.map((c) => ({\n category: c.name,\n compressed: formatSize(c.compressedSize),\n uncompressed: formatSize(c.uncompressedSize),\n entries: c.entries,\n }));\n console.log(\"\\nCategories:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n\n // Top N files\n if (opts.top !== undefined) {\n const largest = topFiles(analysis, opts.top);\n const topRows = largest.map((e) => ({\n path: e.path,\n compressed: formatSize(e.compressedSize),\n uncompressed: formatSize(e.uncompressedSize),\n category: e.category,\n }));\n console.log(`\\nTop ${opts.top} files:`);\n console.log(formatOutput(topRows, format === \"json\" ? \"table\" : format));\n }\n\n // .bundlesize.json config check\n if (opts.config) {\n const check = await checkBundleSize(analysis, opts.config);\n if (!check.passed) {\n console.error(`\\nBundle size check failed:`);\n for (const v of check.violations) {\n console.error(` ${v.subject}: ${formatSize(v.actual)} > max ${formatSize(v.max)}`);\n }\n process.exit(6);\n }\n }\n\n // Legacy --threshold check\n if (opts.threshold !== undefined) {\n const thresholdBytes = opts.threshold * 1024 * 1024;\n if (analysis.totalCompressed > thresholdBytes) {\n console.error(\n `\\nThreshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold} MB`,\n );\n process.exit(6);\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n bundle\n .command(\"compare <file1> <file2>\")\n .description(\"Compare size differences between two bundles or APKs\")\n .action(async (file1: string, file2: string) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const [before, after] = await Promise.all([analyzeBundle(file1), analyzeBundle(file2)]);\n const comparison = compareBundles(before, after);\n\n if (format === \"json\") {\n console.log(formatOutput(comparison, format));\n } else if (format === \"markdown\") {\n const sign = comparison.sizeDelta >= 0 ? \"+\" : \"\";\n const moduleRows = comparison.moduleDeltas\n .filter((m) => m.delta !== 0)\n .map((m) => ({\n module: m.module,\n before: formatSize(m.before),\n after: formatSize(m.after),\n delta: formatDelta(m.delta),\n }));\n const categoryRows = comparison.categoryDeltas\n .filter((c) => c.delta !== 0)\n .map((c) => ({\n category: c.category,\n before: formatSize(c.before),\n after: formatSize(c.after),\n delta: formatDelta(c.delta),\n }));\n console.log(`## Bundle Comparison`);\n console.log();\n console.log(`| | Path | Size |`);\n console.log(`| --- | --- | --- |`);\n console.log(\n `| Before | \\`${comparison.before.path}\\` | ${formatSize(comparison.before.totalCompressed)} |`,\n );\n console.log(\n `| After | \\`${comparison.after.path}\\` | ${formatSize(comparison.after.totalCompressed)} |`,\n );\n console.log(\n `| **Delta** | | **${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)** |`,\n );\n if (moduleRows.length > 0) {\n console.log();\n console.log(`### Module Changes`);\n console.log();\n console.log(formatOutput(moduleRows, \"markdown\"));\n }\n if (categoryRows.length > 0) {\n console.log();\n console.log(`### Category Changes`);\n console.log();\n console.log(formatOutput(categoryRows, \"markdown\"));\n }\n } else {\n const sign = comparison.sizeDelta >= 0 ? \"+\" : \"\";\n console.log(\n `\\nBefore: ${comparison.before.path} (${formatSize(comparison.before.totalCompressed)})`,\n );\n console.log(\n `After: ${comparison.after.path} (${formatSize(comparison.after.totalCompressed)})`,\n );\n console.log(\n `Delta: ${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)\\n`,\n );\n\n // Module deltas\n const moduleRows = comparison.moduleDeltas\n .filter((m) => m.delta !== 0)\n .map((m) => ({\n module: m.module,\n before: formatSize(m.before),\n after: formatSize(m.after),\n delta: formatDelta(m.delta),\n }));\n if (moduleRows.length > 0) {\n console.log(\"Module changes:\");\n console.log(formatOutput(moduleRows, \"table\"));\n }\n\n // Category deltas\n const categoryRows = comparison.categoryDeltas\n .filter((c) => c.delta !== 0)\n .map((c) => ({\n category: c.category,\n before: formatSize(c.before),\n after: formatSize(c.after),\n delta: formatDelta(c.delta),\n }));\n if (categoryRows.length > 0) {\n console.log(\"\\nCategory changes:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n\nasync function getConfig() {\n const { loadConfig } = await import(\"@gpc-cli/config\");\n return loadConfig();\n}\n"],"mappings":"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","names":[]}
|