@gpc-cli/cli 0.9.45 → 0.9.47
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-V3AFS4LD.js +66 -0
- package/dist/anomalies-V3AFS4LD.js.map +1 -0
- package/dist/{apps-J2446UDA.js → apps-FKD3ZG5X.js} +31 -35
- package/dist/apps-FKD3ZG5X.js.map +1 -0
- package/dist/{audit-N2CRHWUN.js → audit-JASSHRWN.js} +47 -62
- package/dist/audit-JASSHRWN.js.map +1 -0
- package/dist/{auth-XGSTT5G5.js → auth-OTA3SV3J.js} +145 -103
- package/dist/auth-OTA3SV3J.js.map +1 -0
- package/dist/bin.js +6 -4
- package/dist/bin.js.map +1 -1
- package/dist/bundle-F7MUVC5J.js +204 -0
- package/dist/bundle-F7MUVC5J.js.map +1 -0
- package/dist/{cache-SLNFRTI2.js → cache-XKPLZYEB.js} +4 -5
- package/dist/cache-XKPLZYEB.js.map +1 -0
- package/dist/changelog-QLDFG5TV.js +48 -0
- package/dist/changelog-QLDFG5TV.js.map +1 -0
- package/dist/{chunk-4O4D5SGL.js → chunk-3SJ6OXCZ.js} +4 -5
- package/dist/chunk-3SJ6OXCZ.js.map +1 -0
- package/dist/{chunk-U6ZTQ34I.js → chunk-BCBXQC7J.js} +45 -11
- package/dist/chunk-BCBXQC7J.js.map +1 -0
- package/dist/{chunk-AA577WVQ.js → chunk-NQH4G7BI.js} +9 -3
- package/dist/chunk-NQH4G7BI.js.map +1 -0
- package/dist/chunk-SLNJEAMK.js +23 -0
- package/dist/chunk-SLNJEAMK.js.map +1 -0
- package/dist/{chunk-SEVX56VN.js → chunk-WWVURXVO.js} +56 -49
- package/dist/chunk-WWVURXVO.js.map +1 -0
- package/dist/{chunk-NV75I5VP.js → chunk-YFUBD2XB.js} +10 -8
- package/dist/chunk-YFUBD2XB.js.map +1 -0
- package/dist/{config-BUXPDN7N.js → config-NY3TZGVS.js} +8 -5
- package/dist/config-NY3TZGVS.js.map +1 -0
- package/dist/{data-safety-Q7FTCEWU.js → data-safety-AFMD6MYI.js} +12 -27
- package/dist/data-safety-AFMD6MYI.js.map +1 -0
- package/dist/{device-tiers-MIOQEXYY.js → device-tiers-AQAMUQXI.js} +23 -38
- package/dist/device-tiers-AQAMUQXI.js.map +1 -0
- package/dist/diff-6EO4ID6W.js +91 -0
- package/dist/diff-6EO4ID6W.js.map +1 -0
- package/dist/{docs-7DUXIKA3.js → docs-4D2SJ4LY.js} +4 -3
- package/dist/docs-4D2SJ4LY.js.map +1 -0
- package/dist/doctor-QCCWG6Y3.js +708 -0
- package/dist/doctor-QCCWG6Y3.js.map +1 -0
- package/dist/{enterprise-7THXNBTC.js → enterprise-7PWXMSUN.js} +11 -21
- package/dist/enterprise-7PWXMSUN.js.map +1 -0
- package/dist/{external-transactions-2GWIMUVM.js → external-transactions-LCZALS3V.js} +12 -28
- package/dist/external-transactions-LCZALS3V.js.map +1 -0
- package/dist/{feedback-DPTO6DUT.js → feedback-CET2X67K.js} +4 -4
- package/dist/{games-BT777WUO.js → games-ZSNGEI7A.js} +17 -32
- package/dist/games-ZSNGEI7A.js.map +1 -0
- package/dist/{generated-apks-RJWTIX7L.js → generated-apks-RX2IUWSF.js} +30 -38
- package/dist/generated-apks-RX2IUWSF.js.map +1 -0
- package/dist/{grants-TKQJ3IER.js → grants-EBPECI26.js} +22 -40
- package/dist/grants-EBPECI26.js.map +1 -0
- package/dist/{iap-ICAEQLK5.js → iap-OUI5YYN4.js} +30 -51
- package/dist/iap-OUI5YYN4.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{init-JZ2THPMS.js → init-WSTQTJOD.js} +5 -4
- package/dist/init-WSTQTJOD.js.map +1 -0
- package/dist/{install-skills-OV4HVANW.js → install-skills-6QDUXI5F.js} +5 -6
- package/dist/{install-skills-OV4HVANW.js.map → install-skills-6QDUXI5F.js.map} +1 -1
- package/dist/{internal-sharing-3U2XFHA4.js → internal-sharing-ONNIWIAT.js} +3 -4
- package/dist/{internal-sharing-3U2XFHA4.js.map → internal-sharing-ONNIWIAT.js.map} +1 -1
- package/dist/{listings-77HZW4S5.js → listings-7SGQ4SRX.js} +118 -157
- package/dist/listings-7SGQ4SRX.js.map +1 -0
- package/dist/migrate-ZQCJGQQS.js +138 -0
- package/dist/migrate-ZQCJGQQS.js.map +1 -0
- package/dist/{one-time-products-LHZAXQES.js → one-time-products-MGZTU7OM.js} +65 -120
- package/dist/one-time-products-MGZTU7OM.js.map +1 -0
- package/dist/{preflight-H3HEBYQW.js → preflight-N7ZRG2JI.js} +58 -55
- package/dist/preflight-N7ZRG2JI.js.map +1 -0
- package/dist/{pricing-XQSDTTK5.js → pricing-JJZFICFL.js} +8 -8
- package/dist/{pricing-XQSDTTK5.js.map → pricing-JJZFICFL.js.map} +1 -1
- package/dist/{prompt-BSV22CQZ.js → prompt-GXC2JSLA.js} +2 -2
- package/dist/{publish-Q5ZKEKZ5.js → publish-JPTI4EBT.js} +34 -30
- package/dist/publish-JPTI4EBT.js.map +1 -0
- package/dist/{purchase-options-CKRN4VIW.js → purchase-options-KFWW4JW2.js} +16 -11
- package/dist/purchase-options-KFWW4JW2.js.map +1 -0
- package/dist/{purchases-43AKV6HG.js → purchases-Z3QBM3UO.js} +121 -194
- package/dist/purchases-Z3QBM3UO.js.map +1 -0
- package/dist/{quickstart-4HB62YEL.js → quickstart-Z5Y3FYJU.js} +5 -3
- package/dist/quickstart-Z5Y3FYJU.js.map +1 -0
- package/dist/{quota-UHIQQYOY.js → quota-MZRWYJGR.js} +5 -15
- package/dist/quota-MZRWYJGR.js.map +1 -0
- package/dist/{recovery-5EV2R476.js → recovery-YE3Z7NIN.js} +32 -61
- package/dist/recovery-YE3Z7NIN.js.map +1 -0
- package/dist/{releases-C2WC2K4E.js → releases-276W3BR7.js} +188 -187
- package/dist/releases-276W3BR7.js.map +1 -0
- package/dist/{reports-2YX3RDOS.js → reports-CIB2T3XT.js} +19 -21
- package/dist/reports-CIB2T3XT.js.map +1 -0
- package/dist/reviews-YCBBM656.js +199 -0
- 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/{status-WHGLODGV.js → status-6LH5W4FU.js} +105 -83
- package/dist/status-6LH5W4FU.js.map +1 -0
- package/dist/{subscriptions-CI3JH3VQ.js → subscriptions-DZP3Y7O7.js} +142 -232
- package/dist/subscriptions-DZP3Y7O7.js.map +1 -0
- package/dist/{testers-NZOFA3EF.js → testers-LSMBXCA2.js} +24 -44
- package/dist/testers-LSMBXCA2.js.map +1 -0
- package/dist/tracks-YHMO2A6B.js +98 -0
- package/dist/tracks-YHMO2A6B.js.map +1 -0
- package/dist/{train-CJJVLY4B.js → train-MDD2EBHS.js} +35 -55
- package/dist/train-MDD2EBHS.js.map +1 -0
- package/dist/{update-NAK6CMUX.js → update-XAO5EZHC.js} +30 -15
- package/dist/update-XAO5EZHC.js.map +1 -0
- package/dist/{users-2YTC4Q36.js → users-UKG7VIQH.js} +45 -67
- package/dist/users-UKG7VIQH.js.map +1 -0
- package/dist/{validate-UOVTM6L3.js → validate-QIYSA3N7.js} +8 -10
- package/dist/validate-QIYSA3N7.js.map +1 -0
- package/dist/{version-N64UBW7A.js → version-R3P4NHCF.js} +4 -4
- package/dist/{vitals-A4CS4MSS.js → vitals-PJEQUUAK.js} +174 -165
- package/dist/vitals-PJEQUUAK.js.map +1 -0
- package/package.json +6 -6
- package/dist/anomalies-NU2IN2GJ.js +0 -54
- package/dist/anomalies-NU2IN2GJ.js.map +0 -1
- package/dist/apps-J2446UDA.js.map +0 -1
- package/dist/audit-N2CRHWUN.js.map +0 -1
- package/dist/auth-XGSTT5G5.js.map +0 -1
- package/dist/bundle-F43TD2BQ.js +0 -218
- package/dist/bundle-F43TD2BQ.js.map +0 -1
- package/dist/cache-SLNFRTI2.js.map +0 -1
- package/dist/changelog-ZYD6W5IV.js +0 -53
- package/dist/changelog-ZYD6W5IV.js.map +0 -1
- package/dist/chunk-4O4D5SGL.js.map +0 -1
- package/dist/chunk-AA577WVQ.js.map +0 -1
- package/dist/chunk-FWKYRLKY.js +0 -19
- package/dist/chunk-FWKYRLKY.js.map +0 -1
- package/dist/chunk-NV75I5VP.js.map +0 -1
- package/dist/chunk-SEVX56VN.js.map +0 -1
- package/dist/chunk-U6ZTQ34I.js.map +0 -1
- package/dist/config-BUXPDN7N.js.map +0 -1
- package/dist/data-safety-Q7FTCEWU.js.map +0 -1
- package/dist/device-tiers-MIOQEXYY.js.map +0 -1
- package/dist/diff-V77SMKAQ.js +0 -96
- package/dist/diff-V77SMKAQ.js.map +0 -1
- package/dist/docs-7DUXIKA3.js.map +0 -1
- package/dist/doctor-3Z4ARPM2.js +0 -372
- package/dist/doctor-3Z4ARPM2.js.map +0 -1
- package/dist/enterprise-7THXNBTC.js.map +0 -1
- package/dist/external-transactions-2GWIMUVM.js.map +0 -1
- package/dist/games-BT777WUO.js.map +0 -1
- package/dist/generated-apks-RJWTIX7L.js.map +0 -1
- package/dist/grants-TKQJ3IER.js.map +0 -1
- package/dist/iap-ICAEQLK5.js.map +0 -1
- package/dist/init-JZ2THPMS.js.map +0 -1
- package/dist/listings-77HZW4S5.js.map +0 -1
- package/dist/migrate-SQT6RD6T.js +0 -143
- package/dist/migrate-SQT6RD6T.js.map +0 -1
- package/dist/one-time-products-LHZAXQES.js.map +0 -1
- package/dist/preflight-H3HEBYQW.js.map +0 -1
- package/dist/publish-Q5ZKEKZ5.js.map +0 -1
- package/dist/purchase-options-CKRN4VIW.js.map +0 -1
- package/dist/purchases-43AKV6HG.js.map +0 -1
- package/dist/quickstart-4HB62YEL.js.map +0 -1
- package/dist/quota-UHIQQYOY.js.map +0 -1
- package/dist/recovery-5EV2R476.js.map +0 -1
- package/dist/releases-C2WC2K4E.js.map +0 -1
- package/dist/reports-2YX3RDOS.js.map +0 -1
- package/dist/reviews-2CWOI5CV.js +0 -213
- package/dist/reviews-2CWOI5CV.js.map +0 -1
- package/dist/status-WHGLODGV.js.map +0 -1
- package/dist/subscriptions-CI3JH3VQ.js.map +0 -1
- package/dist/testers-NZOFA3EF.js.map +0 -1
- package/dist/tracks-NERFFEDT.js +0 -107
- package/dist/tracks-NERFFEDT.js.map +0 -1
- package/dist/train-CJJVLY4B.js.map +0 -1
- package/dist/update-NAK6CMUX.js.map +0 -1
- package/dist/users-2YTC4Q36.js.map +0 -1
- package/dist/validate-UOVTM6L3.js.map +0 -1
- package/dist/vitals-A4CS4MSS.js.map +0 -1
- /package/dist/{feedback-DPTO6DUT.js.map → feedback-CET2X67K.js.map} +0 -0
- /package/dist/{prompt-BSV22CQZ.js.map → prompt-GXC2JSLA.js.map} +0 -0
- /package/dist/{version-N64UBW7A.js.map → version-R3P4NHCF.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
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolvePackageName
|
|
4
|
+
} from "./chunk-NQH4G7BI.js";
|
|
5
|
+
import {
|
|
6
|
+
yellow
|
|
7
|
+
} from "./chunk-FAN4ZITI.js";
|
|
8
|
+
import {
|
|
9
|
+
getOutputFormat
|
|
10
|
+
} from "./chunk-ELXAK7GI.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/anomalies.ts
|
|
13
|
+
import { loadConfig } from "@gpc-cli/config";
|
|
14
|
+
import { resolveAuth } from "@gpc-cli/auth";
|
|
15
|
+
import { createReportingClient, PlayApiError } from "@gpc-cli/api";
|
|
16
|
+
import { getVitalsAnomalies, formatOutput } from "@gpc-cli/core";
|
|
17
|
+
async function getReportingClient(config) {
|
|
18
|
+
const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
|
|
19
|
+
return createReportingClient({ auth });
|
|
20
|
+
}
|
|
21
|
+
function registerAnomaliesCommands(program) {
|
|
22
|
+
const anomalies = program.command("anomalies").description("Detect and list vitals anomalies");
|
|
23
|
+
anomalies.command("list").description("List detected vitals anomalies").action(async () => {
|
|
24
|
+
const config = await loadConfig();
|
|
25
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
26
|
+
const reporting = await getReportingClient(config);
|
|
27
|
+
const format = getOutputFormat(program, config);
|
|
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
|
+
}
|
|
43
|
+
const items = result["anomalies"];
|
|
44
|
+
if (format !== "json") {
|
|
45
|
+
if (!items || items.length === 0) {
|
|
46
|
+
console.log("No anomalies detected.");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const rows = items.map((item) => {
|
|
50
|
+
const a = item;
|
|
51
|
+
return {
|
|
52
|
+
name: String(a["name"] ?? "-"),
|
|
53
|
+
metricSet: String(a["metricSet"] ?? "-"),
|
|
54
|
+
aggregationPeriod: String(a["aggregationPeriod"] ?? "-")
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
console.log(formatOutput(rows, format));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(formatOutput(result, format));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
registerAnomaliesCommands
|
|
65
|
+
};
|
|
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":[]}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { loadConfig } from "@gpc-cli/config";
|
|
12
12
|
import { resolveAuth } from "@gpc-cli/auth";
|
|
13
13
|
import { createApiClient } from "@gpc-cli/api";
|
|
14
|
-
import { getAppInfo, updateAppDetails } from "@gpc-cli/core";
|
|
14
|
+
import { getAppInfo, updateAppDetails, GpcError } from "@gpc-cli/core";
|
|
15
15
|
import { formatOutput } from "@gpc-cli/core";
|
|
16
16
|
function registerAppsCommands(program) {
|
|
17
17
|
const apps = program.command("apps").description("Manage applications");
|
|
@@ -19,31 +19,31 @@ function registerAppsCommands(program) {
|
|
|
19
19
|
const config = await loadConfig();
|
|
20
20
|
const packageName = packageArg || config.app;
|
|
21
21
|
if (!packageName) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const auth = await resolveAuth({
|
|
29
|
-
serviceAccountPath: config.auth?.serviceAccount
|
|
30
|
-
});
|
|
31
|
-
const client = createApiClient({ auth });
|
|
32
|
-
const info = await getAppInfo(client, packageName);
|
|
33
|
-
const format = getOutputFormat(program, config);
|
|
34
|
-
console.log(formatOutput(info, format));
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
37
|
-
process.exit(4);
|
|
22
|
+
throw new GpcError(
|
|
23
|
+
"No package name provided. Usage: gpc apps info <package>",
|
|
24
|
+
"MISSING_PACKAGE",
|
|
25
|
+
2,
|
|
26
|
+
"gpc config set app com.example.app"
|
|
27
|
+
);
|
|
38
28
|
}
|
|
29
|
+
const auth = await resolveAuth({
|
|
30
|
+
serviceAccountPath: config.auth?.serviceAccount
|
|
31
|
+
});
|
|
32
|
+
const client = createApiClient({ auth });
|
|
33
|
+
const info = await getAppInfo(client, packageName);
|
|
34
|
+
const format = getOutputFormat(program, config);
|
|
35
|
+
console.log(formatOutput(info, format));
|
|
39
36
|
});
|
|
40
37
|
apps.command("update").description("Update app details").option("--email <email>", "Contact email").option("--phone <phone>", "Contact phone").option("--website <url>", "Contact website").option("--default-lang <lang>", "Default language").action(async (options) => {
|
|
41
38
|
const config = await loadConfig();
|
|
42
39
|
const packageName = options["app"] || program.opts()["app"] || config.app;
|
|
43
40
|
if (!packageName) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
throw new GpcError(
|
|
42
|
+
"No package name provided. Usage: gpc apps update --email user@example.com",
|
|
43
|
+
"MISSING_PACKAGE",
|
|
44
|
+
2,
|
|
45
|
+
"gpc config set app com.example.app"
|
|
46
|
+
);
|
|
47
47
|
}
|
|
48
48
|
const data = {};
|
|
49
49
|
if (options["email"]) data["contactEmail"] = options["email"];
|
|
@@ -51,10 +51,11 @@ function registerAppsCommands(program) {
|
|
|
51
51
|
if (options["website"]) data["contactWebsite"] = options["website"];
|
|
52
52
|
if (options["defaultLang"]) data["defaultLanguage"] = options["defaultLang"];
|
|
53
53
|
if (Object.keys(data).length === 0) {
|
|
54
|
-
|
|
55
|
-
"
|
|
54
|
+
throw new GpcError(
|
|
55
|
+
"Provide at least one field to update (--email, --phone, --website, --default-lang).",
|
|
56
|
+
"MISSING_OPTION",
|
|
57
|
+
2
|
|
56
58
|
);
|
|
57
|
-
process.exit(2);
|
|
58
59
|
}
|
|
59
60
|
const format = getOutputFormat(program, config);
|
|
60
61
|
if (isDryRun(program)) {
|
|
@@ -70,17 +71,12 @@ function registerAppsCommands(program) {
|
|
|
70
71
|
);
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
console.log(formatOutput(result, format));
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
-
process.exit(4);
|
|
83
|
-
}
|
|
74
|
+
const auth = await resolveAuth({
|
|
75
|
+
serviceAccountPath: config.auth?.serviceAccount
|
|
76
|
+
});
|
|
77
|
+
const client = createApiClient({ auth });
|
|
78
|
+
const result = await updateAppDetails(client, packageName, data);
|
|
79
|
+
console.log(formatOutput(result, format));
|
|
84
80
|
});
|
|
85
81
|
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
82
|
const config = await loadConfig();
|
|
@@ -102,4 +98,4 @@ function registerAppsCommands(program) {
|
|
|
102
98
|
export {
|
|
103
99
|
registerAppsCommands
|
|
104
100
|
};
|
|
105
|
-
//# sourceMappingURL=apps-
|
|
101
|
+
//# sourceMappingURL=apps-FKD3ZG5X.js.map
|
|
@@ -0,0 +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, GpcError } 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 throw new GpcError(\n \"No package name provided. Usage: gpc apps info <package>\",\n \"MISSING_PACKAGE\",\n 2,\n \"gpc config set app com.example.app\",\n );\n }\n\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 });\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 throw new GpcError(\n \"No package name provided. Usage: gpc apps update --email user@example.com\",\n \"MISSING_PACKAGE\",\n 2,\n \"gpc config set app com.example.app\",\n );\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 throw new GpcError(\n \"Provide at least one field to update (--email, --phone, --website, --default-lang).\",\n \"MISSING_OPTION\",\n 2,\n );\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 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 });\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"]}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-ELXAK7GI.js";
|
|
8
8
|
import {
|
|
9
9
|
requireConfirm
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YFUBD2XB.js";
|
|
11
11
|
|
|
12
12
|
// src/commands/audit.ts
|
|
13
13
|
import { loadConfig } from "@gpc-cli/config";
|
|
@@ -55,57 +55,47 @@ function registerAuditCommands(program) {
|
|
|
55
55
|
const config = await loadConfig();
|
|
56
56
|
const format = getOutputFormat(program, config);
|
|
57
57
|
initAudit(getConfigDir());
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.log(formatOutput(events, format));
|
|
79
|
-
}
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
-
process.exit(1);
|
|
58
|
+
const events = await listAuditEvents({
|
|
59
|
+
limit: options.limit,
|
|
60
|
+
since: options.since,
|
|
61
|
+
command: options.command
|
|
62
|
+
});
|
|
63
|
+
if (events.length === 0 && format !== "json") {
|
|
64
|
+
console.log("No audit events found.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (format !== "json") {
|
|
68
|
+
const rows = events.map((e) => ({
|
|
69
|
+
timestamp: formatAuditTimestamp(e.timestamp),
|
|
70
|
+
command: e.command,
|
|
71
|
+
app: e.app || "-",
|
|
72
|
+
success: e.success !== void 0 ? String(e.success) : "-",
|
|
73
|
+
durationMs: e.durationMs ?? "-"
|
|
74
|
+
}));
|
|
75
|
+
console.log(formatOutput(rows, format));
|
|
76
|
+
} else {
|
|
77
|
+
console.log(formatOutput(events, format));
|
|
83
78
|
}
|
|
84
79
|
});
|
|
85
80
|
audit.command("search <query>").description("Search audit events by keyword").action(async (query) => {
|
|
86
81
|
const config = await loadConfig();
|
|
87
82
|
const format = getOutputFormat(program, config);
|
|
88
83
|
initAudit(getConfigDir());
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
console.log(formatOutput(events, format));
|
|
105
|
-
}
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
-
process.exit(1);
|
|
84
|
+
const events = await searchAuditEvents(query);
|
|
85
|
+
if (events.length === 0 && format !== "json") {
|
|
86
|
+
console.log(`No audit events matching "${query}".`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (format !== "json") {
|
|
90
|
+
const rows = events.map((e) => ({
|
|
91
|
+
timestamp: formatAuditTimestamp(e.timestamp),
|
|
92
|
+
command: e.command,
|
|
93
|
+
app: e.app || "-",
|
|
94
|
+
success: e.success !== void 0 ? String(e.success) : "-"
|
|
95
|
+
}));
|
|
96
|
+
console.log(formatOutput(rows, format));
|
|
97
|
+
} else {
|
|
98
|
+
console.log(formatOutput(events, format));
|
|
109
99
|
}
|
|
110
100
|
});
|
|
111
101
|
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) => {
|
|
@@ -115,25 +105,20 @@ function registerAuditCommands(program) {
|
|
|
115
105
|
if (!dryRun && !options.before) {
|
|
116
106
|
await requireConfirm("Clear all audit log entries?", program);
|
|
117
107
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
console.log(`Deleted ${result.deleted} entries. ${result.remaining} remaining.`);
|
|
129
|
-
}
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
-
process.exit(1);
|
|
108
|
+
const result = await clearAuditLog({
|
|
109
|
+
before: options.before,
|
|
110
|
+
dryRun
|
|
111
|
+
});
|
|
112
|
+
if (dryRun) {
|
|
113
|
+
console.log(
|
|
114
|
+
`[dry-run] Would delete ${result.deleted} entries, ${result.remaining} would remain.`
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
console.log(`Deleted ${result.deleted} entries. ${result.remaining} remaining.`);
|
|
133
118
|
}
|
|
134
119
|
});
|
|
135
120
|
}
|
|
136
121
|
export {
|
|
137
122
|
registerAuditCommands
|
|
138
123
|
};
|
|
139
|
-
//# sourceMappingURL=audit-
|
|
124
|
+
//# sourceMappingURL=audit-JASSHRWN.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 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 });\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":[]}
|