@gpc-cli/cli 0.9.46 → 0.9.48
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 +36 -15
- package/dist/{anomalies-UDE4NGHJ.js → anomalies-V3AFS4LD.js} +20 -3
- package/dist/anomalies-V3AFS4LD.js.map +1 -0
- package/dist/{audit-JASSHRWN.js → audit-VTWXTXC6.js} +5 -4
- package/dist/audit-VTWXTXC6.js.map +1 -0
- package/dist/{auth-OTA3SV3J.js → auth-BA4FE2PO.js} +85 -24
- package/dist/auth-BA4FE2PO.js.map +1 -0
- package/dist/bin.js +2 -2
- package/dist/{changelog-7COFZO7Q.js → changelog-QLDFG5TV.js} +5 -5
- package/dist/changelog-QLDFG5TV.js.map +1 -0
- package/dist/{chunk-6OWN6S6X.js → chunk-XBH2V2XK.js} +21 -18
- package/dist/chunk-XBH2V2XK.js.map +1 -0
- package/dist/{config-2FTCYEGD.js → config-NQQF4522.js} +2 -2
- package/dist/{doctor-H4X7Q57B.js → doctor-KGYUWHL5.js} +36 -3
- package/dist/doctor-KGYUWHL5.js.map +1 -0
- package/dist/{feedback-XP765TOO.js → feedback-RMLYHTAH.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{install-skills-6QDUXI5F.js → install-skills-JKPYZHYS.js} +2 -2
- package/dist/{install-skills-6QDUXI5F.js.map → install-skills-JKPYZHYS.js.map} +1 -1
- package/dist/{purchases-DAWTMXP6.js → purchases-UBFLNYZC.js} +16 -3
- package/dist/purchases-UBFLNYZC.js.map +1 -0
- package/dist/{releases-2I3WBULC.js → releases-OUJ65774.js} +10 -7
- package/dist/releases-OUJ65774.js.map +1 -0
- package/dist/{reviews-BCCXIQ6C.js → reviews-YCBBM656.js} +15 -4
- package/dist/reviews-YCBBM656.js.map +1 -0
- package/dist/rtdn-LID2B7XZ.js +87 -0
- package/dist/rtdn-LID2B7XZ.js.map +1 -0
- package/dist/{subscriptions-DZP3Y7O7.js → subscriptions-LURZFPGJ.js} +5 -4
- package/dist/subscriptions-LURZFPGJ.js.map +1 -0
- package/dist/{tracks-YHMO2A6B.js → tracks-DO7C5OSE.js} +3 -3
- package/dist/tracks-DO7C5OSE.js.map +1 -0
- package/dist/{update-OMALGIBR.js → update-QUQ7N7QJ.js} +2 -2
- package/dist/{version-NCSNXNVN.js → version-WGE5Q7QH.js} +2 -2
- package/dist/{vitals-C23L2Y2E.js → vitals-PJEQUUAK.js} +35 -7
- package/dist/vitals-PJEQUUAK.js.map +1 -0
- package/package.json +5 -5
- package/dist/anomalies-UDE4NGHJ.js.map +0 -1
- package/dist/audit-JASSHRWN.js.map +0 -1
- package/dist/auth-OTA3SV3J.js.map +0 -1
- package/dist/changelog-7COFZO7Q.js.map +0 -1
- package/dist/chunk-6OWN6S6X.js.map +0 -1
- package/dist/doctor-H4X7Q57B.js.map +0 -1
- package/dist/purchases-DAWTMXP6.js.map +0 -1
- package/dist/releases-2I3WBULC.js.map +0 -1
- package/dist/reviews-BCCXIQ6C.js.map +0 -1
- package/dist/subscriptions-DZP3Y7O7.js.map +0 -1
- package/dist/tracks-YHMO2A6B.js.map +0 -1
- package/dist/vitals-C23L2Y2E.js.map +0 -1
- /package/dist/{config-2FTCYEGD.js.map → config-NQQF4522.js.map} +0 -0
- /package/dist/{feedback-XP765TOO.js.map → feedback-RMLYHTAH.js.map} +0 -0
- /package/dist/{update-OMALGIBR.js.map → update-QUQ7N7QJ.js.map} +0 -0
- /package/dist/{version-NCSNXNVN.js.map → version-WGE5Q7QH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
<p align="center"><strong>Ship Android apps from your terminal.</strong></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
The complete CLI for Google Play —
|
|
6
|
+
The complete CLI for Google Play — 204 API endpoints, one tool.<br>
|
|
7
7
|
Releases, rollouts, metadata, vitals, reviews, subscriptions, reports, and more.
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
11
|
<a href="https://www.npmjs.com/package/@gpc-cli/cli"><img src="https://img.shields.io/npm/v/@gpc-cli/cli?color=00D26A" alt="npm version"></a>
|
|
12
12
|
<a href="https://github.com/yasserstudio/gpc"><img src="https://img.shields.io/github/stars/yasserstudio/gpc" alt="GitHub Stars"></a>
|
|
13
|
-
<img src="https://img.shields.io/badge/Tests-
|
|
13
|
+
<img src="https://img.shields.io/badge/Tests-1845_passing-00D26A" alt="Tests">
|
|
14
14
|
<img src="https://img.shields.io/badge/License-MIT-yellow" alt="License">
|
|
15
15
|
</p>
|
|
16
16
|
|
|
@@ -23,8 +23,11 @@ npm install -g @gpc-cli/cli
|
|
|
23
23
|
# Homebrew (macOS/Linux)
|
|
24
24
|
brew install yasserstudio/tap/gpc
|
|
25
25
|
|
|
26
|
-
# Standalone binary (no Node.js required)
|
|
26
|
+
# Standalone binary — macOS/Linux (no Node.js required)
|
|
27
27
|
curl -fsSL https://raw.githubusercontent.com/yasserstudio/gpc/main/scripts/install.sh | sh
|
|
28
|
+
|
|
29
|
+
# Standalone binary — Windows (PowerShell)
|
|
30
|
+
iwr -useb https://raw.githubusercontent.com/yasserstudio/gpc/main/scripts/install.ps1 | iex
|
|
28
31
|
```
|
|
29
32
|
|
|
30
33
|
Free. Open-source. No account required beyond your existing Google Play service account.
|
|
@@ -35,13 +38,13 @@ Free. Open-source. No account required beyond your existing Google Play service
|
|
|
35
38
|
# Authenticate
|
|
36
39
|
gpc auth login --service-account path/to/key.json
|
|
37
40
|
|
|
38
|
-
# Verify your setup
|
|
41
|
+
# Verify your setup (20 automated checks)
|
|
39
42
|
gpc doctor
|
|
40
43
|
|
|
41
44
|
# App health at a glance — releases, vitals, and reviews in one command
|
|
42
45
|
gpc status
|
|
43
46
|
|
|
44
|
-
# Upload and release
|
|
47
|
+
# Upload and release (AAB or APK)
|
|
45
48
|
gpc releases upload app.aab --track internal
|
|
46
49
|
|
|
47
50
|
# Promote to production
|
|
@@ -75,25 +78,42 @@ REVIEWS (last 30 days)
|
|
|
75
78
|
|
|
76
79
|
## What You Get
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
204 API endpoints across these command groups:
|
|
79
82
|
|
|
80
83
|
| Group | What you can do |
|
|
81
84
|
| ----------------- | -------------------------------------------------------------------------------------- |
|
|
82
|
-
| **Releases** | Upload, promote, rollout increase/halt/resume,
|
|
83
|
-
| **
|
|
84
|
-
| **
|
|
85
|
-
| **
|
|
85
|
+
| **Releases** | Upload AAB/APK, promote, rollout increase/halt/resume, draft releases, `publish` |
|
|
86
|
+
| **Preflight** | 9 offline AAB policy scanners — catches rejections before upload |
|
|
87
|
+
| **Listings** | Pull and push store listings, upload screenshots — Fastlane metadata compatible |
|
|
88
|
+
| **Reviews** | Filter by stars, reply (350-char validated), auto-paginate, export to CSV |
|
|
89
|
+
| **Vitals** | Crash rates, ANR, startup, rendering, battery, memory — with CI threshold gates |
|
|
90
|
+
| **Status** | Releases + vitals + reviews in one command, `--watch`, `--since-last` diff |
|
|
86
91
|
| **Bundle** | Per-module size breakdown, build-to-build diff, size CI gates |
|
|
87
|
-
| **Subscriptions** |
|
|
88
|
-
| **IAP**
|
|
89
|
-
| **Purchases** | Verify, acknowledge, cancel, refund,
|
|
90
|
-
| **Reports** |
|
|
92
|
+
| **Subscriptions** | Base plans, offers, pricing, batch operations, RTDN notification decoding |
|
|
93
|
+
| **IAP / OTP** | One-time products, purchase options, batch get/update/delete |
|
|
94
|
+
| **Purchases** | Verify, acknowledge, cancel, refund, voided (with `--type` subscription filter) |
|
|
95
|
+
| **Reports** | Financial and stats report downloads |
|
|
91
96
|
| **Testers** | Add, remove, import from CSV |
|
|
92
97
|
| **Users** | Invite, update, remove, manage per-app grants |
|
|
98
|
+
| **Doctor** | 20 automated setup checks — config, auth, connectivity, app access, key age |
|
|
99
|
+
| **Anomalies** | Auto-detect vitals quality spikes from Reporting API |
|
|
100
|
+
| **More** | Init, diff, changelog, quota, train, cache, feedback, enterprise, games |
|
|
101
|
+
|
|
102
|
+
## Exit Codes
|
|
103
|
+
|
|
104
|
+
| Code | Meaning |
|
|
105
|
+
| ---- | ------------------------------------- |
|
|
106
|
+
| `0` | Success |
|
|
107
|
+
| `1` | General error |
|
|
108
|
+
| `2` | Usage error (bad arguments) |
|
|
109
|
+
| `3` | Authentication error |
|
|
110
|
+
| `4` | API error (rate limit, permission) |
|
|
111
|
+
| `5` | Network error |
|
|
112
|
+
| `6` | Threshold breach (vitals CI alerting) |
|
|
93
113
|
|
|
94
114
|
## CI/CD Ready
|
|
95
115
|
|
|
96
|
-
JSON output when piped. Formatted tables in your terminal. Semantic exit codes (0
|
|
116
|
+
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`.
|
|
97
117
|
|
|
98
118
|
```yaml
|
|
99
119
|
- name: Upload
|
|
@@ -102,6 +122,7 @@ JSON output when piped. Formatted tables in your terminal. Semantic exit codes (
|
|
|
102
122
|
GPC_APP: com.example.myapp
|
|
103
123
|
run: |
|
|
104
124
|
npm install -g @gpc-cli/cli
|
|
125
|
+
gpc preflight app.aab --fail-on error
|
|
105
126
|
gpc releases upload app.aab --track internal
|
|
106
127
|
```
|
|
107
128
|
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
resolvePackageName
|
|
4
4
|
} from "./chunk-NQH4G7BI.js";
|
|
5
|
+
import {
|
|
6
|
+
yellow
|
|
7
|
+
} from "./chunk-FAN4ZITI.js";
|
|
5
8
|
import {
|
|
6
9
|
getOutputFormat
|
|
7
10
|
} from "./chunk-ELXAK7GI.js";
|
|
@@ -9,7 +12,7 @@ import {
|
|
|
9
12
|
// src/commands/anomalies.ts
|
|
10
13
|
import { loadConfig } from "@gpc-cli/config";
|
|
11
14
|
import { resolveAuth } from "@gpc-cli/auth";
|
|
12
|
-
import { createReportingClient } from "@gpc-cli/api";
|
|
15
|
+
import { createReportingClient, PlayApiError } from "@gpc-cli/api";
|
|
13
16
|
import { getVitalsAnomalies, formatOutput } from "@gpc-cli/core";
|
|
14
17
|
async function getReportingClient(config) {
|
|
15
18
|
const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
|
|
@@ -22,7 +25,21 @@ function registerAnomaliesCommands(program) {
|
|
|
22
25
|
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
23
26
|
const reporting = await getReportingClient(config);
|
|
24
27
|
const format = getOutputFormat(program, config);
|
|
25
|
-
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await getVitalsAnomalies(reporting, packageName);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (err instanceof PlayApiError && err.statusCode === 403) {
|
|
33
|
+
if (format === "json") {
|
|
34
|
+
console.log(formatOutput({ anomalies: [], message: "Reporting API not enabled or insufficient permissions" }, format));
|
|
35
|
+
} else {
|
|
36
|
+
console.log(`${yellow("\u26A0")} No anomaly data available. The Reporting API may not be enabled for this project.`);
|
|
37
|
+
console.log(` Enable it at: https://console.cloud.google.com/apis/library/playdeveloperreporting.googleapis.com`);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
26
43
|
const items = result["anomalies"];
|
|
27
44
|
if (format !== "json") {
|
|
28
45
|
if (!items || items.length === 0) {
|
|
@@ -46,4 +63,4 @@ function registerAnomaliesCommands(program) {
|
|
|
46
63
|
export {
|
|
47
64
|
registerAnomaliesCommands
|
|
48
65
|
};
|
|
49
|
-
//# sourceMappingURL=anomalies-
|
|
66
|
+
//# sourceMappingURL=anomalies-V3AFS4LD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/anomalies.ts"],"sourcesContent":["import { resolvePackageName } from \"../resolve.js\";\nimport 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, PlayApiError } from \"@gpc-cli/api\";\nimport { getVitalsAnomalies, formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { yellow } from \"../colors.js\";\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 let result;\n try {\n result = await getVitalsAnomalies(reporting, packageName);\n } catch (err) {\n if (err instanceof PlayApiError && err.statusCode === 403) {\n if (format === \"json\") {\n console.log(formatOutput({ anomalies: [], message: \"Reporting API not enabled or insufficient permissions\" }, format));\n } else {\n console.log(`${yellow(\"⚠\")} No anomaly data available. The Reporting API may not be enabled for this project.`);\n console.log(` Enable it at: https://console.cloud.google.com/apis/library/playdeveloperreporting.googleapis.com`);\n }\n return;\n }\n throw err;\n }\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 });\n}\n"],"mappings":";;;;;;;;;;;;AAGA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB,oBAAoB;AACpD,SAAS,oBAAoB,oBAAoB;AAKjD,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;AACJ,QAAI;AACF,eAAS,MAAM,mBAAmB,WAAW,WAAW;AAAA,IAC1D,SAAS,KAAK;AACZ,UAAI,eAAe,gBAAgB,IAAI,eAAe,KAAK;AACzD,YAAI,WAAW,QAAQ;AACrB,kBAAQ,IAAI,aAAa,EAAE,WAAW,CAAC,GAAG,SAAS,wDAAwD,GAAG,MAAM,CAAC;AAAA,QACvH,OAAO;AACL,kBAAQ,IAAI,GAAG,OAAO,QAAG,CAAC,oFAAoF;AAC9G,kBAAQ,IAAI,qGAAqG;AAAA,QACnH;AACA;AAAA,MACF;AACA,YAAM;AAAA,IACR;AACA,UAAM,QAAS,OAA8C,WAAW;AAIxE,QAAI,WAAW,QAAQ;AACrB,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,gBAAQ,IAAI,wBAAwB;AACpC;AAAA,MACF;AACA,YAAM,OAAO,MAAM,IAAI,CAAC,SAAS;AAC/B,cAAM,IAAI;AACV,eAAO;AAAA,UACL,MAAM,OAAO,EAAE,MAAM,KAAK,GAAG;AAAA,UAC7B,WAAW,OAAO,EAAE,WAAW,KAAK,GAAG;AAAA,UACvC,mBAAmB,OAAO,EAAE,mBAAmB,KAAK,GAAG;AAAA,QACzD;AAAA,MACF,CAAC;AACD,cAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,IACxC,OAAO;AACL,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C;AAAA,EACF,CAAC;AACL;","names":[]}
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
listAuditEvents,
|
|
18
18
|
searchAuditEvents,
|
|
19
19
|
clearAuditLog,
|
|
20
|
-
formatOutput
|
|
20
|
+
formatOutput,
|
|
21
|
+
maybePaginate
|
|
21
22
|
} from "@gpc-cli/core";
|
|
22
23
|
function formatAuditTimestamp(iso) {
|
|
23
24
|
const date = new Date(iso);
|
|
@@ -72,9 +73,9 @@ function registerAuditCommands(program) {
|
|
|
72
73
|
success: e.success !== void 0 ? String(e.success) : "-",
|
|
73
74
|
durationMs: e.durationMs ?? "-"
|
|
74
75
|
}));
|
|
75
|
-
|
|
76
|
+
await maybePaginate(formatOutput(rows, format));
|
|
76
77
|
} else {
|
|
77
|
-
|
|
78
|
+
await maybePaginate(formatOutput(events, format));
|
|
78
79
|
}
|
|
79
80
|
});
|
|
80
81
|
audit.command("search <query>").description("Search audit events by keyword").action(async (query) => {
|
|
@@ -121,4 +122,4 @@ function registerAuditCommands(program) {
|
|
|
121
122
|
export {
|
|
122
123
|
registerAuditCommands
|
|
123
124
|
};
|
|
124
|
-
//# sourceMappingURL=audit-
|
|
125
|
+
//# sourceMappingURL=audit-VTWXTXC6.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 maybePaginate,\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 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 await maybePaginate(formatOutput(rows, format));\n } else {\n await maybePaginate(formatOutput(events, format));\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 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 });\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 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 });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -242,30 +242,91 @@ function registerAuthCommands(program) {
|
|
|
242
242
|
const token = await authClient.getAccessToken();
|
|
243
243
|
console.log(token);
|
|
244
244
|
});
|
|
245
|
-
auth.command("setup-gcp").description("Step-by-step guide to create a Google Cloud service account").action(() => {
|
|
245
|
+
auth.command("setup-gcp").description("Step-by-step guide to create a Google Cloud service account").option("--key <path>", "Path to an already-downloaded service account JSON key").action(async (options) => {
|
|
246
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
247
|
+
const { resolve: resolve2 } = await import("path");
|
|
246
248
|
console.log("\nGPC \u2014 Google Cloud Service Account Setup");
|
|
247
|
-
console.log("\u2550
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
249
|
+
console.log("\u2550".repeat(50));
|
|
250
|
+
let keyPath = options.key;
|
|
251
|
+
if (!keyPath) {
|
|
252
|
+
console.log("\nStep 1: Open the Google Cloud Console");
|
|
253
|
+
console.log(" https://console.cloud.google.com/iam-admin/serviceaccounts");
|
|
254
|
+
console.log("\nStep 2: Create a service account");
|
|
255
|
+
console.log(" \u2022 Click 'Create Service Account'");
|
|
256
|
+
console.log(" \u2022 Name it: gpc-deploy (or any name you like)");
|
|
257
|
+
console.log(" \u2022 Description: GPC Google Play Console access");
|
|
258
|
+
console.log("\nStep 3: Grant roles");
|
|
259
|
+
console.log(" No GCP roles needed \u2014 permissions are managed in Google Play Console.");
|
|
260
|
+
console.log("\nStep 4: Download the JSON key");
|
|
261
|
+
console.log(" \u2022 Click your new service account \u2192 Keys \u2192 Add Key \u2192 Create new key \u2192 JSON");
|
|
262
|
+
console.log(" \u2022 Save as: ~/gpc-service-account.json");
|
|
263
|
+
console.log("\nStep 5: Add to Google Play Console");
|
|
264
|
+
console.log(" https://play.google.com/console/developers");
|
|
265
|
+
console.log(" \u2022 Users and Permissions \u2192 Invite new users");
|
|
266
|
+
console.log(" \u2022 Paste the service account email (ends with @...gserviceaccount.com)");
|
|
267
|
+
console.log(" \u2022 Grant: Release manager + View app info + Reply to reviews");
|
|
268
|
+
const commonPaths = [
|
|
269
|
+
"~/gpc-service-account.json",
|
|
270
|
+
"./service-account.json",
|
|
271
|
+
"./gpc-service-account.json",
|
|
272
|
+
"~/.config/gpc/service-account.json"
|
|
273
|
+
].map((p) => resolve2(p.replace("~", process.env["HOME"] || "")));
|
|
274
|
+
for (const p of commonPaths) {
|
|
275
|
+
if (existsSync(p)) {
|
|
276
|
+
console.log(`
|
|
277
|
+
\u2713 Found key file: ${p}`);
|
|
278
|
+
keyPath = p;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!keyPath) {
|
|
283
|
+
console.log("\nOnce you've downloaded the key, run:");
|
|
284
|
+
console.log(" gpc auth setup-gcp --key <path/to/key.json>");
|
|
285
|
+
console.log("\nOr authenticate directly:");
|
|
286
|
+
console.log(" gpc auth login --service-account <path/to/key.json>");
|
|
287
|
+
console.log("\nSee full docs: https://yasserstudio.github.io/gpc/guide/authentication");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const absPath = resolve2(keyPath.replace("~", process.env["HOME"] || ""));
|
|
292
|
+
if (!existsSync(absPath)) {
|
|
293
|
+
console.error(`
|
|
294
|
+
\u2717 File not found: ${absPath}`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const content = JSON.parse(readFileSync(absPath, "utf-8"));
|
|
299
|
+
if (content.type !== "service_account") {
|
|
300
|
+
console.error(`
|
|
301
|
+
\u2717 Not a service account key (type: ${content.type})`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
console.log(`
|
|
305
|
+
\u2713 Valid service account key`);
|
|
306
|
+
console.log(` Email: ${content.client_email}`);
|
|
307
|
+
console.log(` Project: ${content.project_id}`);
|
|
308
|
+
} catch {
|
|
309
|
+
console.error(`
|
|
310
|
+
\u2717 Invalid JSON file: ${absPath}`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
console.log(`
|
|
314
|
+
Step 6: Authenticating...`);
|
|
315
|
+
try {
|
|
316
|
+
const auth2 = await resolveAuth({ serviceAccountPath: absPath });
|
|
317
|
+
await auth2.getAccessToken();
|
|
318
|
+
const { setConfigValue: setConfigValue2 } = await import("@gpc-cli/config");
|
|
319
|
+
await setConfigValue2("auth.serviceAccount", absPath);
|
|
320
|
+
console.log(`\u2713 Authenticated and saved to config`);
|
|
321
|
+
} catch (err) {
|
|
322
|
+
console.error(`\u2717 Authentication failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
323
|
+
console.log(`
|
|
324
|
+
Try manually: gpc auth login --service-account ${absPath}`);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
console.log(`
|
|
328
|
+
Step 7: Verifying setup...`);
|
|
329
|
+
console.log(` Run: gpc doctor`);
|
|
269
330
|
});
|
|
270
331
|
auth.command("profiles").description("List configured profiles").action(async () => {
|
|
271
332
|
const { listProfiles } = await import("@gpc-cli/config");
|
|
@@ -288,4 +349,4 @@ function registerAuthCommands(program) {
|
|
|
288
349
|
export {
|
|
289
350
|
registerAuthCommands
|
|
290
351
|
};
|
|
291
|
-
//# sourceMappingURL=auth-
|
|
352
|
+
//# sourceMappingURL=auth-BA4FE2PO.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 { resolve } from \"node:path\";\nimport { resolveAuth, loadServiceAccountKey, clearTokenCache, AuthError } from \"@gpc-cli/auth\";\nimport { loadConfig, getCacheDir, deleteConfigValue, setConfigValue } from \"@gpc-cli/config\";\nimport { formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\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\n/** Resolve a path to absolute (prevents CWD-dependent config). */\nfunction toAbsolutePath(pathStr: string): string {\n return resolve(pathStr);\n}\n\n/** Verify that the saved credentials can acquire a token. */\nasync function verifyToken(serviceAccountPath?: string): Promise<{ email: string; ok: boolean; error?: string }> {\n try {\n const client = await resolveAuth({ serviceAccountPath });\n const email = client.getClientEmail();\n await client.getAccessToken();\n return { email, ok: true };\n } catch (err) {\n const email = \"unknown\";\n const msg = err instanceof Error ? err.message : String(err);\n return { email, ok: false, error: msg };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Interactive login wizard\n// ---------------------------------------------------------------------------\n\nasync function runLoginWizard(program: Command): Promise<void> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n try {\n console.log(\"\\nGPC Authentication Setup\");\n 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\");\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 client = await resolveAuth();\n const email = client.getClientEmail();\n // Verify token works\n await client.getAccessToken();\n outputLoginResult(program, { account: email, method: \"adc\", verified: true });\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 = toAbsolutePath(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 key = await loadServiceAccountKey(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 } else {\n await setConfigValue(\"auth.serviceAccount\", saPath);\n if (packageName) await setConfigValue(\"app\", packageName);\n }\n\n // Verify token\n const verification = await verifyToken(saPath);\n\n outputLoginResult(program, {\n account: key.client_email,\n project: key.project_id,\n method: \"service-account\",\n profile: profileName || undefined,\n verified: verification.ok,\n verifyError: verification.error,\n });\n } finally {\n rl.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Structured login output\n// ---------------------------------------------------------------------------\n\ninterface LoginResult {\n account: string;\n project?: string;\n method: string;\n profile?: string;\n verified: boolean;\n verifyError?: string;\n}\n\nfunction outputLoginResult(program: Command, result: LoginResult): void {\n const parentOpts = program.opts() ?? {};\n const jsonMode = !!(parentOpts[\"json\"] || parentOpts[\"output\"] === \"json\");\n\n if (jsonMode) {\n console.log(\n JSON.stringify({\n success: true,\n account: result.account,\n project: result.project,\n method: result.method,\n profile: result.profile,\n verified: result.verified,\n ...(result.verifyError && { verifyError: result.verifyError }),\n }),\n );\n return;\n }\n\n if (result.profile) {\n console.log(`\\nProfile \"${result.profile}\" configured with ${result.account}`);\n } else if (result.method === \"adc\") {\n console.log(`\\nAuthenticated via Application Default Credentials`);\n console.log(`Account: ${result.account}`);\n } else {\n console.log(`\\nAuthenticated as ${result.account}`);\n }\n if (result.project) console.log(`Project: ${result.project}`);\n\n if (result.verified) {\n console.log(\"Verified: token acquired successfully\");\n } else if (result.verifyError) {\n console.log(`Warning: token verification failed: ${result.verifyError}`);\n console.log(\"Credentials saved, but check your setup with: gpc doctor\");\n }\n\n if (!result.verifyError) {\n console.log(\"\\nRun 'gpc doctor' to verify your full setup.\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command registration\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 if (options.serviceAccount) {\n const absolutePath = toAbsolutePath(options.serviceAccount);\n const key = await loadServiceAccountKey(absolutePath);\n\n if (options.profile) {\n const { setProfileConfig } = await import(\"@gpc-cli/config\");\n await setProfileConfig(options.profile, {\n auth: { serviceAccount: absolutePath },\n });\n } else {\n await setConfigValue(\"auth.serviceAccount\", absolutePath);\n }\n\n // Verify token works\n const verification = await verifyToken(absolutePath);\n\n outputLoginResult(program, {\n account: key.client_email,\n project: key.project_id,\n method: \"service-account\",\n profile: options.profile,\n verified: verification.ok,\n verifyError: verification.error,\n });\n } else if (options.adc) {\n const client = await resolveAuth();\n const email = client.getClientEmail();\n await client.getAccessToken();\n outputLoginResult(program, { account: email, method: \"adc\", verified: true });\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(program);\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 });\n\n auth\n .command(\"status\")\n .description(\"Show current authentication status\")\n .action(async () => {\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n try {\n const client = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\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 data = {\n authenticated: false,\n error: error.message,\n suggestion: error.suggestion,\n };\n console.log(formatOutput(data, format));\n }\n throw error; // Let error handler set exit code 3\n }\n });\n\n auth\n .command(\"logout\")\n .description(\"Clear stored credentials and token cache\")\n .option(\"--profile <name>\", \"Clear credentials for a specific profile\")\n .action(async (options: { profile?: string }) => {\n if (options.profile) {\n const { deleteProfile } = await import(\"@gpc-cli/config\");\n const deleted = await deleteProfile(options.profile);\n if (deleted) {\n console.log(`Profile \"${options.profile}\" removed.`);\n } else {\n console.log(`Profile \"${options.profile}\" not found.`);\n }\n } else {\n await deleteConfigValue(\"auth.serviceAccount\");\n console.log(\"Credentials cleared.\");\n }\n await clearTokenCache(getCacheDir());\n console.log(\"Token cache cleared.\");\n });\n\n auth\n .command(\"whoami\")\n .description(\"Show current authenticated identity\")\n .action(async () => {\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 });\n\n auth\n .command(\"switch <profile>\")\n .description(\"Switch to a named profile\")\n .action(async (profile: string) => {\n // loadConfig with the profile will throw ConfigError if profile doesn't exist\n const config = await loadConfig({ profile });\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 });\n\n auth\n .command(\"token\")\n .description(\"Print the current access token (useful for manual API calls)\")\n .action(async () => {\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 });\n\n auth\n .command(\"setup-gcp\")\n .description(\"Step-by-step guide to create a Google Cloud service account\")\n .option(\"--key <path>\", \"Path to an already-downloaded service account JSON key\")\n .action(async (options: { key?: string }) => {\n const { existsSync, readFileSync } = await import(\"node:fs\");\n const { resolve } = await import(\"node:path\");\n\n console.log(\"\\nGPC \\u2014 Google Cloud Service Account Setup\");\n console.log(\"\\u2550\".repeat(50));\n\n // If key path provided or user already has one, skip to validation\n let keyPath = options.key;\n\n if (!keyPath) {\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(\" \\u2022 Click 'Create Service Account'\");\n console.log(\" \\u2022 Name it: gpc-deploy (or any name you like)\");\n console.log(\" \\u2022 Description: GPC Google Play Console access\");\n console.log(\"\\nStep 3: Grant roles\");\n console.log(\" No GCP roles needed \\u2014 permissions are managed in Google Play Console.\");\n console.log(\"\\nStep 4: Download the JSON key\");\n console.log(\" \\u2022 Click your new service account \\u2192 Keys \\u2192 Add Key \\u2192 Create new key \\u2192 JSON\");\n console.log(\" \\u2022 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(\" \\u2022 Users and Permissions \\u2192 Invite new users\");\n console.log(\" \\u2022 Paste the service account email (ends with @...gserviceaccount.com)\");\n console.log(\" \\u2022 Grant: Release manager + View app info + Reply to reviews\");\n\n // Try to auto-detect a key file\n const commonPaths = [\n \"~/gpc-service-account.json\",\n \"./service-account.json\",\n \"./gpc-service-account.json\",\n \"~/.config/gpc/service-account.json\",\n ].map((p) => resolve(p.replace(\"~\", process.env[\"HOME\"] || \"\")));\n\n for (const p of commonPaths) {\n if (existsSync(p)) {\n console.log(`\\n\\u2713 Found key file: ${p}`);\n keyPath = p;\n break;\n }\n }\n\n if (!keyPath) {\n console.log(\"\\nOnce you've downloaded the key, run:\");\n console.log(\" gpc auth setup-gcp --key <path/to/key.json>\");\n console.log(\"\\nOr authenticate directly:\");\n console.log(\" gpc auth login --service-account <path/to/key.json>\");\n console.log(\"\\nSee full docs: https://yasserstudio.github.io/gpc/guide/authentication\");\n return;\n }\n }\n\n // Validate the key file\n const absPath = resolve(keyPath.replace(\"~\", process.env[\"HOME\"] || \"\"));\n if (!existsSync(absPath)) {\n console.error(`\\n\\u2717 File not found: ${absPath}`);\n return;\n }\n\n try {\n const content = JSON.parse(readFileSync(absPath, \"utf-8\"));\n if (content.type !== \"service_account\") {\n console.error(`\\n\\u2717 Not a service account key (type: ${content.type})`);\n return;\n }\n console.log(`\\n\\u2713 Valid service account key`);\n console.log(` Email: ${content.client_email}`);\n console.log(` Project: ${content.project_id}`);\n } catch {\n console.error(`\\n\\u2717 Invalid JSON file: ${absPath}`);\n return;\n }\n\n // Auto-login\n console.log(`\\nStep 6: Authenticating...`);\n try {\n const auth = await resolveAuth({ serviceAccountPath: absPath });\n await auth.getAccessToken();\n const { setConfigValue } = await import(\"@gpc-cli/config\");\n await setConfigValue(\"auth.serviceAccount\", absPath);\n console.log(`\\u2713 Authenticated and saved to config`);\n } catch (err) {\n console.error(`\\u2717 Authentication failed: ${err instanceof Error ? err.message : String(err)}`);\n console.log(`\\nTry manually: gpc auth login --service-account ${absPath}`);\n return;\n }\n\n // Auto-run doctor\n console.log(`\\nStep 7: Verifying setup...`);\n console.log(` Run: gpc doctor`);\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":";;;;;;AACA,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,aAAa,uBAAuB,iBAAiB,iBAAiB;AAC/E,SAAS,YAAY,aAAa,mBAAmB,sBAAsB;AAC3E,SAAS,oBAAoB;AAO7B,eAAe,YACb,IACA,UACiB;AACjB,SAAO,IAAI,QAAQ,CAACA,aAAY;AAC9B,OAAG,SAAS,UAAU,CAAC,WAAWA,SAAQ,OAAO,KAAK,CAAC,CAAC;AAAA,EAC1D,CAAC;AACH;AAGA,SAAS,eAAe,SAAyB;AAC/C,SAAO,QAAQ,OAAO;AACxB;AAGA,eAAe,YAAY,oBAAsF;AAC/G,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,EAAE,mBAAmB,CAAC;AACvD,UAAM,QAAQ,OAAO,eAAe;AACpC,UAAM,OAAO,eAAe;AAC5B,WAAO,EAAE,OAAO,IAAI,KAAK;AAAA,EAC3B,SAAS,KAAK;AACZ,UAAM,QAAQ;AACd,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,WAAO,EAAE,OAAO,IAAI,OAAO,OAAO,IAAI;AAAA,EACxC;AACF;AAMA,eAAe,eAAe,SAAiC;AAC7D,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAE3E,MAAI;AACF,YAAQ,IAAI,4BAA4B;AACxC,YAAQ,IAAI,wJAAwJ;AAGpK,YAAQ,IAAI,2BAA2B;AACvC,YAAQ,IAAI,6CAA6C;AACzD,YAAQ,IAAI,4CAA4C;AACxD,UAAM,SAAS,MAAM,YAAY,IAAI,uBAAuB;AAC5D,UAAM,SAAS,WAAW;AAE1B,QAAI,QAAQ;AACV,YAAM,SAAS,MAAM,YAAY;AACjC,YAAM,QAAQ,OAAO,eAAe;AAEpC,YAAM,OAAO,eAAe;AAC5B,wBAAkB,SAAS,EAAE,SAAS,OAAO,QAAQ,OAAO,UAAU,KAAK,CAAC;AAC5E;AAAA,IACF;AAGA,QAAI,SAAS;AACb,WAAO,CAAC,QAAQ;AACd,YAAM,QAAQ,MAAM,YAAY,IAAI,sCAAsC;AAC1E,UAAI,CAAC,OAAO;AACV,gBAAQ,IAAI,qBAAqB;AACjC;AAAA,MACF;AACA,UAAI;AACF,cAAM,OAAO,KAAK;AAClB,iBAAS,eAAe,KAAK;AAAA,MAC/B,QAAQ;AACN,gBAAQ,IAAI,qBAAqB,KAAK,EAAE;AAAA,MAC1C;AAAA,IACF;AAGA,UAAM,cAAc,MAAM,YAAY,IAAI,kDAAkD;AAG5F,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAGA,UAAM,MAAM,MAAM,sBAAsB,MAAM;AAE9C,QAAI,aAAa;AACf,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,iBAAiB;AAC3D,YAAM,iBAAiB,aAAa;AAAA,QAClC,MAAM,EAAE,gBAAgB,OAAO;AAAA,QAC/B,GAAI,eAAe,EAAE,KAAK,YAAY;AAAA,MACxC,CAAC;AAAA,IACH,OAAO;AACL,YAAM,eAAe,uBAAuB,MAAM;AAClD,UAAI,YAAa,OAAM,eAAe,OAAO,WAAW;AAAA,IAC1D;AAGA,UAAM,eAAe,MAAM,YAAY,MAAM;AAE7C,sBAAkB,SAAS;AAAA,MACzB,SAAS,IAAI;AAAA,MACb,SAAS,IAAI;AAAA,MACb,QAAQ;AAAA,MACR,SAAS,eAAe;AAAA,MACxB,UAAU,aAAa;AAAA,MACvB,aAAa,aAAa;AAAA,IAC5B,CAAC;AAAA,EACH,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAeA,SAAS,kBAAkB,SAAkB,QAA2B;AACtE,QAAM,aAAa,QAAQ,KAAK,KAAK,CAAC;AACtC,QAAM,WAAW,CAAC,EAAE,WAAW,MAAM,KAAK,WAAW,QAAQ,MAAM;AAEnE,MAAI,UAAU;AACZ,YAAQ;AAAA,MACN,KAAK,UAAU;AAAA,QACb,SAAS;AAAA,QACT,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,QAAQ,OAAO;AAAA,QACf,SAAS,OAAO;AAAA,QAChB,UAAU,OAAO;AAAA,QACjB,GAAI,OAAO,eAAe,EAAE,aAAa,OAAO,YAAY;AAAA,MAC9D,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI;AAAA,WAAc,OAAO,OAAO,qBAAqB,OAAO,OAAO,EAAE;AAAA,EAC/E,WAAW,OAAO,WAAW,OAAO;AAClC,YAAQ,IAAI;AAAA,kDAAqD;AACjE,YAAQ,IAAI,YAAY,OAAO,OAAO,EAAE;AAAA,EAC1C,OAAO;AACL,YAAQ,IAAI;AAAA,mBAAsB,OAAO,OAAO,EAAE;AAAA,EACpD;AACA,MAAI,OAAO,QAAS,SAAQ,IAAI,YAAY,OAAO,OAAO,EAAE;AAE5D,MAAI,OAAO,UAAU;AACnB,YAAQ,IAAI,uCAAuC;AAAA,EACrD,WAAW,OAAO,aAAa;AAC7B,YAAQ,IAAI,uCAAuC,OAAO,WAAW,EAAE;AACvE,YAAQ,IAAI,0DAA0D;AAAA,EACxE;AAEA,MAAI,CAAC,OAAO,aAAa;AACvB,YAAQ,IAAI,+CAA+C;AAAA,EAC7D;AACF;AAMO,SAAS,qBAAqB,SAAwB;AAC3D,QAAM,OAAO,QAAQ,QAAQ,MAAM,EAAE,YAAY,uBAAuB;AAExE,OACG,QAAQ,OAAO,EACf,YAAY,6CAA6C,EACzD,OAAO,4BAA4B,uCAAuC,EAC1E,OAAO,SAAS,qCAAqC,EACrD,OAAO,oBAAoB,yCAAyC,EACpE,OAAO,OAAO,YAA0E;AACvF,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,eAAe,eAAe,QAAQ,cAAc;AAC1D,YAAM,MAAM,MAAM,sBAAsB,YAAY;AAEpD,UAAI,QAAQ,SAAS;AACnB,cAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,iBAAiB;AAC3D,cAAM,iBAAiB,QAAQ,SAAS;AAAA,UACtC,MAAM,EAAE,gBAAgB,aAAa;AAAA,QACvC,CAAC;AAAA,MACH,OAAO;AACL,cAAM,eAAe,uBAAuB,YAAY;AAAA,MAC1D;AAGA,YAAM,eAAe,MAAM,YAAY,YAAY;AAEnD,wBAAkB,SAAS;AAAA,QACzB,SAAS,IAAI;AAAA,QACb,SAAS,IAAI;AAAA,QACb,QAAQ;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB,UAAU,aAAa;AAAA,QACvB,aAAa,aAAa;AAAA,MAC5B,CAAC;AAAA,IACH,WAAW,QAAQ,KAAK;AACtB,YAAM,SAAS,MAAM,YAAY;AACjC,YAAM,QAAQ,OAAO,eAAe;AACpC,YAAM,OAAO,eAAe;AAC5B,wBAAkB,SAAS,EAAE,SAAS,OAAO,QAAQ,OAAO,UAAU,KAAK,CAAC;AAAA,IAC9E,OAAO;AAEL,YAAM,OAAO,QAAQ,KAAK;AAC1B,YAAM,cACJ,KAAK,aAAa,MAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,QAAQ,MAAM;AACxE,UAAI,aAAa;AACf,cAAM,eAAe,OAAO;AAAA,MAC9B,OAAO;AACL,gBAAQ,IAAI,gDAAgD;AAC5D,gBAAQ,IAAI,EAAE;AACd,gBAAQ,IAAI,yBAAyB;AACrC,gBAAQ,IAAI,2DAA2D;AACvE,gBAAQ,IAAI,6DAA6D;AACzE,gBAAQ,IAAI,EAAE;AACd,gBAAQ,IAAI,UAAU;AACtB,gBAAQ,IAAI,yDAAyD;AAAA,MACvE;AAAA,IACF;AAAA,EACF,CAAC;AAEH,OACG,QAAQ,QAAQ,EAChB,YAAY,oCAAoC,EAChD,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,QAAI;AACF,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B,oBAAoB,OAAO,MAAM;AAAA,QACjC,WAAW,YAAY;AAAA,MACzB,CAAC;AACD,YAAM,OAAO;AAAA,QACX,eAAe;AAAA,QACf,SAAS,OAAO,eAAe;AAAA,QAC/B,SAAS,OAAO,aAAa;AAAA,QAC7B,GAAI,OAAO,WAAW,EAAE,SAAS,OAAO,QAAQ;AAAA,MAClD;AACA,cAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,IACxC,SAAS,OAAO;AACd,UAAI,iBAAiB,WAAW;AAC9B,cAAM,OAAO;AAAA,UACX,eAAe;AAAA,UACf,OAAO,MAAM;AAAA,UACb,YAAY,MAAM;AAAA,QACpB;AACA,gBAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,MACxC;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAEH,OACG,QAAQ,QAAQ,EAChB,YAAY,0CAA0C,EACtD,OAAO,oBAAoB,0CAA0C,EACrE,OAAO,OAAO,YAAkC;AAC/C,QAAI,QAAQ,SAAS;AACnB,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,iBAAiB;AACxD,YAAM,UAAU,MAAM,cAAc,QAAQ,OAAO;AACnD,UAAI,SAAS;AACX,gBAAQ,IAAI,YAAY,QAAQ,OAAO,YAAY;AAAA,MACrD,OAAO;AACL,gBAAQ,IAAI,YAAY,QAAQ,OAAO,cAAc;AAAA,MACvD;AAAA,IACF,OAAO;AACL,YAAM,kBAAkB,qBAAqB;AAC7C,cAAQ,IAAI,sBAAsB;AAAA,IACpC;AACA,UAAM,gBAAgB,YAAY,CAAC;AACnC,YAAQ,IAAI,sBAAsB;AAAA,EACpC,CAAC;AAEH,OACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,SAAS,MAAM,YAAY;AAAA,MAC/B,oBAAoB,OAAO,MAAM;AAAA,MACjC,WAAW,YAAY;AAAA,IACzB,CAAC;AACD,YAAQ,IAAI,OAAO,eAAe,CAAC;AAAA,EACrC,CAAC;AAEH,OACG,QAAQ,kBAAkB,EAC1B,YAAY,2BAA2B,EACvC,OAAO,OAAO,YAAoB;AAEjC,UAAM,SAAS,MAAM,WAAW,EAAE,QAAQ,CAAC;AAC3C,UAAM,eAAe,WAAW,OAAO;AACvC,YAAQ,IAAI,wBAAwB,OAAO,GAAG;AAC9C,QAAI,OAAO,MAAM,gBAAgB;AAC/B,cAAQ,IAAI,oBAAoB,OAAO,KAAK,cAAc,EAAE;AAAA,IAC9D;AAAA,EACF,CAAC;AAEH,OACG,QAAQ,OAAO,EACf,YAAY,8DAA8D,EAC1E,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,aAAa,MAAM,YAAY;AAAA,MACnC,oBAAoB,OAAO,MAAM;AAAA,MACjC,WAAW,YAAY;AAAA,IACzB,CAAC;AACD,UAAM,QAAQ,MAAM,WAAW,eAAe;AAC9C,YAAQ,IAAI,KAAK;AAAA,EACnB,CAAC;AAEH,OACG,QAAQ,WAAW,EACnB,YAAY,6DAA6D,EACzE,OAAO,gBAAgB,wDAAwD,EAC/E,OAAO,OAAO,YAA8B;AAC3C,UAAM,EAAE,YAAY,aAAa,IAAI,MAAM,OAAO,IAAS;AAC3D,UAAM,EAAE,SAAAA,SAAQ,IAAI,MAAM,OAAO,MAAW;AAE5C,YAAQ,IAAI,iDAAiD;AAC7D,YAAQ,IAAI,SAAS,OAAO,EAAE,CAAC;AAG/B,QAAI,UAAU,QAAQ;AAEtB,QAAI,CAAC,SAAS;AACZ,cAAQ,IAAI,yCAAyC;AACrD,cAAQ,IAAI,8DAA8D;AAC1E,cAAQ,IAAI,oCAAoC;AAChD,cAAQ,IAAI,yCAAyC;AACrD,cAAQ,IAAI,qDAAqD;AACjE,cAAQ,IAAI,sDAAsD;AAClE,cAAQ,IAAI,uBAAuB;AACnC,cAAQ,IAAI,8EAA8E;AAC1F,cAAQ,IAAI,iCAAiC;AAC7C,cAAQ,IAAI,sGAAsG;AAClH,cAAQ,IAAI,8CAA8C;AAC1D,cAAQ,IAAI,sCAAsC;AAClD,cAAQ,IAAI,8CAA8C;AAC1D,cAAQ,IAAI,wDAAwD;AACpE,cAAQ,IAAI,8EAA8E;AAC1F,cAAQ,IAAI,oEAAoE;AAGhF,YAAM,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,IAAI,CAAC,MAAMA,SAAQ,EAAE,QAAQ,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC;AAE/D,iBAAW,KAAK,aAAa;AAC3B,YAAI,WAAW,CAAC,GAAG;AACjB,kBAAQ,IAAI;AAAA,yBAA4B,CAAC,EAAE;AAC3C,oBAAU;AACV;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,SAAS;AACZ,gBAAQ,IAAI,wCAAwC;AACpD,gBAAQ,IAAI,+CAA+C;AAC3D,gBAAQ,IAAI,6BAA6B;AACzC,gBAAQ,IAAI,uDAAuD;AACnE,gBAAQ,IAAI,0EAA0E;AACtF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,UAAUA,SAAQ,QAAQ,QAAQ,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,CAAC;AACvE,QAAI,CAAC,WAAW,OAAO,GAAG;AACxB,cAAQ,MAAM;AAAA,yBAA4B,OAAO,EAAE;AACnD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AACzD,UAAI,QAAQ,SAAS,mBAAmB;AACtC,gBAAQ,MAAM;AAAA,0CAA6C,QAAQ,IAAI,GAAG;AAC1E;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,iCAAoC;AAChD,cAAQ,IAAI,YAAY,QAAQ,YAAY,EAAE;AAC9C,cAAQ,IAAI,cAAc,QAAQ,UAAU,EAAE;AAAA,IAChD,QAAQ;AACN,cAAQ,MAAM;AAAA,4BAA+B,OAAO,EAAE;AACtD;AAAA,IACF;AAGA,YAAQ,IAAI;AAAA,0BAA6B;AACzC,QAAI;AACF,YAAMC,QAAO,MAAM,YAAY,EAAE,oBAAoB,QAAQ,CAAC;AAC9D,YAAMA,MAAK,eAAe;AAC1B,YAAM,EAAE,gBAAAC,gBAAe,IAAI,MAAM,OAAO,iBAAiB;AACzD,YAAMA,gBAAe,uBAAuB,OAAO;AACnD,cAAQ,IAAI,0CAA0C;AAAA,IACxD,SAAS,KAAK;AACZ,cAAQ,MAAM,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AACjG,cAAQ,IAAI;AAAA,iDAAoD,OAAO,EAAE;AACzE;AAAA,IACF;AAGA,YAAQ,IAAI;AAAA,2BAA8B;AAC1C,YAAQ,IAAI,mBAAmB;AAAA,EACjC,CAAC;AAEH,OACG,QAAQ,UAAU,EAClB,YAAY,0BAA0B,EACtC,OAAO,YAAY;AAClB,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,iBAAiB;AACvD,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,WAAW,MAAM,aAAa;AACpC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,OAAO,SAAS,IAAI,CAAC,UAAU;AAAA,MACnC;AAAA,MACA,QAAQ,SAAS,OAAO;AAAA,IAC1B,EAAE;AACF,YAAQ,IAAI,aAAa,MAAM,MAAM,CAAC;AAAA,EACxC,CAAC;AACL;","names":["resolve","auth","setConfigValue"]}
|
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-XBH2V2XK.js";
|
|
7
7
|
import {
|
|
8
8
|
checkForUpdate,
|
|
9
9
|
formatUpdateNotification
|
|
@@ -46,7 +46,7 @@ if (!_isJsonMode && !_isQuiet && !_isSetupCommand && !existsSync(getUserConfigPa
|
|
|
46
46
|
}
|
|
47
47
|
await setupNetworking();
|
|
48
48
|
initAudit(getConfigDir());
|
|
49
|
-
var currentVersion = "0.9.
|
|
49
|
+
var currentVersion = "0.9.48";
|
|
50
50
|
var isUpdateCommand = process.argv[2] === "update";
|
|
51
51
|
var updateCheckPromise = isUpdateCommand ? Promise.resolve(null) : checkForUpdate(currentVersion);
|
|
52
52
|
if (process.argv.includes("--ci")) {
|
|
@@ -8,12 +8,12 @@ import { fetchChangelog, formatChangelogEntry } from "@gpc-cli/core";
|
|
|
8
8
|
import { formatOutput } from "@gpc-cli/core";
|
|
9
9
|
import { loadConfig } from "@gpc-cli/config";
|
|
10
10
|
function registerChangelogCommand(program) {
|
|
11
|
-
program.command("changelog").description("Show release history").option("-n, --limit <count>", "Number of releases to show", parseInt, 5).option("--
|
|
11
|
+
program.command("changelog").description("Show release history").option("-n, --limit <count>", "Number of releases to show", parseInt, 5).option("--tag <tag>", "Show a specific release (e.g., v0.9.43)").option("--all", "Show all releases").action(async (opts) => {
|
|
12
12
|
const config = await loadConfig();
|
|
13
13
|
const format = getOutputFormat(program, config);
|
|
14
14
|
const entries = await fetchChangelog({
|
|
15
15
|
limit: opts.all ? 100 : opts.limit,
|
|
16
|
-
version: opts.
|
|
16
|
+
version: opts.tag
|
|
17
17
|
});
|
|
18
18
|
if (entries.length === 0) {
|
|
19
19
|
console.log("No releases found.");
|
|
@@ -23,7 +23,7 @@ function registerChangelogCommand(program) {
|
|
|
23
23
|
console.log(formatOutput(entries, "json"));
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
if (opts.
|
|
26
|
+
if (opts.tag || entries.length === 1) {
|
|
27
27
|
console.log(formatChangelogEntry(entries[0]));
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
@@ -38,11 +38,11 @@ function registerChangelogCommand(program) {
|
|
|
38
38
|
}
|
|
39
39
|
console.log("");
|
|
40
40
|
console.log(
|
|
41
|
-
`Showing ${entries.length} releases. Use --
|
|
41
|
+
`Showing ${entries.length} releases. Use --tag <tag> for details, or --all for full history.`
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
export {
|
|
46
46
|
registerChangelogCommand
|
|
47
47
|
};
|
|
48
|
-
//# sourceMappingURL=changelog-
|
|
48
|
+
//# sourceMappingURL=changelog-QLDFG5TV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/changelog.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { fetchChangelog, formatChangelogEntry } from \"@gpc-cli/core\";\nimport { formatOutput } from \"@gpc-cli/core\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { getOutputFormat } from \"../format.js\";\n\nexport function registerChangelogCommand(program: Command): void {\n program\n .command(\"changelog\")\n .description(\"Show release history\")\n .option(\"-n, --limit <count>\", \"Number of releases to show\", parseInt, 5)\n .option(\"--tag <tag>\", \"Show a specific release (e.g., v0.9.43)\")\n .option(\"--all\", \"Show all releases\")\n .action(async (opts: { limit: number; tag?: string; all?: boolean }) => {\n const config = await loadConfig();\n const format = getOutputFormat(program, config);\n\n const entries = await fetchChangelog({\n limit: opts.all ? 100 : opts.limit,\n version: opts.tag,\n });\n\n if (entries.length === 0) {\n console.log(\"No releases found.\");\n return;\n }\n\n if (format === \"json\") {\n console.log(formatOutput(entries, \"json\"));\n return;\n }\n\n // Single version — show full details\n if (opts.tag || entries.length === 1) {\n console.log(formatChangelogEntry(entries[0]!));\n return;\n }\n\n // Multiple versions — table summary\n const header = \"VERSION DATE TITLE\";\n const separator = \"─\".repeat(header.length);\n console.log(header);\n console.log(separator);\n for (const entry of entries) {\n const version = entry.version.padEnd(12);\n const date = entry.date.padEnd(12);\n console.log(`${version} ${date} ${entry.title}`);\n }\n console.log(\"\");\n console.log(\n `Showing ${entries.length} releases. Use --tag <tag> for details, or --all for full history.`,\n );\n });\n}\n"],"mappings":";;;;;;AACA,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAGpB,SAAS,yBAAyB,SAAwB;AAC/D,UACG,QAAQ,WAAW,EACnB,YAAY,sBAAsB,EAClC,OAAO,uBAAuB,8BAA8B,UAAU,CAAC,EACvE,OAAO,eAAe,yCAAyC,EAC/D,OAAO,SAAS,mBAAmB,EACnC,OAAO,OAAO,SAAyD;AACtE,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,UAAM,UAAU,MAAM,eAAe;AAAA,MACnC,OAAO,KAAK,MAAM,MAAM,KAAK;AAAA,MAC7B,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,oBAAoB;AAChC;AAAA,IACF;AAEA,QAAI,WAAW,QAAQ;AACrB,cAAQ,IAAI,aAAa,SAAS,MAAM,CAAC;AACzC;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,QAAQ,WAAW,GAAG;AACpC,cAAQ,IAAI,qBAAqB,QAAQ,CAAC,CAAE,CAAC;AAC7C;AAAA,IACF;AAGA,UAAM,SAAS;AACf,UAAM,YAAY,SAAI,OAAO,OAAO,MAAM;AAC1C,YAAQ,IAAI,MAAM;AAClB,YAAQ,IAAI,SAAS;AACrB,eAAW,SAAS,SAAS;AAC3B,YAAM,UAAU,MAAM,QAAQ,OAAO,EAAE;AACvC,YAAM,OAAO,MAAM,KAAK,OAAO,EAAE;AACjC,cAAQ,IAAI,GAAG,OAAO,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE;AAAA,IACjD;AACA,YAAQ,IAAI,EAAE;AACd,YAAQ;AAAA,MACN,WAAW,QAAQ,MAAM;AAAA,IAC3B;AAAA,EACF,CAAC;AACL;","names":[]}
|
|
@@ -67,25 +67,25 @@ function registerPluginCommands(program, manager) {
|
|
|
67
67
|
import { Command } from "commander";
|
|
68
68
|
async function createProgram(pluginManager) {
|
|
69
69
|
const program = new Command();
|
|
70
|
-
program.name("gpc").description("GPC \u2014 Google Play Console CLI").version("0.9.
|
|
70
|
+
program.name("gpc").description("GPC \u2014 Google Play Console CLI").version("0.9.48", "-V, --version").option("-o, --output <format>", "Output format: table, json, yaml, markdown, junit").option("-v, --verbose", "Enable debug logging").option("-q, --quiet", "Suppress non-essential output").option("-a, --app <package>", "App package name").option("-p, --profile <name>", "Auth profile name").option("--no-color", "Disable colored output").option("--no-interactive", "Disable interactive prompts").option("-y, --yes", "Skip confirmation prompts").option("--dry-run", "Preview changes without executing").option("--notify [target]", "Send webhook notification on completion (slack, discord, custom)").option("--ci", "Force CI mode (JSON output, no prompts, strict exit codes)").option("-j, --json", "Shorthand for --output json").option("--apps <csv>", "Comma-separated package names for multi-app operations").showSuggestionAfterError(false);
|
|
71
71
|
const commandLoaders = {
|
|
72
72
|
auth: async () => {
|
|
73
|
-
(await import("./auth-
|
|
73
|
+
(await import("./auth-BA4FE2PO.js")).registerAuthCommands(program);
|
|
74
74
|
},
|
|
75
75
|
config: async () => {
|
|
76
|
-
(await import("./config-
|
|
76
|
+
(await import("./config-NQQF4522.js")).registerConfigCommands(program);
|
|
77
77
|
},
|
|
78
78
|
doctor: async () => {
|
|
79
|
-
(await import("./doctor-
|
|
79
|
+
(await import("./doctor-KGYUWHL5.js")).registerDoctorCommand(program);
|
|
80
80
|
},
|
|
81
81
|
update: async () => {
|
|
82
|
-
(await import("./update-
|
|
82
|
+
(await import("./update-QUQ7N7QJ.js")).registerUpdateCommand(program);
|
|
83
83
|
},
|
|
84
84
|
docs: async () => {
|
|
85
85
|
(await import("./docs-4D2SJ4LY.js")).registerDocsCommand(program);
|
|
86
86
|
},
|
|
87
87
|
changelog: async () => {
|
|
88
|
-
(await import("./changelog-
|
|
88
|
+
(await import("./changelog-QLDFG5TV.js")).registerChangelogCommand(program);
|
|
89
89
|
},
|
|
90
90
|
completion: async () => {
|
|
91
91
|
(await import("./completion-BCHRJSAT.js")).registerCompletionCommand(program);
|
|
@@ -94,10 +94,10 @@ async function createProgram(pluginManager) {
|
|
|
94
94
|
(await import("./apps-FKD3ZG5X.js")).registerAppsCommands(program);
|
|
95
95
|
},
|
|
96
96
|
releases: async () => {
|
|
97
|
-
(await import("./releases-
|
|
97
|
+
(await import("./releases-OUJ65774.js")).registerReleasesCommands(program);
|
|
98
98
|
},
|
|
99
99
|
tracks: async () => {
|
|
100
|
-
(await import("./tracks-
|
|
100
|
+
(await import("./tracks-DO7C5OSE.js")).registerTracksCommands(program);
|
|
101
101
|
},
|
|
102
102
|
status: async () => {
|
|
103
103
|
(await import("./status-6LH5W4FU.js")).registerStatusCommand(program);
|
|
@@ -106,19 +106,19 @@ async function createProgram(pluginManager) {
|
|
|
106
106
|
(await import("./listings-7SGQ4SRX.js")).registerListingsCommands(program);
|
|
107
107
|
},
|
|
108
108
|
reviews: async () => {
|
|
109
|
-
(await import("./reviews-
|
|
109
|
+
(await import("./reviews-YCBBM656.js")).registerReviewsCommands(program);
|
|
110
110
|
},
|
|
111
111
|
vitals: async () => {
|
|
112
|
-
(await import("./vitals-
|
|
112
|
+
(await import("./vitals-PJEQUUAK.js")).registerVitalsCommands(program);
|
|
113
113
|
},
|
|
114
114
|
subscriptions: async () => {
|
|
115
|
-
(await import("./subscriptions-
|
|
115
|
+
(await import("./subscriptions-LURZFPGJ.js")).registerSubscriptionsCommands(program);
|
|
116
116
|
},
|
|
117
117
|
iap: async () => {
|
|
118
118
|
(await import("./iap-OUI5YYN4.js")).registerIapCommands(program);
|
|
119
119
|
},
|
|
120
120
|
purchases: async () => {
|
|
121
|
-
(await import("./purchases-
|
|
121
|
+
(await import("./purchases-UBFLNYZC.js")).registerPurchasesCommands(program);
|
|
122
122
|
},
|
|
123
123
|
pricing: async () => {
|
|
124
124
|
(await import("./pricing-JJZFICFL.js")).registerPricingCommands(program);
|
|
@@ -168,25 +168,25 @@ async function createProgram(pluginManager) {
|
|
|
168
168
|
(await import("./bundle-F7MUVC5J.js")).registerBundleCommands(program);
|
|
169
169
|
},
|
|
170
170
|
audit: async () => {
|
|
171
|
-
(await import("./audit-
|
|
171
|
+
(await import("./audit-VTWXTXC6.js")).registerAuditCommands(program);
|
|
172
172
|
},
|
|
173
173
|
migrate: async () => {
|
|
174
174
|
(await import("./migrate-ZQCJGQQS.js")).registerMigrateCommands(program);
|
|
175
175
|
},
|
|
176
176
|
anomalies: async () => {
|
|
177
|
-
(await import("./anomalies-
|
|
177
|
+
(await import("./anomalies-V3AFS4LD.js")).registerAnomaliesCommands(program);
|
|
178
178
|
},
|
|
179
179
|
"install-skills": async () => {
|
|
180
|
-
(await import("./install-skills-
|
|
180
|
+
(await import("./install-skills-JKPYZHYS.js")).registerInstallSkillsCommand(program);
|
|
181
181
|
},
|
|
182
182
|
version: async () => {
|
|
183
|
-
(await import("./version-
|
|
183
|
+
(await import("./version-WGE5Q7QH.js")).registerVersionCommand(program);
|
|
184
184
|
},
|
|
185
185
|
cache: async () => {
|
|
186
186
|
(await import("./cache-XKPLZYEB.js")).registerCacheCommand(program);
|
|
187
187
|
},
|
|
188
188
|
feedback: async () => {
|
|
189
|
-
(await import("./feedback-
|
|
189
|
+
(await import("./feedback-RMLYHTAH.js")).registerFeedbackCommand(program);
|
|
190
190
|
},
|
|
191
191
|
quickstart: async () => {
|
|
192
192
|
(await import("./quickstart-Z5Y3FYJU.js")).registerQuickstartCommand(program);
|
|
@@ -217,6 +217,9 @@ async function createProgram(pluginManager) {
|
|
|
217
217
|
},
|
|
218
218
|
plugins: async () => {
|
|
219
219
|
registerPluginsCommand(program, pluginManager);
|
|
220
|
+
},
|
|
221
|
+
rtdn: async () => {
|
|
222
|
+
(await import("./rtdn-LID2B7XZ.js")).registerRtdnCommands(program);
|
|
220
223
|
}
|
|
221
224
|
};
|
|
222
225
|
function levenshtein(a, b) {
|
|
@@ -451,4 +454,4 @@ export {
|
|
|
451
454
|
createProgram,
|
|
452
455
|
handleCliError
|
|
453
456
|
};
|
|
454
|
-
//# sourceMappingURL=chunk-
|
|
457
|
+
//# sourceMappingURL=chunk-XBH2V2XK.js.map
|