@gpc-cli/core 0.1.1 → 0.1.3
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 +74 -0
- package/dist/index.js +65 -45
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @gpc-cli/core
|
|
2
|
+
|
|
3
|
+
Business logic and command orchestration for GPC. Contains all command implementations, validation, output formatting, and plugin management.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @gpc-cli/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import {
|
|
15
|
+
uploadRelease,
|
|
16
|
+
promoteRelease,
|
|
17
|
+
getVitalsOverview,
|
|
18
|
+
listReviews,
|
|
19
|
+
formatOutput,
|
|
20
|
+
} from "@gpc-cli/core";
|
|
21
|
+
|
|
22
|
+
// Upload a release
|
|
23
|
+
const result = await uploadRelease(context, {
|
|
24
|
+
file: "app.aab",
|
|
25
|
+
track: "internal",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Promote between tracks
|
|
29
|
+
await promoteRelease(context, {
|
|
30
|
+
from: "internal",
|
|
31
|
+
to: "production",
|
|
32
|
+
rollout: 0.1,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Check vitals
|
|
36
|
+
const vitals = await getVitalsOverview(context);
|
|
37
|
+
|
|
38
|
+
// Format output
|
|
39
|
+
console.log(formatOutput(vitals, "table"));
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Command Groups
|
|
43
|
+
|
|
44
|
+
| Group | Functions |
|
|
45
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| **Releases** | `uploadRelease`, `promoteRelease`, `updateRollout`, `getReleasesStatus`, `listTracks` |
|
|
47
|
+
| **Listings** | `getListings`, `updateListing`, `pullListings`, `pushListings`, `diffListings` |
|
|
48
|
+
| **Images** | `listImages`, `uploadImage`, `deleteImage` |
|
|
49
|
+
| **Reviews** | `listReviews`, `getReview`, `replyToReview`, `exportReviews` |
|
|
50
|
+
| **Vitals** | `getVitalsOverview`, `getVitalsCrashes`, `getVitalsAnr`, `getVitalsStartup`, `compareVitalsTrend`, `checkThreshold` |
|
|
51
|
+
| **Subscriptions** | `listSubscriptions`, `createSubscription`, `updateSubscription`, `deleteSubscription`, `listOffers`, `createOffer` |
|
|
52
|
+
| **IAP** | `listInAppProducts`, `createInAppProduct`, `syncInAppProducts` |
|
|
53
|
+
| **Purchases** | `getProductPurchase`, `acknowledgeProductPurchase`, `refundOrder` |
|
|
54
|
+
| **Reports** | `listReports`, `downloadReport` |
|
|
55
|
+
| **Users** | `listUsers`, `inviteUser`, `updateUser`, `removeUser` |
|
|
56
|
+
| **Testers** | `listTesters`, `addTesters`, `removeTesters`, `importTestersFromCsv` |
|
|
57
|
+
| **Publishing** | `publish` (end-to-end: upload + track + notes + commit) |
|
|
58
|
+
| **Validation** | `validateUploadFile`, `validateImage`, `validatePreSubmission` |
|
|
59
|
+
|
|
60
|
+
## Utilities
|
|
61
|
+
|
|
62
|
+
- **Output formatting** — `formatOutput()`, `detectOutputFormat()`, `redactSensitive()`
|
|
63
|
+
- **Error hierarchy** — `GpcError`, `ConfigError`, `ApiError`, `NetworkError` with exit codes
|
|
64
|
+
- **Audit logging** — `initAudit()`, `writeAuditLog()` for write operation tracking
|
|
65
|
+
- **Path safety** — `safePath()`, `safePathWithin()` for path traversal prevention
|
|
66
|
+
- **Plugin management** — `PluginManager`, `discoverPlugins()`, `scaffoldPlugin()`
|
|
67
|
+
|
|
68
|
+
## Part of the GPC Monorepo
|
|
69
|
+
|
|
70
|
+
This is the core logic layer for [GPC](https://github.com/yasserstudio/gpc). The CLI calls into core; core calls into api, auth, and config.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -138,16 +138,16 @@ ${formatYaml(value, indent + 1)}`;
|
|
|
138
138
|
function formatTable(data) {
|
|
139
139
|
const rows = toRows(data);
|
|
140
140
|
if (rows.length === 0) return "";
|
|
141
|
-
const
|
|
141
|
+
const firstRow = rows[0];
|
|
142
|
+
if (!firstRow) return "";
|
|
143
|
+
const keys = Object.keys(firstRow);
|
|
142
144
|
if (keys.length === 0) return "";
|
|
143
145
|
const widths = keys.map(
|
|
144
146
|
(key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
|
|
145
147
|
);
|
|
146
|
-
const header = keys.map((key, i) => key.padEnd(widths[i])).join(" ");
|
|
148
|
+
const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" ");
|
|
147
149
|
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
148
|
-
const body = rows.map(
|
|
149
|
-
(row) => keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i])).join(" ")
|
|
150
|
-
).join("\n");
|
|
150
|
+
const body = rows.map((row) => keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i] ?? 0)).join(" ")).join("\n");
|
|
151
151
|
return `${header}
|
|
152
152
|
${separator}
|
|
153
153
|
${body}`;
|
|
@@ -155,15 +155,17 @@ ${body}`;
|
|
|
155
155
|
function formatMarkdown(data) {
|
|
156
156
|
const rows = toRows(data);
|
|
157
157
|
if (rows.length === 0) return "";
|
|
158
|
-
const
|
|
158
|
+
const firstRow = rows[0];
|
|
159
|
+
if (!firstRow) return "";
|
|
160
|
+
const keys = Object.keys(firstRow);
|
|
159
161
|
if (keys.length === 0) return "";
|
|
160
162
|
const widths = keys.map(
|
|
161
163
|
(key) => Math.max(key.length, ...rows.map((row) => String(row[key] ?? "").length))
|
|
162
164
|
);
|
|
163
|
-
const header = `| ${keys.map((key, i) => key.padEnd(widths[i])).join(" | ")} |`;
|
|
165
|
+
const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(" | ")} |`;
|
|
164
166
|
const separator = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
|
|
165
167
|
const body = rows.map(
|
|
166
|
-
(row) => `| ${keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i])).join(" | ")} |`
|
|
168
|
+
(row) => `| ${keys.map((key, i) => String(row[key] ?? "").padEnd(widths[i] ?? 0)).join(" | ")} |`
|
|
167
169
|
).join("\n");
|
|
168
170
|
return `${header}
|
|
169
171
|
${separator}
|
|
@@ -396,7 +398,7 @@ async function validateUploadFile(filePath) {
|
|
|
396
398
|
} else {
|
|
397
399
|
errors.push(`Unsupported file extension "${ext}". Expected .aab or .apk`);
|
|
398
400
|
}
|
|
399
|
-
let sizeBytes
|
|
401
|
+
let sizeBytes;
|
|
400
402
|
try {
|
|
401
403
|
const stats = await stat(filePath);
|
|
402
404
|
sizeBytes = stats.size;
|
|
@@ -724,7 +726,7 @@ async function validateImage(filePath, imageType) {
|
|
|
724
726
|
if (!VALID_EXTENSIONS.has(ext)) {
|
|
725
727
|
errors.push(`Unsupported image format "${ext}". Use PNG or JPEG.`);
|
|
726
728
|
}
|
|
727
|
-
let sizeBytes
|
|
729
|
+
let sizeBytes;
|
|
728
730
|
try {
|
|
729
731
|
const stats = await stat2(filePath);
|
|
730
732
|
sizeBytes = stats.size;
|
|
@@ -747,7 +749,9 @@ async function validateImage(filePath, imageType) {
|
|
|
747
749
|
);
|
|
748
750
|
}
|
|
749
751
|
if (ext === ".png" && sizeBytes > 512 * 1024) {
|
|
750
|
-
warnings.push(
|
|
752
|
+
warnings.push(
|
|
753
|
+
"PNG file is over 512 KB. Consider compressing with tools like pngquant or optipng."
|
|
754
|
+
);
|
|
751
755
|
}
|
|
752
756
|
return { valid: errors.length === 0, errors, warnings };
|
|
753
757
|
}
|
|
@@ -820,7 +824,7 @@ function diffListings(local, remote) {
|
|
|
820
824
|
const localMap = new Map(local.map((l) => [l.language, l]));
|
|
821
825
|
for (const localListing of local) {
|
|
822
826
|
const remoteListing = remoteMap.get(localListing.language);
|
|
823
|
-
for (const [field
|
|
827
|
+
for (const [field] of Object.entries(FIELD_TO_FILE)) {
|
|
824
828
|
const localVal = (localListing[field] ?? "").toString();
|
|
825
829
|
const remoteVal = remoteListing ? (remoteListing[field] ?? "").toString() : "";
|
|
826
830
|
if (localVal !== remoteVal) {
|
|
@@ -1231,14 +1235,15 @@ async function replyToReview(client, packageName, reviewId, replyText) {
|
|
|
1231
1235
|
return client.reviews.reply(packageName, reviewId, replyText);
|
|
1232
1236
|
}
|
|
1233
1237
|
async function exportReviews(client, packageName, options) {
|
|
1234
|
-
const { items: allReviews } = await paginateAll(
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1238
|
+
const { items: allReviews } = await paginateAll(async (pageToken) => {
|
|
1239
|
+
const apiOptions = { token: pageToken };
|
|
1240
|
+
if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;
|
|
1241
|
+
const response = await client.reviews.list(packageName, apiOptions);
|
|
1242
|
+
return {
|
|
1243
|
+
items: response.reviews || [],
|
|
1244
|
+
nextPageToken: response.tokenPagination?.nextPageToken
|
|
1245
|
+
};
|
|
1246
|
+
});
|
|
1242
1247
|
let filtered = allReviews;
|
|
1243
1248
|
if (options?.stars !== void 0) {
|
|
1244
1249
|
filtered = filtered.filter((r) => {
|
|
@@ -1340,8 +1345,10 @@ async function getVitalsOverview(reporting, packageName) {
|
|
|
1340
1345
|
const overview = {};
|
|
1341
1346
|
for (let i = 0; i < metricSets.length; i++) {
|
|
1342
1347
|
const entry = metricSets[i];
|
|
1348
|
+
if (!entry) continue;
|
|
1343
1349
|
const key = entry[1];
|
|
1344
1350
|
const result = results[i];
|
|
1351
|
+
if (!result) continue;
|
|
1345
1352
|
if (result.status === "fulfilled") {
|
|
1346
1353
|
overview[key] = result.value.rows || [];
|
|
1347
1354
|
}
|
|
@@ -1370,11 +1377,7 @@ async function getVitalsAnomalies(reporting, packageName) {
|
|
|
1370
1377
|
return reporting.getAnomalies(packageName);
|
|
1371
1378
|
}
|
|
1372
1379
|
async function searchVitalsErrors(reporting, packageName, options) {
|
|
1373
|
-
return reporting.searchErrorIssues(
|
|
1374
|
-
packageName,
|
|
1375
|
-
options?.filter,
|
|
1376
|
-
options?.maxResults
|
|
1377
|
-
);
|
|
1380
|
+
return reporting.searchErrorIssues(packageName, options?.filter, options?.maxResults);
|
|
1378
1381
|
}
|
|
1379
1382
|
async function compareVitalsTrend(reporting, packageName, metricSet, days = 7) {
|
|
1380
1383
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1442,14 +1445,20 @@ async function listSubscriptions(client, packageName, options) {
|
|
|
1442
1445
|
if (options?.limit || options?.nextPage) {
|
|
1443
1446
|
const result = await paginateAll2(
|
|
1444
1447
|
async (pageToken) => {
|
|
1445
|
-
const resp = await client.subscriptions.list(packageName, {
|
|
1448
|
+
const resp = await client.subscriptions.list(packageName, {
|
|
1449
|
+
pageToken,
|
|
1450
|
+
pageSize: options?.pageSize
|
|
1451
|
+
});
|
|
1446
1452
|
return { items: resp.subscriptions || [], nextPageToken: resp.nextPageToken };
|
|
1447
1453
|
},
|
|
1448
1454
|
{ limit: options.limit, startPageToken: options.nextPage }
|
|
1449
1455
|
);
|
|
1450
1456
|
return { subscriptions: result.items, nextPageToken: result.nextPageToken };
|
|
1451
1457
|
}
|
|
1452
|
-
return client.subscriptions.list(packageName, {
|
|
1458
|
+
return client.subscriptions.list(packageName, {
|
|
1459
|
+
pageToken: options?.pageToken,
|
|
1460
|
+
pageSize: options?.pageSize
|
|
1461
|
+
});
|
|
1453
1462
|
}
|
|
1454
1463
|
async function getSubscription(client, packageName, productId) {
|
|
1455
1464
|
return client.subscriptions.get(packageName, productId);
|
|
@@ -1485,7 +1494,14 @@ async function createOffer(client, packageName, productId, basePlanId, data) {
|
|
|
1485
1494
|
return client.subscriptions.createOffer(packageName, productId, basePlanId, data);
|
|
1486
1495
|
}
|
|
1487
1496
|
async function updateOffer(client, packageName, productId, basePlanId, offerId, data, updateMask) {
|
|
1488
|
-
return client.subscriptions.updateOffer(
|
|
1497
|
+
return client.subscriptions.updateOffer(
|
|
1498
|
+
packageName,
|
|
1499
|
+
productId,
|
|
1500
|
+
basePlanId,
|
|
1501
|
+
offerId,
|
|
1502
|
+
data,
|
|
1503
|
+
updateMask
|
|
1504
|
+
);
|
|
1489
1505
|
}
|
|
1490
1506
|
async function deleteOffer(client, packageName, productId, basePlanId, offerId) {
|
|
1491
1507
|
return client.subscriptions.deleteOffer(packageName, productId, basePlanId, offerId);
|
|
@@ -1505,14 +1521,23 @@ async function listInAppProducts(client, packageName, options) {
|
|
|
1505
1521
|
if (options?.limit || options?.nextPage) {
|
|
1506
1522
|
const result = await paginateAll3(
|
|
1507
1523
|
async (pageToken) => {
|
|
1508
|
-
const resp = await client.inappproducts.list(packageName, {
|
|
1509
|
-
|
|
1524
|
+
const resp = await client.inappproducts.list(packageName, {
|
|
1525
|
+
token: pageToken,
|
|
1526
|
+
maxResults: options?.maxResults
|
|
1527
|
+
});
|
|
1528
|
+
return {
|
|
1529
|
+
items: resp.inappproduct || [],
|
|
1530
|
+
nextPageToken: resp.tokenPagination?.nextPageToken
|
|
1531
|
+
};
|
|
1510
1532
|
},
|
|
1511
1533
|
{ limit: options.limit, startPageToken: options.nextPage }
|
|
1512
1534
|
);
|
|
1513
1535
|
return { inappproduct: result.items, nextPageToken: result.nextPageToken };
|
|
1514
1536
|
}
|
|
1515
|
-
return client.inappproducts.list(packageName, {
|
|
1537
|
+
return client.inappproducts.list(packageName, {
|
|
1538
|
+
token: options?.token,
|
|
1539
|
+
maxResults: options?.maxResults
|
|
1540
|
+
});
|
|
1516
1541
|
}
|
|
1517
1542
|
async function getInAppProduct(client, packageName, sku) {
|
|
1518
1543
|
return client.inappproducts.get(packageName, sku);
|
|
@@ -1541,7 +1566,7 @@ async function syncInAppProducts(client, packageName, dir, options) {
|
|
|
1541
1566
|
const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));
|
|
1542
1567
|
let created = 0;
|
|
1543
1568
|
let updated = 0;
|
|
1544
|
-
|
|
1569
|
+
const unchanged = 0;
|
|
1545
1570
|
const skus = [];
|
|
1546
1571
|
for (const product of localProducts) {
|
|
1547
1572
|
skus.push(product.sku);
|
|
@@ -1600,7 +1625,10 @@ async function listVoidedPurchases(client, packageName, options) {
|
|
|
1600
1625
|
maxResults: options?.maxResults,
|
|
1601
1626
|
token: pageToken
|
|
1602
1627
|
});
|
|
1603
|
-
return {
|
|
1628
|
+
return {
|
|
1629
|
+
items: resp.voidedPurchases || [],
|
|
1630
|
+
nextPageToken: resp.tokenPagination?.nextPageToken
|
|
1631
|
+
};
|
|
1604
1632
|
},
|
|
1605
1633
|
{ limit: options.limit, startPageToken: options.nextPage }
|
|
1606
1634
|
);
|
|
@@ -1665,9 +1693,7 @@ function isValidStatsDimension(dim) {
|
|
|
1665
1693
|
function parseMonth(monthStr) {
|
|
1666
1694
|
const match = /^(\d{4})-(\d{2})$/.exec(monthStr);
|
|
1667
1695
|
if (!match) {
|
|
1668
|
-
throw new Error(
|
|
1669
|
-
`Invalid month format "${monthStr}". Expected YYYY-MM (e.g., 2026-03).`
|
|
1670
|
-
);
|
|
1696
|
+
throw new Error(`Invalid month format "${monthStr}". Expected YYYY-MM (e.g., 2026-03).`);
|
|
1671
1697
|
}
|
|
1672
1698
|
const year = Number(match[1]);
|
|
1673
1699
|
const month = Number(match[2]);
|
|
@@ -1696,9 +1722,7 @@ async function downloadReport(client, packageName, reportType, year, month) {
|
|
|
1696
1722
|
const uri = bucket.uri;
|
|
1697
1723
|
const response = await fetch(uri);
|
|
1698
1724
|
if (!response.ok) {
|
|
1699
|
-
throw new Error(
|
|
1700
|
-
`Failed to download report from signed URI: HTTP ${response.status}`
|
|
1701
|
-
);
|
|
1725
|
+
throw new Error(`Failed to download report from signed URI: HTTP ${response.status}`);
|
|
1702
1726
|
}
|
|
1703
1727
|
return response.text();
|
|
1704
1728
|
}
|
|
@@ -1794,9 +1818,7 @@ async function removeTesters(client, packageName, track, groupEmails) {
|
|
|
1794
1818
|
try {
|
|
1795
1819
|
const current = await client.testers.get(packageName, edit.id, track);
|
|
1796
1820
|
const toRemove = new Set(groupEmails.map((e) => e.trim()));
|
|
1797
|
-
const filtered = (current.googleGroups || []).filter(
|
|
1798
|
-
(g) => !toRemove.has(g)
|
|
1799
|
-
);
|
|
1821
|
+
const filtered = (current.googleGroups || []).filter((g) => !toRemove.has(g));
|
|
1800
1822
|
const updated = await client.testers.update(packageName, edit.id, track, {
|
|
1801
1823
|
googleGroups: filtered
|
|
1802
1824
|
});
|
|
@@ -1828,9 +1850,7 @@ function safePathWithin(userPath, baseDir) {
|
|
|
1828
1850
|
const resolved = safePath(userPath);
|
|
1829
1851
|
const base = safePath(baseDir);
|
|
1830
1852
|
if (!resolved.startsWith(base + "/") && resolved !== base) {
|
|
1831
|
-
throw new Error(
|
|
1832
|
-
`Path "${userPath}" resolves outside the expected directory "${baseDir}"`
|
|
1833
|
-
);
|
|
1853
|
+
throw new Error(`Path "${userPath}" resolves outside the expected directory "${baseDir}"`);
|
|
1834
1854
|
}
|
|
1835
1855
|
return resolved;
|
|
1836
1856
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/output.ts","../src/plugins.ts","../src/commands/apps.ts","../src/utils/file-validation.ts","../src/commands/releases.ts","../src/utils/bcp47.ts","../src/utils/image-validation.ts","../src/utils/fastlane.ts","../src/commands/listings.ts","../src/utils/release-notes.ts","../src/commands/validate.ts","../src/commands/publish.ts","../src/commands/reviews.ts","../src/commands/vitals.ts","../src/commands/subscriptions.ts","../src/commands/iap.ts","../src/commands/purchases.ts","../src/commands/pricing.ts","../src/commands/reports.ts","../src/commands/users.ts","../src/commands/testers.ts","../src/utils/safe-path.ts","../src/commands/plugin-scaffold.ts","../src/audit.ts"],"sourcesContent":["export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\n\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import type { OutputFormat } from \"@gpc-cli/config\";\nimport process from \"node:process\";\n\nexport function detectOutputFormat(): OutputFormat {\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nexport function formatOutput(data: unknown, format: OutputFormat, redact = true): string {\n const safe = redact ? redactSensitive(data) : data;\n switch (format) {\n case \"json\":\n return formatJson(safe);\n case \"yaml\":\n return formatYaml(safe);\n case \"markdown\":\n return formatMarkdown(safe);\n case \"table\":\n return formatTable(safe);\n default:\n return formatJson(safe);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field redaction\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_KEYS = new Set([\n \"private_key\",\n \"privateKey\",\n \"private_key_id\",\n \"privateKeyId\",\n \"accessToken\",\n \"access_token\",\n \"refreshToken\",\n \"refresh_token\",\n \"client_secret\",\n \"clientSecret\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n]);\n\nconst REDACTED = \"[REDACTED]\";\n\n/** Recursively redact sensitive fields from data before output. */\nexport function redactSensitive(data: unknown): unknown {\n if (data === null || data === undefined) return data;\n\n if (typeof data === \"string\") return data;\n if (typeof data === \"number\" || typeof data === \"boolean\") return data;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitive(item));\n }\n\n if (typeof data === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (SENSITIVE_KEYS.has(key) && typeof value === \"string\") {\n result[key] = REDACTED;\n } else {\n result[key] = redactSensitive(value);\n }\n }\n return result;\n }\n\n return data;\n}\n\nfunction formatJson(data: unknown): string {\n return JSON.stringify(data, null, 2);\n}\n\nfunction formatYaml(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return \"null\";\n }\n\n if (typeof data === \"string\") {\n return data.includes(\"\\n\") ? `|\\n${data.split(\"\\n\").map((l) => `${\" \".repeat(indent + 1)}${l}`).join(\"\\n\")}` : data;\n }\n\n if (typeof data === \"number\" || typeof data === \"boolean\") {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return \"[]\";\n return data\n .map((item) => {\n const value = formatYaml(item, indent + 1);\n const prefix = `${\" \".repeat(indent)}- `;\n if (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n const lines = value.split(\"\\n\");\n return `${prefix}${lines[0]}\\n${lines.slice(1).map((l) => `${\" \".repeat(indent)} ${l}`).join(\"\\n\")}`;\n }\n return `${prefix}${value}`;\n })\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return \"{}\";\n return entries\n .map(([key, value]) => {\n if (typeof value === \"object\" && value !== null) {\n return `${\" \".repeat(indent)}${key}:\\n${formatYaml(value, indent + 1)}`;\n }\n return `${\" \".repeat(indent)}${key}: ${formatYaml(value, indent)}`;\n })\n .join(\"\\n\");\n }\n\n return String(data);\n}\n\nfunction formatTable(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const keys = Object.keys(rows[0]!);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = keys.map((key, i) => key.padEnd(widths[i]!)).join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const body = rows\n .map((row) =>\n keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i]!)).join(\" \"),\n )\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction formatMarkdown(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const keys = Object.keys(rows[0]!);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = `| ${keys.map((key, i) => key.padEnd(widths[i]!)).join(\" | \")} |`;\n const separator = `| ${widths.map((w) => \"-\".repeat(w)).join(\" | \")} |`;\n const body = rows\n .map(\n (row) =>\n `| ${keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i]!)).join(\" | \")} |`,\n )\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction toRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) {\n return data.filter(\n (item): item is Record<string, unknown> =>\n typeof item === \"object\" && item !== null,\n );\n }\n if (typeof data === \"object\" && data !== null) {\n return [data as Record<string, unknown>];\n }\n return [];\n}\n","import type {\n GpcPlugin,\n PluginHooks,\n BeforeCommandHandler,\n AfterCommandHandler,\n ErrorHandler,\n BeforeRequestHandler,\n AfterResponseHandler,\n CommandRegistrar,\n CommandEvent,\n CommandResult,\n PluginError,\n PluginCommand,\n PluginManifest,\n PluginPermission,\n RequestEvent,\n ResponseEvent,\n} from \"@gpc-cli/plugin-sdk\";\nimport { GpcError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Plugin Manager — orchestrates discovery, loading, and lifecycle\n// ---------------------------------------------------------------------------\n\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private beforeHandlers: BeforeCommandHandler[] = [];\n private afterHandlers: AfterCommandHandler[] = [];\n private errorHandlers: ErrorHandler[] = [];\n private beforeRequestHandlers: BeforeRequestHandler[] = [];\n private afterResponseHandlers: AfterResponseHandler[] = [];\n private registeredCommands: PluginCommand[] = [];\n\n /** Load and register a plugin */\n async load(plugin: GpcPlugin, manifest?: PluginManifest): Promise<void> {\n const isTrusted = manifest?.trusted ?? plugin.name.startsWith(\"@gpc-cli/\");\n\n if (!isTrusted && manifest?.permissions) {\n validatePermissions(manifest.permissions);\n }\n\n const hooks = createHooks(\n this.beforeHandlers,\n this.afterHandlers,\n this.errorHandlers,\n this.beforeRequestHandlers,\n this.afterResponseHandlers,\n this.registeredCommands,\n );\n\n await plugin.register(hooks);\n\n this.plugins.push({\n name: plugin.name,\n version: plugin.version,\n trusted: isTrusted,\n });\n }\n\n /** Run all beforeCommand handlers */\n async runBeforeCommand(event: CommandEvent): Promise<void> {\n for (const handler of this.beforeHandlers) {\n await handler(event);\n }\n }\n\n /** Run all afterCommand handlers */\n async runAfterCommand(event: CommandEvent, result: CommandResult): Promise<void> {\n for (const handler of this.afterHandlers) {\n await handler(event, result);\n }\n }\n\n /** Run all onError handlers */\n async runOnError(event: CommandEvent, error: PluginError): Promise<void> {\n for (const handler of this.errorHandlers) {\n try {\n await handler(event, error);\n } catch {\n // Don't let error handlers crash the process\n }\n }\n }\n\n /** Run all beforeRequest handlers */\n async runBeforeRequest(event: RequestEvent): Promise<void> {\n for (const handler of this.beforeRequestHandlers) {\n try {\n await handler(event);\n } catch {\n // Don't let request hooks block API calls\n }\n }\n }\n\n /** Run all afterResponse handlers */\n async runAfterResponse(event: RequestEvent, response: ResponseEvent): Promise<void> {\n for (const handler of this.afterResponseHandlers) {\n try {\n await handler(event, response);\n } catch {\n // Don't let response hooks crash the process\n }\n }\n }\n\n /** Get commands registered by plugins */\n getRegisteredCommands(): PluginCommand[] {\n return [...this.registeredCommands];\n }\n\n /** Get list of loaded plugins */\n getLoadedPlugins(): LoadedPlugin[] {\n return [...this.plugins];\n }\n\n /** Whether any request/response hooks are registered */\n hasRequestHooks(): boolean {\n return this.beforeRequestHandlers.length > 0 || this.afterResponseHandlers.length > 0;\n }\n\n /** Reset (for testing) */\n reset(): void {\n this.plugins = [];\n this.beforeHandlers = [];\n this.afterHandlers = [];\n this.errorHandlers = [];\n this.beforeRequestHandlers = [];\n this.afterResponseHandlers = [];\n this.registeredCommands = [];\n }\n}\n\nexport interface LoadedPlugin {\n name: string;\n version: string;\n trusted: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Hook factory\n// ---------------------------------------------------------------------------\n\nfunction createHooks(\n beforeHandlers: BeforeCommandHandler[],\n afterHandlers: AfterCommandHandler[],\n errorHandlers: ErrorHandler[],\n beforeRequestHandlers: BeforeRequestHandler[],\n afterResponseHandlers: AfterResponseHandler[],\n registeredCommands: PluginCommand[],\n): PluginHooks {\n return {\n beforeCommand(handler) {\n beforeHandlers.push(handler);\n },\n afterCommand(handler) {\n afterHandlers.push(handler);\n },\n onError(handler) {\n errorHandlers.push(handler);\n },\n beforeRequest(handler) {\n beforeRequestHandlers.push(handler);\n },\n afterResponse(handler) {\n afterResponseHandlers.push(handler);\n },\n registerCommands(registrar) {\n const registry = {\n add(cmd: PluginCommand) {\n registeredCommands.push(cmd);\n },\n };\n registrar(registry);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Permission validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PERMISSIONS: ReadonlySet<string> = new Set<PluginPermission>([\n \"read:config\",\n \"write:config\",\n \"read:auth\",\n \"api:read\",\n \"api:write\",\n \"commands:register\",\n \"hooks:beforeCommand\",\n \"hooks:afterCommand\",\n \"hooks:onError\",\n \"hooks:beforeRequest\",\n \"hooks:afterResponse\",\n]);\n\nfunction validatePermissions(permissions: PluginPermission[]): void {\n for (const perm of permissions) {\n if (!VALID_PERMISSIONS.has(perm)) {\n throw new GpcError(\n `Unknown plugin permission: \"${perm}\"`,\n \"PLUGIN_INVALID_PERMISSION\",\n 10,\n `Valid permissions: ${[...VALID_PERMISSIONS].join(\", \")}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin discovery\n// ---------------------------------------------------------------------------\n\nexport interface DiscoverPluginsOptions {\n /** Plugin names from config file */\n configPlugins?: string[];\n\n /** Working directory for node_modules scanning */\n cwd?: string;\n}\n\n/**\n * Discover plugins from multiple sources:\n * 1. Explicit config: gpc.config.ts → plugins: [...]\n * 2. Convention: node_modules/@gpc-cli/plugin-*\n * 3. Convention: node_modules/gpc-plugin-*\n */\nexport async function discoverPlugins(\n options?: DiscoverPluginsOptions,\n): Promise<GpcPlugin[]> {\n const plugins: GpcPlugin[] = [];\n const seen = new Set<string>();\n\n // Source 1: Explicit config plugins\n if (options?.configPlugins) {\n for (const name of options.configPlugins) {\n if (seen.has(name)) continue;\n try {\n const mod = await import(name);\n const plugin = resolvePlugin(mod);\n if (plugin) {\n plugins.push(plugin);\n seen.add(name);\n }\n } catch {\n // Plugin not found — skip silently\n }\n }\n }\n\n return plugins;\n}\n\n/**\n * Resolve a plugin from a module.\n * Supports: default export, named `plugin` export, or the module itself as a plugin.\n */\nfunction resolvePlugin(mod: unknown): GpcPlugin | undefined {\n if (!mod || typeof mod !== \"object\") return undefined;\n\n const m = mod as Record<string, unknown>;\n\n // Check default export\n if (isPlugin(m[\"default\"])) return m[\"default\"];\n\n // Check named `plugin` export\n if (isPlugin(m[\"plugin\"])) return m[\"plugin\"];\n\n // Check if module itself is a plugin\n if (isPlugin(m)) return m as unknown as GpcPlugin;\n\n return undefined;\n}\n\nfunction isPlugin(obj: unknown): obj is GpcPlugin {\n if (!obj || typeof obj !== \"object\") return false;\n const p = obj as Record<string, unknown>;\n return (\n typeof p[\"name\"] === \"string\" &&\n typeof p[\"version\"] === \"string\" &&\n typeof p[\"register\"] === \"function\"\n );\n}\n","import type { PlayApiClient, AppDetails, AppEdit } from \"@gpc-cli/api\";\n\nexport interface AppInfo {\n packageName: string;\n title?: string;\n defaultLanguage?: string;\n contactEmail?: string;\n}\n\nexport async function getAppInfo(\n client: PlayApiClient,\n packageName: string,\n): Promise<AppInfo> {\n // Create an edit to read app details (Google Play requires an edit context)\n const edit = await client.edits.insert(packageName);\n try {\n const details = await client.details.get(packageName, edit.id);\n // Delete the edit since we're only reading\n await client.edits.delete(packageName, edit.id);\n return {\n packageName,\n title: details.title,\n defaultLanguage: details.defaultLanguage,\n contactEmail: details.contactEmail,\n };\n } catch (error) {\n // Clean up edit on failure\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 150 * 1024 * 1024; // 150 MB\nconst MAX_AAB_SIZE = 500 * 1024 * 1024; // 500 MB (Play Store limit for AABs)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes = 0;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 150 MB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 500 MB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes (ZIP format — both AAB and APK are ZIP-based)\n if (sizeBytes > 0) {\n try {\n const fd = await readFile(filePath, { flag: \"r\" });\n const header = fd.subarray(0, 4);\n\n if (!header.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, Release, Track, Bundle } from \"@gpc-cli/api\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n },\n): Promise<UploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n if (!validation.valid) {\n throw new Error(`File validation failed:\\n${validation.errors.join(\"\\n\")}`);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n // Upload the bundle\n const bundle = await client.bundles.upload(packageName, edit.id, filePath);\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n );\n }\n\n // Create release and assign to track\n const release: Release = {\n versionCodes: [String(bundle.versionCode)],\n status: (options.status || (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n // Validate and commit\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: { userFraction?: number; releaseNotes?: { language: string; text: string }[] },\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active release found on track \"${fromTrack}\"`);\n }\n\n // Create release on target track\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.userFraction ? \"inProgress\" : \"completed\") as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active rollout found on track \"${track}\"`);\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction) throw new Error(\"--to <percentage> is required for rollout increase\");\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(\n client: PlayApiClient,\n packageName: string,\n): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","export const GOOGLE_PLAY_LANGUAGES: string[] = [\n \"af\", \"am\", \"ar\", \"hy-AM\", \"az-AZ\", \"eu-ES\", \"be\", \"bn-BD\",\n \"bg\", \"my-MM\", \"ca\", \"zh-HK\", \"zh-CN\", \"zh-TW\", \"hr\", \"cs-CZ\",\n \"da-DK\", \"nl-NL\", \"en-AU\", \"en-CA\", \"en-IN\", \"en-SG\", \"en-GB\", \"en-US\",\n \"et\", \"fil\", \"fi-FI\", \"fr-FR\", \"fr-CA\", \"gl-ES\", \"ka-GE\", \"de-DE\",\n \"el-GR\", \"gu\", \"iw-IL\", \"hi-IN\", \"hu-HU\", \"is-IS\", \"id\", \"it-IT\",\n \"ja-JP\", \"kn-IN\", \"kk\", \"km-KH\", \"ko-KR\", \"ky-KG\", \"lo-LA\", \"lv\",\n \"lt\", \"mk-MK\", \"ms\", \"ms-MY\", \"ml-IN\", \"mr-IN\", \"mn-MN\", \"ne-NP\",\n \"no-NO\", \"fa\", \"pl-PL\", \"pt-BR\", \"pt-PT\", \"pa\", \"ro\", \"rm\",\n \"ru-RU\", \"sr\", \"si-LK\", \"sk\", \"sl\", \"es-419\", \"es-ES\", \"es-US\",\n \"sw\", \"sv-SE\", \"ta-IN\", \"te-IN\", \"th\", \"tr-TR\", \"uk\", \"ur\",\n \"vi\", \"zu\",\n];\n\nexport function isValidBcp47(tag: string): boolean {\n return GOOGLE_PLAY_LANGUAGES.includes(tag);\n}\n","import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface ImageValidationResult {\n valid: boolean;\n warnings: string[];\n errors: string[];\n}\n\n// Google Play image size limits\nconst IMAGE_SIZE_LIMITS: Record<string, { maxBytes: number; label: string }> = {\n icon: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n featureGraphic: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n tvBanner: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n phoneScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n sevenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tvScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n wearScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n};\n\nconst VALID_EXTENSIONS = new Set([\".png\", \".jpg\", \".jpeg\"]);\nconst LARGE_IMAGE_THRESHOLD = 2 * 1024 * 1024; // 2 MB\n\nexport async function validateImage(\n filePath: string,\n imageType?: string,\n): Promise<ImageValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n if (!VALID_EXTENSIONS.has(ext)) {\n errors.push(`Unsupported image format \"${ext}\". Use PNG or JPEG.`);\n }\n\n // Check file exists and size\n let sizeBytes = 0;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"Image file is empty (0 bytes)\");\n }\n } catch {\n errors.push(`Image file not found: ${filePath}`);\n return { valid: false, errors, warnings };\n }\n\n // Check size limits per image type\n if (imageType && sizeBytes > 0) {\n const limit = IMAGE_SIZE_LIMITS[imageType];\n if (limit && sizeBytes > limit.maxBytes) {\n errors.push(`Image exceeds ${limit.label} limit for ${imageType} (${formatSize(sizeBytes)})`);\n }\n }\n\n // Warn about large images\n if (sizeBytes > LARGE_IMAGE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large image (${formatSize(sizeBytes)}). Consider optimizing for faster upload and better store performance.`,\n );\n }\n\n // PNG optimization warning\n if (ext === \".png\" && sizeBytes > 512 * 1024) {\n warnings.push(\"PNG file is over 512 KB. Consider compressing with tools like pngquant or optipng.\");\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import { readFile, writeFile, mkdir, readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Listing } from \"@gpc-cli/api\";\n\nconst FILE_MAP: Record<string, keyof Omit<Listing, \"language\">> = {\n \"title.txt\": \"title\",\n \"short_description.txt\": \"shortDescription\",\n \"full_description.txt\": \"fullDescription\",\n \"video.txt\": \"video\",\n};\n\nconst FIELD_TO_FILE: Record<string, string> = Object.fromEntries(\n Object.entries(FILE_MAP).map(([file, field]) => [field, file]),\n);\n\nexport interface ListingDiff {\n language: string;\n field: string;\n local: string;\n remote: string;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readListingsFromDir(dir: string): Promise<Listing[]> {\n const listings: Listing[] = [];\n\n if (!(await exists(dir))) return listings;\n\n const entries = await readdir(dir);\n for (const lang of entries) {\n const langDir = join(dir, lang);\n const langStat = await stat(langDir);\n if (!langStat.isDirectory()) continue;\n\n const listing: Listing = {\n language: lang,\n title: \"\",\n shortDescription: \"\",\n fullDescription: \"\",\n };\n\n for (const [fileName, field] of Object.entries(FILE_MAP)) {\n const filePath = join(langDir, fileName);\n if (await exists(filePath)) {\n const content = await readFile(filePath, \"utf-8\");\n (listing as any)[field] = content.trimEnd();\n }\n }\n\n listings.push(listing);\n }\n\n return listings;\n}\n\nexport async function writeListingsToDir(dir: string, listings: Listing[]): Promise<void> {\n for (const listing of listings) {\n const langDir = join(dir, listing.language);\n await mkdir(langDir, { recursive: true });\n\n for (const [field, fileName] of Object.entries(FIELD_TO_FILE)) {\n const value = (listing as any)[field];\n if (value !== undefined && value !== \"\") {\n await writeFile(join(langDir, fileName), value + \"\\n\", \"utf-8\");\n }\n }\n }\n}\n\nexport function diffListings(local: Listing[], remote: Listing[]): ListingDiff[] {\n const diffs: ListingDiff[] = [];\n const remoteMap = new Map(remote.map((l) => [l.language, l]));\n const localMap = new Map(local.map((l) => [l.language, l]));\n\n // Check all local listings against remote\n for (const localListing of local) {\n const remoteListing = remoteMap.get(localListing.language);\n for (const [field, fileName] of Object.entries(FIELD_TO_FILE)) {\n const localVal = ((localListing as any)[field] ?? \"\").toString();\n const remoteVal = remoteListing ? ((remoteListing as any)[field] ?? \"\").toString() : \"\";\n if (localVal !== remoteVal) {\n diffs.push({\n language: localListing.language,\n field,\n local: localVal,\n remote: remoteVal,\n });\n }\n }\n }\n\n // Check for remote-only languages\n for (const remoteListing of remote) {\n if (!localMap.has(remoteListing.language)) {\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const remoteVal = ((remoteListing as any)[field] ?? \"\").toString();\n if (remoteVal) {\n diffs.push({\n language: remoteListing.language,\n field,\n local: \"\",\n remote: remoteVal,\n });\n }\n }\n }\n }\n\n return diffs;\n}\n","import type { PlayApiClient, Listing, Image, ImageType, AppDetails, CountryAvailability } from \"@gpc-cli/api\";\nimport { isValidBcp47 } from \"../utils/bcp47.js\";\nimport { validateImage } from \"../utils/image-validation.js\";\nimport { readListingsFromDir, writeListingsToDir, diffListings } from \"../utils/fastlane.js\";\nimport type { ListingDiff } from \"../utils/fastlane.js\";\n\nexport interface ListingsResult {\n listings: Listing[];\n}\n\nexport interface PushResult {\n updated: number;\n languages: string[];\n}\n\nexport interface DryRunResult {\n diffs: ListingDiff[];\n}\n\nfunction validateLanguage(lang: string): void {\n if (!isValidBcp47(lang)) {\n throw new Error(`Invalid language tag \"${lang}\". Must be a valid Google Play BCP 47 code.`);\n }\n}\n\nexport async function getListings(\n client: PlayApiClient,\n packageName: string,\n language?: string,\n): Promise<Listing[]> {\n const edit = await client.edits.insert(packageName);\n try {\n let listings: Listing[];\n if (language) {\n validateLanguage(language);\n const listing = await client.listings.get(packageName, edit.id, language);\n listings = [listing];\n } else {\n listings = await client.listings.list(packageName, edit.id);\n }\n await client.edits.delete(packageName, edit.id);\n return listings;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n data: Partial<Omit<Listing, \"language\">>,\n): Promise<Listing> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const listing = await client.listings.patch(packageName, edit.id, language, data);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return listing;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.listings.delete(packageName, edit.id, language);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pullListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n): Promise<ListingsResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const listings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n await writeListingsToDir(dir, listings);\n return { listings };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pushListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<PushResult | DryRunResult> {\n const localListings = await readListingsFromDir(dir);\n\n if (localListings.length === 0) {\n throw new Error(`No listings found in directory \"${dir}\"`);\n }\n\n // Validate all languages\n for (const listing of localListings) {\n validateLanguage(listing.language);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n if (options?.dryRun) {\n const remoteListings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n const diffs = diffListings(localListings, remoteListings);\n return { diffs };\n }\n\n for (const listing of localListings) {\n const { language, ...data } = listing;\n await client.listings.update(packageName, edit.id, language, data);\n }\n\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n updated: localListings.length,\n languages: localListings.map((l) => l.language),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listImages(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n): Promise<Image[]> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const images = await client.images.list(packageName, edit.id, language, imageType);\n await client.edits.delete(packageName, edit.id);\n return images;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n): Promise<Image> {\n validateLanguage(language);\n\n // Validate image before upload\n const imageCheck = await validateImage(filePath, imageType);\n if (!imageCheck.valid) {\n throw new Error(`Image validation failed: ${imageCheck.errors.join(\"; \")}`);\n }\n if (imageCheck.warnings.length > 0) {\n for (const w of imageCheck.warnings) {\n console.warn(`Warning: ${w}`);\n }\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return image;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.images.delete(packageName, edit.id, language, imageType, imageId);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getCountryAvailability(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<CountryAvailability> {\n const edit = await client.edits.insert(packageName);\n try {\n const availability = await client.countryAvailability.get(packageName, edit.id, track);\n await client.edits.delete(packageName, edit.id);\n return availability;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateAppDetails(\n client: PlayApiClient,\n packageName: string,\n details: Partial<AppDetails>,\n): Promise<AppDetails> {\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.details.patch(packageName, edit.id, details);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { extname, basename, join } from \"node:path\";\n\nexport interface ReleaseNote {\n language: string;\n text: string;\n}\n\nexport interface ReleaseNotesValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nconst MAX_NOTES_LENGTH = 500;\n\nexport async function readReleaseNotesFromDir(dir: string): Promise<ReleaseNote[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new Error(`Release notes directory not found: ${dir}`);\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n if (extname(entry) !== \".txt\") continue;\n\n const language = basename(entry, \".txt\");\n const filePath = join(dir, entry);\n\n const stats = await stat(filePath);\n if (!stats.isFile()) continue;\n\n const text = (await readFile(filePath, \"utf-8\")).trim();\n if (text.length === 0) continue;\n\n notes.push({ language, text });\n }\n\n return notes;\n}\n\nexport function validateReleaseNotes(notes: ReleaseNote[]): ReleaseNotesValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const seen = new Set<string>();\n for (const note of notes) {\n if (seen.has(note.language)) {\n errors.push(`Duplicate language code: ${note.language}`);\n }\n seen.add(note.language);\n\n if (note.text.length > MAX_NOTES_LENGTH) {\n errors.push(\n `Release notes for \"${note.language}\" exceed ${MAX_NOTES_LENGTH} chars (${note.text.length} chars)`,\n );\n }\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n","import { stat } from \"node:fs/promises\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { readReleaseNotesFromDir, validateReleaseNotes } from \"../utils/release-notes.js\";\n\nexport interface ValidateOptions {\n filePath: string;\n mappingFile?: string;\n track?: string;\n notes?: { language: string; text: string }[];\n notesDir?: string;\n}\n\nexport interface ValidateCheck {\n name: string;\n passed: boolean;\n message: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n checks: ValidateCheck[];\n}\n\nconst STANDARD_TRACKS = new Set([\n \"internal\", \"alpha\", \"beta\", \"production\",\n // Form factor tracks\n \"wear:internal\", \"wear:alpha\", \"wear:beta\", \"wear:production\",\n \"automotive:internal\", \"automotive:alpha\", \"automotive:beta\", \"automotive:production\",\n \"tv:internal\", \"tv:alpha\", \"tv:beta\", \"tv:production\",\n \"android_xr:internal\", \"android_xr:alpha\", \"android_xr:beta\", \"android_xr:production\",\n]);\nconst TRACK_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_:-]*$/;\n\nexport async function validatePreSubmission(\n options: ValidateOptions,\n): Promise<ValidateResult> {\n const checks: ValidateCheck[] = [];\n\n // 1. File validation\n const fileResult = await validateUploadFile(options.filePath);\n checks.push({\n name: \"file\",\n passed: fileResult.valid,\n message: fileResult.valid\n ? `Valid ${fileResult.fileType} file (${formatSize(fileResult.sizeBytes)})`\n : fileResult.errors.join(\"; \"),\n });\n\n // 2. Mapping file\n if (options.mappingFile) {\n try {\n const stats = await stat(options.mappingFile);\n checks.push({\n name: \"mapping\",\n passed: stats.isFile(),\n message: stats.isFile()\n ? `Mapping file found (${formatSize(stats.size)})`\n : \"Mapping path is not a file\",\n });\n } catch {\n checks.push({\n name: \"mapping\",\n passed: false,\n message: `Mapping file not found: ${options.mappingFile}`,\n });\n }\n }\n\n // 3. Track validation\n if (options.track) {\n const isValid = STANDARD_TRACKS.has(options.track) || TRACK_PATTERN.test(options.track);\n checks.push({\n name: \"track\",\n passed: isValid,\n message: isValid\n ? `Track \"${options.track}\" is valid`\n : `Invalid track name \"${options.track}\". Use: internal, alpha, beta, production, or a custom track ID`,\n });\n }\n\n // 4. Release notes validation\n let resolvedNotes = options.notes;\n if (options.notesDir) {\n try {\n resolvedNotes = await readReleaseNotesFromDir(options.notesDir);\n checks.push({\n name: \"notes-dir\",\n passed: true,\n message: `Read release notes for ${resolvedNotes.length} language(s)`,\n });\n } catch (err) {\n checks.push({\n name: \"notes-dir\",\n passed: false,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n if (resolvedNotes && resolvedNotes.length > 0) {\n const notesResult = validateReleaseNotes(resolvedNotes);\n checks.push({\n name: \"notes\",\n passed: notesResult.valid,\n message: notesResult.valid\n ? `Release notes valid (${resolvedNotes.length} language(s))`\n : notesResult.errors.join(\"; \"),\n });\n }\n\n return {\n valid: checks.every((c) => c.passed),\n checks,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\nimport { uploadRelease } from \"./releases.js\";\nimport type { UploadResult } from \"./releases.js\";\nimport { validatePreSubmission } from \"./validate.js\";\nimport type { ValidateResult } from \"./validate.js\";\nimport { readReleaseNotesFromDir } from \"../utils/release-notes.js\";\n\nexport interface PublishOptions {\n track?: string;\n rolloutPercent?: number;\n notes?: string;\n notesDir?: string;\n releaseName?: string;\n mappingFile?: string;\n}\n\nexport interface PublishResult {\n validation: ValidateResult;\n upload?: UploadResult;\n}\n\nexport async function publish(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: PublishOptions,\n): Promise<PublishResult> {\n // Resolve release notes\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.notesDir) {\n releaseNotes = await readReleaseNotesFromDir(options.notesDir);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n // Validate\n const validation = await validatePreSubmission({\n filePath,\n mappingFile: options.mappingFile,\n track: options.track || \"internal\",\n notes: releaseNotes,\n });\n\n if (!validation.valid) {\n return { validation };\n }\n\n // Upload\n const upload = await uploadRelease(client, packageName, filePath, {\n track: options.track || \"internal\",\n userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : undefined,\n releaseNotes,\n releaseName: options.releaseName,\n mappingFile: options.mappingFile,\n });\n\n return { validation, upload };\n}\n","import type { PlayApiClient, Review, ReviewsListOptions, ReviewReplyResponse } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ReviewsFilterOptions {\n stars?: number;\n language?: string;\n since?: string;\n translationLanguage?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport interface ReviewExportOptions extends ReviewsFilterOptions {\n format?: \"json\" | \"csv\";\n}\n\nexport async function listReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewsFilterOptions,\n): Promise<Review[]> {\n const apiOptions: ReviewsListOptions = {};\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n if (options?.maxResults) apiOptions.maxResults = options.maxResults;\n\n const response = await client.reviews.list(packageName, apiOptions);\n let reviews = response.reviews || [];\n\n // Client-side filters\n if (options?.stars !== undefined) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && userComment.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && Number(userComment.lastModified.seconds) >= sinceTime;\n });\n }\n\n return reviews;\n}\n\nexport async function getReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n translationLanguage?: string,\n): Promise<Review> {\n return client.reviews.get(packageName, reviewId, translationLanguage);\n}\n\nconst MAX_REPLY_LENGTH = 350;\n\nexport async function replyToReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n replyText: string,\n): Promise<ReviewReplyResponse> {\n if (replyText.length > MAX_REPLY_LENGTH) {\n throw new Error(\n `Reply text exceeds ${MAX_REPLY_LENGTH} characters (${replyText.length}). Google Play limits replies to ${MAX_REPLY_LENGTH} characters.`,\n );\n }\n if (replyText.length === 0) {\n throw new Error(\"Reply text cannot be empty.\");\n }\n return client.reviews.reply(packageName, reviewId, replyText);\n}\n\nexport async function exportReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewExportOptions,\n): Promise<string> {\n const { items: allReviews } = await paginateAll<Review>(\n async (pageToken) => {\n const apiOptions: ReviewsListOptions = { token: pageToken };\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n const response = await client.reviews.list(packageName, apiOptions);\n return { items: response.reviews || [], nextPageToken: response.tokenPagination?.nextPageToken };\n },\n );\n\n let filtered = allReviews;\n\n if (options?.stars !== undefined) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && uc.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && Number(uc.lastModified.seconds) >= sinceTime;\n });\n }\n\n if (options?.format === \"csv\") {\n return reviewsToCsv(filtered);\n }\n\n return JSON.stringify(filtered, null, 2);\n}\n\nfunction reviewsToCsv(reviews: Review[]): string {\n const header = \"reviewId,authorName,starRating,text,language,date,device,appVersionName\";\n const rows = reviews.map((r) => {\n const uc = r.comments?.[0]?.userComment;\n const fields = [\n r.reviewId,\n csvEscape(r.authorName),\n uc?.starRating ?? \"\",\n csvEscape(uc?.text ?? \"\"),\n uc?.reviewerLanguage ?? \"\",\n uc ? new Date(Number(uc.lastModified.seconds) * 1000).toISOString() : \"\",\n csvEscape(uc?.device ?? \"\"),\n csvEscape(uc?.appVersionName ?? \"\"),\n ];\n return fields.join(\",\");\n });\n return [header, ...rows].join(\"\\n\");\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(\",\") || value.includes('\"') || value.includes(\"\\n\")) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","import type {\n ReportingApiClient,\n VitalsMetricSet,\n MetricSetQuery,\n MetricSetResponse,\n MetricRow,\n AnomalyDetectionResponse,\n ErrorIssuesResponse,\n ReportingDimension,\n ReportingAggregation,\n} from \"@gpc-cli/api\";\n\nexport interface VitalsQueryOptions {\n dimension?: ReportingDimension;\n days?: number;\n aggregation?: ReportingAggregation;\n}\n\nexport interface VitalsOverview {\n crashRate?: MetricRow[];\n anrRate?: MetricRow[];\n slowStartRate?: MetricRow[];\n slowRenderingRate?: MetricRow[];\n excessiveWakeupRate?: MetricRow[];\n stuckWakelockRate?: MetricRow[];\n}\n\nexport interface ThresholdResult {\n breached: boolean;\n value: number | undefined;\n threshold: number;\n}\n\nfunction buildQuery(options?: VitalsQueryOptions): MetricSetQuery {\n const query: MetricSetQuery = {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n };\n\n if (options?.dimension) {\n query.dimensions = [options.dimension];\n }\n\n if (options?.days) {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - options.days);\n query.timelineSpec = {\n aggregationPeriod: options.aggregation ?? \"DAILY\",\n startTime: {\n year: start.getFullYear(),\n month: start.getMonth() + 1,\n day: start.getDate(),\n },\n endTime: {\n year: end.getFullYear(),\n month: end.getMonth() + 1,\n day: end.getDate(),\n },\n };\n }\n\n return query;\n}\n\nasync function queryMetric(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n const query = buildQuery(options);\n return reporting.queryMetricSet(packageName, metricSet, query);\n}\n\nexport async function getVitalsOverview(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<VitalsOverview> {\n const metricSets: [VitalsMetricSet, keyof VitalsOverview][] = [\n [\"vitals.crashrate\", \"crashRate\"],\n [\"vitals.anrrate\", \"anrRate\"],\n [\"vitals.slowstartrate\", \"slowStartRate\"],\n [\"vitals.slowrenderingrate\", \"slowRenderingRate\"],\n [\"vitals.excessivewakeuprate\", \"excessiveWakeupRate\"],\n [\"vitals.stuckbackgroundwakelockrate\", \"stuckWakelockRate\"],\n ];\n\n const results = await Promise.allSettled(\n metricSets.map(([metric]) =>\n reporting.queryMetricSet(packageName, metric, {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n }),\n ),\n );\n\n const overview: VitalsOverview = {};\n for (let i = 0; i < metricSets.length; i++) {\n const entry = metricSets[i]!;\n const key = entry[1];\n const result = results[i]!;\n if (result.status === \"fulfilled\") {\n overview[key] = result.value.rows || [];\n }\n }\n\n return overview;\n}\n\nexport async function getVitalsCrashes(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.crashrate\", options);\n}\n\nexport async function getVitalsAnr(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.anrrate\", options);\n}\n\nexport async function getVitalsStartup(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowstartrate\", options);\n}\n\nexport async function getVitalsRendering(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowrenderingrate\", options);\n}\n\nexport async function getVitalsBattery(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.excessivewakeuprate\", options);\n}\n\nexport async function getVitalsMemory(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.stuckbackgroundwakelockrate\", options);\n}\n\nexport async function getVitalsAnomalies(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<AnomalyDetectionResponse> {\n return reporting.getAnomalies(packageName);\n}\n\nexport async function searchVitalsErrors(\n reporting: ReportingApiClient,\n packageName: string,\n options?: { filter?: string; maxResults?: number },\n): Promise<ErrorIssuesResponse> {\n return reporting.searchErrorIssues(\n packageName,\n options?.filter,\n options?.maxResults,\n );\n}\n\nexport interface VitalsTrendComparison {\n metric: string;\n current: number | undefined;\n previous: number | undefined;\n changePercent: number | undefined;\n direction: \"improved\" | \"degraded\" | \"unchanged\" | \"unknown\";\n}\n\nexport async function compareVitalsTrend(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n days: number = 7,\n): Promise<VitalsTrendComparison> {\n const now = new Date();\n\n // Current period\n const currentEnd = new Date(now);\n const currentStart = new Date(now);\n currentStart.setDate(currentStart.getDate() - days);\n\n // Previous period\n const previousEnd = new Date(currentStart);\n const previousStart = new Date(previousEnd);\n previousStart.setDate(previousStart.getDate() - days);\n\n const makeQuery = (start: Date, end: Date): MetricSetQuery => ({\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n timelineSpec: {\n aggregationPeriod: \"DAILY\",\n startTime: { year: start.getFullYear(), month: start.getMonth() + 1, day: start.getDate() },\n endTime: { year: end.getFullYear(), month: end.getMonth() + 1, day: end.getDate() },\n },\n });\n\n const [currentResult, previousResult] = await Promise.all([\n reporting.queryMetricSet(packageName, metricSet, makeQuery(currentStart, currentEnd)),\n reporting.queryMetricSet(packageName, metricSet, makeQuery(previousStart, previousEnd)),\n ]);\n\n const extractAvg = (rows: MetricRow[] | undefined): number | undefined => {\n if (!rows || rows.length === 0) return undefined;\n const values = rows\n .map((r) => {\n const keys = Object.keys(r.metrics);\n const first = keys[0];\n return first ? Number(r.metrics[first]?.decimalValue?.value) : NaN;\n })\n .filter((v) => !isNaN(v));\n if (values.length === 0) return undefined;\n return values.reduce((a, b) => a + b, 0) / values.length;\n };\n\n const current = extractAvg(currentResult.rows);\n const previous = extractAvg(previousResult.rows);\n\n let changePercent: number | undefined;\n let direction: VitalsTrendComparison[\"direction\"] = \"unknown\";\n\n if (current !== undefined && previous !== undefined && previous !== 0) {\n changePercent = ((current - previous) / previous) * 100;\n if (Math.abs(changePercent) < 1) {\n direction = \"unchanged\";\n } else if (changePercent < 0) {\n direction = \"improved\"; // lower error rate = better\n } else {\n direction = \"degraded\";\n }\n }\n\n return {\n metric: metricSet,\n current,\n previous,\n changePercent: changePercent !== undefined ? Math.round(changePercent * 10) / 10 : undefined,\n direction,\n };\n}\n\nexport function checkThreshold(\n value: number | undefined,\n threshold: number,\n): ThresholdResult {\n return {\n breached: value !== undefined && value > threshold,\n value,\n threshold,\n };\n}\n","import type {\n PlayApiClient,\n Subscription,\n SubscriptionsListResponse,\n BasePlanMigratePricesRequest,\n SubscriptionOffer,\n OffersListResponse,\n} from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListSubscriptionsOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listSubscriptions(\n client: PlayApiClient,\n packageName: string,\n options?: ListSubscriptionsOptions,\n): Promise<{ subscriptions: Subscription[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<Subscription>(\n async (pageToken) => {\n const resp = await client.subscriptions.list(packageName, { pageToken, pageSize: options?.pageSize });\n return { items: resp.subscriptions || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { subscriptions: result.items, nextPageToken: result.nextPageToken };\n }\n return client.subscriptions.list(packageName, { pageToken: options?.pageToken, pageSize: options?.pageSize });\n}\n\nexport async function getSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<Subscription> {\n return client.subscriptions.get(packageName, productId);\n}\n\nexport async function createSubscription(\n client: PlayApiClient,\n packageName: string,\n data: Subscription,\n): Promise<Subscription> {\n return client.subscriptions.create(packageName, data);\n}\n\nexport async function updateSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n): Promise<Subscription> {\n return client.subscriptions.update(packageName, productId, data, updateMask);\n}\n\nexport async function deleteSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<void> {\n return client.subscriptions.delete(packageName, productId);\n}\n\nexport async function activateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.activateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deactivateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.deactivateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deleteBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<void> {\n return client.subscriptions.deleteBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function migratePrices(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: BasePlanMigratePricesRequest,\n): Promise<Subscription> {\n return client.subscriptions.migratePrices(packageName, productId, basePlanId, data);\n}\n\nexport async function listOffers(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<OffersListResponse> {\n return client.subscriptions.listOffers(packageName, productId, basePlanId);\n}\n\nexport async function getOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.getOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function createOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.createOffer(packageName, productId, basePlanId, data);\n}\n\nexport async function updateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.updateOffer(packageName, productId, basePlanId, offerId, data, updateMask);\n}\n\nexport async function deleteOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<void> {\n return client.subscriptions.deleteOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function activateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function deactivateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.deactivateOffer(packageName, productId, basePlanId, offerId);\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n PlayApiClient,\n InAppProduct,\n InAppProductsListResponse,\n} from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListIapOptions {\n token?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listInAppProducts(\n client: PlayApiClient,\n packageName: string,\n options?: ListIapOptions,\n): Promise<{ inappproduct: InAppProduct[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<InAppProduct>(\n async (pageToken) => {\n const resp = await client.inappproducts.list(packageName, { token: pageToken, maxResults: options?.maxResults });\n return { items: resp.inappproduct || [], nextPageToken: resp.tokenPagination?.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { inappproduct: result.items, nextPageToken: result.nextPageToken };\n }\n return client.inappproducts.list(packageName, { token: options?.token, maxResults: options?.maxResults });\n}\n\nexport async function getInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<InAppProduct> {\n return client.inappproducts.get(packageName, sku);\n}\n\nexport async function createInAppProduct(\n client: PlayApiClient,\n packageName: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.create(packageName, data);\n}\n\nexport async function updateInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.update(packageName, sku, data);\n}\n\nexport async function deleteInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<void> {\n return client.inappproducts.delete(packageName, sku);\n}\n\nexport interface SyncResult {\n created: number;\n updated: number;\n unchanged: number;\n skus: string[];\n}\n\nexport async function syncInAppProducts(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<SyncResult> {\n const files = await readdir(dir);\n const jsonFiles = files.filter((f) => f.endsWith(\".json\"));\n\n if (jsonFiles.length === 0) {\n return { created: 0, updated: 0, unchanged: 0, skus: [] };\n }\n\n const localProducts: InAppProduct[] = [];\n for (const file of jsonFiles) {\n const content = await readFile(join(dir, file), \"utf-8\");\n localProducts.push(JSON.parse(content) as InAppProduct);\n }\n\n const response = await client.inappproducts.list(packageName);\n const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));\n\n let created = 0;\n let updated = 0;\n let unchanged = 0;\n const skus: string[] = [];\n\n for (const product of localProducts) {\n skus.push(product.sku);\n if (remoteSkus.has(product.sku)) {\n if (!options?.dryRun) {\n await client.inappproducts.update(packageName, product.sku, product);\n }\n updated++;\n } else {\n if (!options?.dryRun) {\n await client.inappproducts.create(packageName, product);\n }\n created++;\n }\n }\n\n return { created, updated, unchanged, skus };\n}\n","import type {\n PlayApiClient,\n ProductPurchase,\n SubscriptionPurchaseV2,\n SubscriptionDeferResponse,\n} from \"@gpc-cli/api\";\n\nexport async function getProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<ProductPurchase> {\n return client.purchases.getProduct(packageName, productId, token);\n}\n\nexport async function acknowledgeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n payload?: string,\n): Promise<void> {\n const body = payload ? { developerPayload: payload } : undefined;\n return client.purchases.acknowledgeProduct(packageName, productId, token, body);\n}\n\nexport async function consumeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<void> {\n return client.purchases.consumeProduct(packageName, productId, token);\n}\n\nexport async function getSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<SubscriptionPurchaseV2> {\n return client.purchases.getSubscriptionV2(packageName, token);\n}\n\nexport async function cancelSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n): Promise<void> {\n return client.purchases.cancelSubscription(packageName, subscriptionId, token);\n}\n\nexport async function deferSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n desiredExpiry: string,\n): Promise<SubscriptionDeferResponse> {\n const sub = await client.purchases.getSubscriptionV1(packageName, subscriptionId, token);\n return client.purchases.deferSubscription(packageName, subscriptionId, token, {\n deferralInfo: {\n expectedExpiryTimeMillis: sub.expiryTimeMillis,\n desiredExpiryTimeMillis: String(new Date(desiredExpiry).getTime()),\n },\n });\n}\n\nexport async function revokeSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<void> {\n return client.purchases.revokeSubscriptionV2(packageName, token);\n}\n\nimport type { VoidedPurchase } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListVoidedOptions {\n startTime?: string;\n endTime?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listVoidedPurchases(\n client: PlayApiClient,\n packageName: string,\n options?: ListVoidedOptions,\n): Promise<{ voidedPurchases: VoidedPurchase[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<VoidedPurchase>(\n async (pageToken) => {\n const resp = await client.purchases.listVoided(packageName, {\n startTime: options?.startTime,\n endTime: options?.endTime,\n maxResults: options?.maxResults,\n token: pageToken,\n });\n return { items: resp.voidedPurchases || [], nextPageToken: resp.tokenPagination?.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { voidedPurchases: result.items, nextPageToken: result.nextPageToken };\n }\n return client.purchases.listVoided(packageName, options);\n}\n\nexport async function refundOrder(\n client: PlayApiClient,\n packageName: string,\n orderId: string,\n options?: { fullRefund?: boolean; proratedRefund?: boolean },\n): Promise<void> {\n return client.orders.refund(packageName, orderId, options);\n}\n","import type { PlayApiClient, ConvertRegionPricesResponse } from \"@gpc-cli/api\";\n\nexport async function convertRegionPrices(\n client: PlayApiClient,\n packageName: string,\n currencyCode: string,\n amount: string,\n): Promise<ConvertRegionPricesResponse> {\n const units = amount.split(\".\")[0] || \"0\";\n const fractional = amount.split(\".\")[1] || \"0\";\n const nanos = Number(fractional.padEnd(9, \"0\").slice(0, 9));\n\n return client.monetization.convertRegionPrices(packageName, {\n price: {\n currencyCode,\n units,\n nanos,\n },\n });\n}\n","import type { PlayApiClient, ReportBucket, ReportType, StatsDimension } from \"@gpc-cli/api\";\n\nconst FINANCIAL_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"earnings\",\n \"sales\",\n \"estimated_sales\",\n \"play_balance\",\n]);\n\nconst STATS_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"installs\",\n \"crashes\",\n \"ratings\",\n \"reviews\",\n \"store_performance\",\n \"subscriptions\",\n]);\n\nconst VALID_DIMENSIONS: ReadonlySet<string> = new Set([\n \"country\",\n \"language\",\n \"os_version\",\n \"device\",\n \"app_version\",\n \"carrier\",\n \"overview\",\n]);\n\nexport function isFinancialReportType(type: string): boolean {\n return FINANCIAL_REPORT_TYPES.has(type);\n}\n\nexport function isStatsReportType(type: string): boolean {\n return STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidReportType(type: string): type is ReportType {\n return FINANCIAL_REPORT_TYPES.has(type) || STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidStatsDimension(dim: string): dim is StatsDimension {\n return VALID_DIMENSIONS.has(dim);\n}\n\nexport interface ParsedMonth {\n year: number;\n month: number;\n}\n\nexport function parseMonth(monthStr: string): ParsedMonth {\n const match = /^(\\d{4})-(\\d{2})$/.exec(monthStr);\n if (!match) {\n throw new Error(\n `Invalid month format \"${monthStr}\". Expected YYYY-MM (e.g., 2026-03).`,\n );\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n if (month < 1 || month > 12) {\n throw new Error(`Invalid month \"${month}\". Must be between 01 and 12.`);\n }\n return { year, month };\n}\n\nexport async function listReports(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<ReportBucket[]> {\n const response = await client.reports.list(packageName, reportType, year, month);\n return response.reports || [];\n}\n\nexport async function downloadReport(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<string> {\n const reports = await listReports(client, packageName, reportType, year, month);\n\n if (reports.length === 0) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n\n // Download the first report bucket (signed URI — no auth needed)\n const bucket = reports[0];\n if (!bucket) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n const uri = bucket.uri;\n const response = await fetch(uri);\n\n if (!response.ok) {\n throw new Error(\n `Failed to download report from signed URI: HTTP ${response.status}`,\n );\n }\n\n return response.text();\n}\n","import type { UsersApiClient, User, DeveloperPermission, Grant } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport const PERMISSION_PROPAGATION_WARNING =\n \"Note: Permission changes may take up to 48 hours to propagate.\";\n\nexport interface ListUsersOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listUsers(\n client: UsersApiClient,\n developerId: string,\n options?: ListUsersOptions,\n): Promise<{ users: User[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<User>(\n async (pageToken) => {\n const resp = await client.list(developerId, { pageToken, pageSize: options?.pageSize });\n return { items: resp.users || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { users: result.items, nextPageToken: result.nextPageToken };\n }\n const response = await client.list(developerId, options);\n return { users: response.users || [], nextPageToken: response.nextPageToken };\n}\n\nexport async function getUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<User> {\n return client.get(developerId, userId);\n}\n\nexport async function inviteUser(\n client: UsersApiClient,\n developerId: string,\n email: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const user: Partial<User> = { email };\n if (permissions) user.developerAccountPermission = permissions;\n if (grants) user.grants = grants;\n return client.create(developerId, user);\n}\n\nexport async function updateUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const updates: Partial<User> = {};\n const masks: string[] = [];\n\n if (permissions) {\n updates.developerAccountPermission = permissions;\n masks.push(\"developerAccountPermission\");\n }\n if (grants) {\n updates.grants = grants;\n masks.push(\"grants\");\n }\n\n const updateMask = masks.length > 0 ? masks.join(\",\") : undefined;\n return client.update(developerId, userId, updates, updateMask);\n}\n\nexport async function removeUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<void> {\n return client.delete(developerId, userId);\n}\n\nexport function parseGrantArg(grantStr: string): Grant {\n const colonIdx = grantStr.indexOf(\":\");\n if (colonIdx === -1) {\n throw new Error(\n `Invalid grant format \"${grantStr}\". Expected <packageName>:<PERMISSION>[,<PERMISSION>...]`,\n );\n }\n const packageName = grantStr.slice(0, colonIdx);\n const perms = grantStr.slice(colonIdx + 1).split(\",\") as DeveloperPermission[];\n return { packageName, appLevelPermissions: perms };\n}\n","import type { PlayApiClient, Testers } from \"@gpc-cli/api\";\nimport { readFile } from \"node:fs/promises\";\n\nexport async function listTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const testers = await client.testers.get(packageName, edit.id, track);\n return testers;\n } finally {\n await client.edits.delete(packageName, edit.id);\n }\n}\n\nexport async function addTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const existing = new Set(current.googleGroups || []);\n for (const email of groupEmails) {\n existing.add(email.trim());\n }\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: [...existing],\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function removeTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const toRemove = new Set(groupEmails.map((e) => e.trim()));\n const filtered = (current.googleGroups || []).filter(\n (g) => !toRemove.has(g),\n );\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: filtered,\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function importTestersFromCsv(\n client: PlayApiClient,\n packageName: string,\n track: string,\n csvPath: string,\n): Promise<{ added: number; testers: Testers }> {\n const content = await readFile(csvPath, \"utf-8\");\n const emails = content\n .split(/[,\\n\\r]+/)\n .map((e) => e.trim())\n .filter((e) => e.length > 0 && e.includes(\"@\"));\n\n if (emails.length === 0) {\n throw new Error(`No valid email addresses found in ${csvPath}.`);\n }\n\n const testers = await addTesters(client, packageName, track, emails);\n return { added: emails.length, testers };\n}\n","import { resolve, normalize } from \"node:path\";\n\n/**\n * Normalize and resolve a user-supplied path.\n * Prevents path traversal by normalizing `.` and `..` components.\n */\nexport function safePath(userPath: string): string {\n return resolve(normalize(userPath));\n}\n\n/**\n * Validate that a resolved path is within an expected base directory.\n * Returns the resolved path or throws if it escapes the base.\n */\nexport function safePathWithin(userPath: string, baseDir: string): string {\n const resolved = safePath(userPath);\n const base = safePath(baseDir);\n\n if (!resolved.startsWith(base + \"/\") && resolved !== base) {\n throw new Error(\n `Path \"${userPath}\" resolves outside the expected directory \"${baseDir}\"`,\n );\n }\n\n return resolved;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface ScaffoldOptions {\n name: string;\n dir: string;\n description?: string;\n}\n\nexport interface ScaffoldResult {\n dir: string;\n files: string[];\n}\n\n/**\n * Scaffold a new GPC plugin project.\n */\nexport async function scaffoldPlugin(options: ScaffoldOptions): Promise<ScaffoldResult> {\n const { name, dir, description = `GPC plugin: ${name}` } = options;\n\n // Ensure name follows convention\n const pluginName = name.startsWith(\"gpc-plugin-\") ? name : `gpc-plugin-${name}`;\n const shortName = pluginName.replace(/^gpc-plugin-/, \"\");\n\n const srcDir = join(dir, \"src\");\n const testDir = join(dir, \"tests\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n const files: string[] = [];\n\n // package.json\n const pkg = {\n name: pluginName,\n version: \"0.1.0\",\n description,\n type: \"module\",\n main: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n exports: {\n \".\": {\n import: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n },\n },\n files: [\"dist\"],\n scripts: {\n build: \"tsup src/index.ts --format esm --dts\",\n dev: \"tsup src/index.ts --format esm --dts --watch\",\n test: \"vitest run\",\n \"test:watch\": \"vitest\",\n },\n keywords: [\"gpc\", \"gpc-plugin\", \"google-play\"],\n license: \"MIT\",\n peerDependencies: {\n \"@gpc-cli/plugin-sdk\": \">=0.8.0\",\n },\n devDependencies: {\n \"@gpc-cli/plugin-sdk\": \"^0.8.0\",\n tsup: \"^8.0.0\",\n typescript: \"^5.0.0\",\n vitest: \"^3.0.0\",\n },\n };\n await writeFile(join(dir, \"package.json\"), JSON.stringify(pkg, null, 2) + \"\\n\");\n files.push(\"package.json\");\n\n // tsconfig.json\n const tsconfig = {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n declaration: true,\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"./dist\",\n rootDir: \"./src\",\n },\n include: [\"src\"],\n };\n await writeFile(join(dir, \"tsconfig.json\"), JSON.stringify(tsconfig, null, 2) + \"\\n\");\n files.push(\"tsconfig.json\");\n\n // src/index.ts\n const srcContent = `import { definePlugin } from \"@gpc-cli/plugin-sdk\";\nimport type { CommandEvent, CommandResult } from \"@gpc-cli/plugin-sdk\";\n\nexport const plugin = definePlugin({\n name: \"${pluginName}\",\n version: \"0.1.0\",\n\n register(hooks) {\n hooks.beforeCommand(async (event: CommandEvent) => {\n // Called before every gpc command\n // Example: log command usage, validate prerequisites, etc.\n });\n\n hooks.afterCommand(async (event: CommandEvent, result: CommandResult) => {\n // Called after every successful gpc command\n // Example: send notifications, update dashboards, etc.\n });\n\n // Uncomment to add custom commands:\n // hooks.registerCommands((registry) => {\n // registry.add({\n // name: \"${shortName}\",\n // description: \"${description}\",\n // action: async (args, opts) => {\n // console.log(\"Hello from ${pluginName}!\");\n // },\n // });\n // });\n },\n});\n`;\n await writeFile(join(srcDir, \"index.ts\"), srcContent);\n files.push(\"src/index.ts\");\n\n // tests/plugin.test.ts\n const testContent = `import { describe, it, expect, vi } from \"vitest\";\nimport { plugin } from \"../src/index\";\n\ndescribe(\"${pluginName}\", () => {\n it(\"has correct name and version\", () => {\n expect(plugin.name).toBe(\"${pluginName}\");\n expect(plugin.version).toBe(\"0.1.0\");\n });\n\n it(\"registers without errors\", () => {\n const hooks = {\n beforeCommand: vi.fn(),\n afterCommand: vi.fn(),\n onError: vi.fn(),\n beforeRequest: vi.fn(),\n afterResponse: vi.fn(),\n registerCommands: vi.fn(),\n };\n\n expect(() => plugin.register(hooks)).not.toThrow();\n });\n});\n`;\n await writeFile(join(testDir, \"plugin.test.ts\"), testContent);\n files.push(\"tests/plugin.test.ts\");\n\n return { dir, files };\n}\n","import { appendFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface AuditEntry {\n timestamp: string;\n command: string;\n app?: string;\n args: Record<string, unknown>;\n user?: string;\n success?: boolean;\n durationMs?: number;\n error?: string;\n}\n\nlet auditDir: string | null = null;\n\n/**\n * Initialize audit logging with a directory path.\n * Typically ~/.config/gpc/ or the XDG config dir.\n */\nexport function initAudit(configDir: string): void {\n auditDir = configDir;\n}\n\n/**\n * Write an audit log entry. Non-blocking — errors are silently ignored.\n */\nexport async function writeAuditLog(entry: AuditEntry): Promise<void> {\n if (!auditDir) return;\n\n try {\n await mkdir(auditDir, { recursive: true, mode: 0o700 });\n const logPath = join(auditDir, \"audit.log\");\n const redactedEntry = redactAuditArgs(entry);\n const line = JSON.stringify(redactedEntry) + \"\\n\";\n await appendFile(logPath, line, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(logPath, 0o600).catch(() => {});\n } catch {\n // Audit logging must never break the CLI\n }\n}\n\nconst SENSITIVE_ARG_KEYS = new Set([\n \"key\",\n \"keyFile\",\n \"key-file\",\n \"serviceAccount\",\n \"service-account\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n \"private_key\",\n \"client_secret\",\n]);\n\nfunction redactAuditArgs(entry: AuditEntry): AuditEntry {\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(entry.args)) {\n redacted[k] = SENSITIVE_ARG_KEYS.has(k) ? \"[REDACTED]\" : v;\n }\n return { ...entry, args: redacted };\n}\n\n/**\n * Convenience: create an audit entry for a write command.\n */\nexport function createAuditEntry(\n command: string,\n args: Record<string, unknown>,\n app?: string,\n): AuditEntry {\n return {\n timestamp: new Date().toISOString(),\n command,\n app,\n args,\n };\n}\n"],"mappings":"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","names":["stat","extname","formatSize","readFile","stat","readdir","readFile","stat","extname","join","stat","formatSize","stat","paginateAll","readdir","readFile","join","paginateAll","paginateAll","paginateAll","readFile","mkdir","writeFile","join","mkdir","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/output.ts","../src/plugins.ts","../src/commands/apps.ts","../src/utils/file-validation.ts","../src/commands/releases.ts","../src/utils/bcp47.ts","../src/utils/image-validation.ts","../src/utils/fastlane.ts","../src/commands/listings.ts","../src/utils/release-notes.ts","../src/commands/validate.ts","../src/commands/publish.ts","../src/commands/reviews.ts","../src/commands/vitals.ts","../src/commands/subscriptions.ts","../src/commands/iap.ts","../src/commands/purchases.ts","../src/commands/pricing.ts","../src/commands/reports.ts","../src/commands/users.ts","../src/commands/testers.ts","../src/utils/safe-path.ts","../src/commands/plugin-scaffold.ts","../src/audit.ts"],"sourcesContent":["export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\n\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import type { OutputFormat } from \"@gpc-cli/config\";\nimport process from \"node:process\";\n\nexport function detectOutputFormat(): OutputFormat {\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nexport function formatOutput(data: unknown, format: OutputFormat, redact = true): string {\n const safe = redact ? redactSensitive(data) : data;\n switch (format) {\n case \"json\":\n return formatJson(safe);\n case \"yaml\":\n return formatYaml(safe);\n case \"markdown\":\n return formatMarkdown(safe);\n case \"table\":\n return formatTable(safe);\n default:\n return formatJson(safe);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field redaction\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_KEYS = new Set([\n \"private_key\",\n \"privateKey\",\n \"private_key_id\",\n \"privateKeyId\",\n \"accessToken\",\n \"access_token\",\n \"refreshToken\",\n \"refresh_token\",\n \"client_secret\",\n \"clientSecret\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n]);\n\nconst REDACTED = \"[REDACTED]\";\n\n/** Recursively redact sensitive fields from data before output. */\nexport function redactSensitive(data: unknown): unknown {\n if (data === null || data === undefined) return data;\n\n if (typeof data === \"string\") return data;\n if (typeof data === \"number\" || typeof data === \"boolean\") return data;\n\n if (Array.isArray(data)) {\n return data.map((item) => redactSensitive(item));\n }\n\n if (typeof data === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (SENSITIVE_KEYS.has(key) && typeof value === \"string\") {\n result[key] = REDACTED;\n } else {\n result[key] = redactSensitive(value);\n }\n }\n return result;\n }\n\n return data;\n}\n\nfunction formatJson(data: unknown): string {\n return JSON.stringify(data, null, 2);\n}\n\nfunction formatYaml(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return \"null\";\n }\n\n if (typeof data === \"string\") {\n return data.includes(\"\\n\")\n ? `|\\n${data\n .split(\"\\n\")\n .map((l) => `${\" \".repeat(indent + 1)}${l}`)\n .join(\"\\n\")}`\n : data;\n }\n\n if (typeof data === \"number\" || typeof data === \"boolean\") {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return \"[]\";\n return data\n .map((item) => {\n const value = formatYaml(item, indent + 1);\n const prefix = `${\" \".repeat(indent)}- `;\n if (typeof item === \"object\" && item !== null && !Array.isArray(item)) {\n const lines = value.split(\"\\n\");\n return `${prefix}${lines[0]}\\n${lines\n .slice(1)\n .map((l) => `${\" \".repeat(indent)} ${l}`)\n .join(\"\\n\")}`;\n }\n return `${prefix}${value}`;\n })\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return \"{}\";\n return entries\n .map(([key, value]) => {\n if (typeof value === \"object\" && value !== null) {\n return `${\" \".repeat(indent)}${key}:\\n${formatYaml(value, indent + 1)}`;\n }\n return `${\" \".repeat(indent)}${key}: ${formatYaml(value, indent)}`;\n })\n .join(\"\\n\");\n }\n\n return String(data);\n}\n\nfunction formatTable(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\" \");\n const body = rows\n .map((row) => keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" \"))\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction formatMarkdown(data: unknown): string {\n const rows = toRows(data);\n if (rows.length === 0) return \"\";\n\n const firstRow = rows[0];\n if (!firstRow) return \"\";\n const keys = Object.keys(firstRow);\n if (keys.length === 0) return \"\";\n\n const widths = keys.map((key) =>\n Math.max(key.length, ...rows.map((row) => String(row[key] ?? \"\").length)),\n );\n\n const header = `| ${keys.map((key, i) => key.padEnd(widths[i] ?? 0)).join(\" | \")} |`;\n const separator = `| ${widths.map((w) => \"-\".repeat(w)).join(\" | \")} |`;\n const body = rows\n .map(\n (row) =>\n `| ${keys.map((key, i) => String(row[key] ?? \"\").padEnd(widths[i] ?? 0)).join(\" | \")} |`,\n )\n .join(\"\\n\");\n\n return `${header}\\n${separator}\\n${body}`;\n}\n\nfunction toRows(data: unknown): Record<string, unknown>[] {\n if (Array.isArray(data)) {\n return data.filter(\n (item): item is Record<string, unknown> => typeof item === \"object\" && item !== null,\n );\n }\n if (typeof data === \"object\" && data !== null) {\n return [data as Record<string, unknown>];\n }\n return [];\n}\n","import type {\n GpcPlugin,\n PluginHooks,\n BeforeCommandHandler,\n AfterCommandHandler,\n ErrorHandler,\n BeforeRequestHandler,\n AfterResponseHandler,\n CommandEvent,\n CommandResult,\n PluginError,\n PluginCommand,\n PluginManifest,\n PluginPermission,\n RequestEvent,\n ResponseEvent,\n} from \"@gpc-cli/plugin-sdk\";\nimport { GpcError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Plugin Manager — orchestrates discovery, loading, and lifecycle\n// ---------------------------------------------------------------------------\n\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private beforeHandlers: BeforeCommandHandler[] = [];\n private afterHandlers: AfterCommandHandler[] = [];\n private errorHandlers: ErrorHandler[] = [];\n private beforeRequestHandlers: BeforeRequestHandler[] = [];\n private afterResponseHandlers: AfterResponseHandler[] = [];\n private registeredCommands: PluginCommand[] = [];\n\n /** Load and register a plugin */\n async load(plugin: GpcPlugin, manifest?: PluginManifest): Promise<void> {\n const isTrusted = manifest?.trusted ?? plugin.name.startsWith(\"@gpc-cli/\");\n\n if (!isTrusted && manifest?.permissions) {\n validatePermissions(manifest.permissions);\n }\n\n const hooks = createHooks(\n this.beforeHandlers,\n this.afterHandlers,\n this.errorHandlers,\n this.beforeRequestHandlers,\n this.afterResponseHandlers,\n this.registeredCommands,\n );\n\n await plugin.register(hooks);\n\n this.plugins.push({\n name: plugin.name,\n version: plugin.version,\n trusted: isTrusted,\n });\n }\n\n /** Run all beforeCommand handlers */\n async runBeforeCommand(event: CommandEvent): Promise<void> {\n for (const handler of this.beforeHandlers) {\n await handler(event);\n }\n }\n\n /** Run all afterCommand handlers */\n async runAfterCommand(event: CommandEvent, result: CommandResult): Promise<void> {\n for (const handler of this.afterHandlers) {\n await handler(event, result);\n }\n }\n\n /** Run all onError handlers */\n async runOnError(event: CommandEvent, error: PluginError): Promise<void> {\n for (const handler of this.errorHandlers) {\n try {\n await handler(event, error);\n } catch {\n // Don't let error handlers crash the process\n }\n }\n }\n\n /** Run all beforeRequest handlers */\n async runBeforeRequest(event: RequestEvent): Promise<void> {\n for (const handler of this.beforeRequestHandlers) {\n try {\n await handler(event);\n } catch {\n // Don't let request hooks block API calls\n }\n }\n }\n\n /** Run all afterResponse handlers */\n async runAfterResponse(event: RequestEvent, response: ResponseEvent): Promise<void> {\n for (const handler of this.afterResponseHandlers) {\n try {\n await handler(event, response);\n } catch {\n // Don't let response hooks crash the process\n }\n }\n }\n\n /** Get commands registered by plugins */\n getRegisteredCommands(): PluginCommand[] {\n return [...this.registeredCommands];\n }\n\n /** Get list of loaded plugins */\n getLoadedPlugins(): LoadedPlugin[] {\n return [...this.plugins];\n }\n\n /** Whether any request/response hooks are registered */\n hasRequestHooks(): boolean {\n return this.beforeRequestHandlers.length > 0 || this.afterResponseHandlers.length > 0;\n }\n\n /** Reset (for testing) */\n reset(): void {\n this.plugins = [];\n this.beforeHandlers = [];\n this.afterHandlers = [];\n this.errorHandlers = [];\n this.beforeRequestHandlers = [];\n this.afterResponseHandlers = [];\n this.registeredCommands = [];\n }\n}\n\nexport interface LoadedPlugin {\n name: string;\n version: string;\n trusted: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Hook factory\n// ---------------------------------------------------------------------------\n\nfunction createHooks(\n beforeHandlers: BeforeCommandHandler[],\n afterHandlers: AfterCommandHandler[],\n errorHandlers: ErrorHandler[],\n beforeRequestHandlers: BeforeRequestHandler[],\n afterResponseHandlers: AfterResponseHandler[],\n registeredCommands: PluginCommand[],\n): PluginHooks {\n return {\n beforeCommand(handler) {\n beforeHandlers.push(handler);\n },\n afterCommand(handler) {\n afterHandlers.push(handler);\n },\n onError(handler) {\n errorHandlers.push(handler);\n },\n beforeRequest(handler) {\n beforeRequestHandlers.push(handler);\n },\n afterResponse(handler) {\n afterResponseHandlers.push(handler);\n },\n registerCommands(registrar) {\n const registry = {\n add(cmd: PluginCommand) {\n registeredCommands.push(cmd);\n },\n };\n registrar(registry);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Permission validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PERMISSIONS: ReadonlySet<string> = new Set<PluginPermission>([\n \"read:config\",\n \"write:config\",\n \"read:auth\",\n \"api:read\",\n \"api:write\",\n \"commands:register\",\n \"hooks:beforeCommand\",\n \"hooks:afterCommand\",\n \"hooks:onError\",\n \"hooks:beforeRequest\",\n \"hooks:afterResponse\",\n]);\n\nfunction validatePermissions(permissions: PluginPermission[]): void {\n for (const perm of permissions) {\n if (!VALID_PERMISSIONS.has(perm)) {\n throw new GpcError(\n `Unknown plugin permission: \"${perm}\"`,\n \"PLUGIN_INVALID_PERMISSION\",\n 10,\n `Valid permissions: ${[...VALID_PERMISSIONS].join(\", \")}`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin discovery\n// ---------------------------------------------------------------------------\n\nexport interface DiscoverPluginsOptions {\n /** Plugin names from config file */\n configPlugins?: string[];\n\n /** Working directory for node_modules scanning */\n cwd?: string;\n}\n\n/**\n * Discover plugins from multiple sources:\n * 1. Explicit config: gpc.config.ts → plugins: [...]\n * 2. Convention: node_modules/@gpc-cli/plugin-*\n * 3. Convention: node_modules/gpc-plugin-*\n */\nexport async function discoverPlugins(options?: DiscoverPluginsOptions): Promise<GpcPlugin[]> {\n const plugins: GpcPlugin[] = [];\n const seen = new Set<string>();\n\n // Source 1: Explicit config plugins\n if (options?.configPlugins) {\n for (const name of options.configPlugins) {\n if (seen.has(name)) continue;\n try {\n const mod = await import(name);\n const plugin = resolvePlugin(mod);\n if (plugin) {\n plugins.push(plugin);\n seen.add(name);\n }\n } catch {\n // Plugin not found — skip silently\n }\n }\n }\n\n return plugins;\n}\n\n/**\n * Resolve a plugin from a module.\n * Supports: default export, named `plugin` export, or the module itself as a plugin.\n */\nfunction resolvePlugin(mod: unknown): GpcPlugin | undefined {\n if (!mod || typeof mod !== \"object\") return undefined;\n\n const m = mod as Record<string, unknown>;\n\n // Check default export\n if (isPlugin(m[\"default\"])) return m[\"default\"];\n\n // Check named `plugin` export\n if (isPlugin(m[\"plugin\"])) return m[\"plugin\"];\n\n // Check if module itself is a plugin\n if (isPlugin(m)) return m as unknown as GpcPlugin;\n\n return undefined;\n}\n\nfunction isPlugin(obj: unknown): obj is GpcPlugin {\n if (!obj || typeof obj !== \"object\") return false;\n const p = obj as Record<string, unknown>;\n return (\n typeof p[\"name\"] === \"string\" &&\n typeof p[\"version\"] === \"string\" &&\n typeof p[\"register\"] === \"function\"\n );\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\n\nexport interface AppInfo {\n packageName: string;\n title?: string;\n defaultLanguage?: string;\n contactEmail?: string;\n}\n\nexport async function getAppInfo(client: PlayApiClient, packageName: string): Promise<AppInfo> {\n // Create an edit to read app details (Google Play requires an edit context)\n const edit = await client.edits.insert(packageName);\n try {\n const details = await client.details.get(packageName, edit.id);\n // Delete the edit since we're only reading\n await client.edits.delete(packageName, edit.id);\n return {\n packageName,\n title: details.title,\n defaultLanguage: details.defaultLanguage,\n contactEmail: details.contactEmail,\n };\n } catch (error) {\n // Clean up edit on failure\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 150 * 1024 * 1024; // 150 MB\nconst MAX_AAB_SIZE = 500 * 1024 * 1024; // 500 MB (Play Store limit for AABs)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 150 MB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 500 MB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes (ZIP format — both AAB and APK are ZIP-based)\n if (sizeBytes > 0) {\n try {\n const fd = await readFile(filePath, { flag: \"r\" });\n const header = fd.subarray(0, 4);\n\n if (!header.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, Release, Track } from \"@gpc-cli/api\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n },\n): Promise<UploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n if (!validation.valid) {\n throw new Error(`File validation failed:\\n${validation.errors.join(\"\\n\")}`);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n // Upload the bundle\n const bundle = await client.bundles.upload(packageName, edit.id, filePath);\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n );\n }\n\n // Create release and assign to track\n const release: Release = {\n versionCodes: [String(bundle.versionCode)],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n // Validate and commit\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: { userFraction?: number; releaseNotes?: { language: string; text: string }[] },\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active release found on track \"${fromTrack}\"`);\n }\n\n // Create release on target track\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.userFraction ? \"inProgress\" : \"completed\") as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new Error(`No active rollout found on track \"${track}\"`);\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction) throw new Error(\"--to <percentage> is required for rollout increase\");\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","export const GOOGLE_PLAY_LANGUAGES: string[] = [\n \"af\",\n \"am\",\n \"ar\",\n \"hy-AM\",\n \"az-AZ\",\n \"eu-ES\",\n \"be\",\n \"bn-BD\",\n \"bg\",\n \"my-MM\",\n \"ca\",\n \"zh-HK\",\n \"zh-CN\",\n \"zh-TW\",\n \"hr\",\n \"cs-CZ\",\n \"da-DK\",\n \"nl-NL\",\n \"en-AU\",\n \"en-CA\",\n \"en-IN\",\n \"en-SG\",\n \"en-GB\",\n \"en-US\",\n \"et\",\n \"fil\",\n \"fi-FI\",\n \"fr-FR\",\n \"fr-CA\",\n \"gl-ES\",\n \"ka-GE\",\n \"de-DE\",\n \"el-GR\",\n \"gu\",\n \"iw-IL\",\n \"hi-IN\",\n \"hu-HU\",\n \"is-IS\",\n \"id\",\n \"it-IT\",\n \"ja-JP\",\n \"kn-IN\",\n \"kk\",\n \"km-KH\",\n \"ko-KR\",\n \"ky-KG\",\n \"lo-LA\",\n \"lv\",\n \"lt\",\n \"mk-MK\",\n \"ms\",\n \"ms-MY\",\n \"ml-IN\",\n \"mr-IN\",\n \"mn-MN\",\n \"ne-NP\",\n \"no-NO\",\n \"fa\",\n \"pl-PL\",\n \"pt-BR\",\n \"pt-PT\",\n \"pa\",\n \"ro\",\n \"rm\",\n \"ru-RU\",\n \"sr\",\n \"si-LK\",\n \"sk\",\n \"sl\",\n \"es-419\",\n \"es-ES\",\n \"es-US\",\n \"sw\",\n \"sv-SE\",\n \"ta-IN\",\n \"te-IN\",\n \"th\",\n \"tr-TR\",\n \"uk\",\n \"ur\",\n \"vi\",\n \"zu\",\n];\n\nexport function isValidBcp47(tag: string): boolean {\n return GOOGLE_PLAY_LANGUAGES.includes(tag);\n}\n","import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface ImageValidationResult {\n valid: boolean;\n warnings: string[];\n errors: string[];\n}\n\n// Google Play image size limits\nconst IMAGE_SIZE_LIMITS: Record<string, { maxBytes: number; label: string }> = {\n icon: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n featureGraphic: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n tvBanner: { maxBytes: 1024 * 1024, label: \"1 MB\" },\n phoneScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n sevenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tenInchScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n tvScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n wearScreenshots: { maxBytes: 8 * 1024 * 1024, label: \"8 MB\" },\n};\n\nconst VALID_EXTENSIONS = new Set([\".png\", \".jpg\", \".jpeg\"]);\nconst LARGE_IMAGE_THRESHOLD = 2 * 1024 * 1024; // 2 MB\n\nexport async function validateImage(\n filePath: string,\n imageType?: string,\n): Promise<ImageValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n if (!VALID_EXTENSIONS.has(ext)) {\n errors.push(`Unsupported image format \"${ext}\". Use PNG or JPEG.`);\n }\n\n // Check file exists and size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"Image file is empty (0 bytes)\");\n }\n } catch {\n errors.push(`Image file not found: ${filePath}`);\n return { valid: false, errors, warnings };\n }\n\n // Check size limits per image type\n if (imageType && sizeBytes > 0) {\n const limit = IMAGE_SIZE_LIMITS[imageType];\n if (limit && sizeBytes > limit.maxBytes) {\n errors.push(`Image exceeds ${limit.label} limit for ${imageType} (${formatSize(sizeBytes)})`);\n }\n }\n\n // Warn about large images\n if (sizeBytes > LARGE_IMAGE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large image (${formatSize(sizeBytes)}). Consider optimizing for faster upload and better store performance.`,\n );\n }\n\n // PNG optimization warning\n if (ext === \".png\" && sizeBytes > 512 * 1024) {\n warnings.push(\n \"PNG file is over 512 KB. Consider compressing with tools like pngquant or optipng.\",\n );\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import { readFile, writeFile, mkdir, readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Listing } from \"@gpc-cli/api\";\n\nconst FILE_MAP: Record<string, keyof Omit<Listing, \"language\">> = {\n \"title.txt\": \"title\",\n \"short_description.txt\": \"shortDescription\",\n \"full_description.txt\": \"fullDescription\",\n \"video.txt\": \"video\",\n};\n\nconst FIELD_TO_FILE: Record<string, string> = Object.fromEntries(\n Object.entries(FILE_MAP).map(([file, field]) => [field, file]),\n);\n\nexport interface ListingDiff {\n language: string;\n field: string;\n local: string;\n remote: string;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readListingsFromDir(dir: string): Promise<Listing[]> {\n const listings: Listing[] = [];\n\n if (!(await exists(dir))) return listings;\n\n const entries = await readdir(dir);\n for (const lang of entries) {\n const langDir = join(dir, lang);\n const langStat = await stat(langDir);\n if (!langStat.isDirectory()) continue;\n\n const listing: Listing = {\n language: lang,\n title: \"\",\n shortDescription: \"\",\n fullDescription: \"\",\n };\n\n for (const [fileName, field] of Object.entries(FILE_MAP)) {\n const filePath = join(langDir, fileName);\n if (await exists(filePath)) {\n const content = await readFile(filePath, \"utf-8\");\n (listing as unknown as Record<string, string>)[field] = content.trimEnd();\n }\n }\n\n listings.push(listing);\n }\n\n return listings;\n}\n\nexport async function writeListingsToDir(dir: string, listings: Listing[]): Promise<void> {\n for (const listing of listings) {\n const langDir = join(dir, listing.language);\n await mkdir(langDir, { recursive: true });\n\n for (const [field, fileName] of Object.entries(FIELD_TO_FILE)) {\n const value = (listing as unknown as Record<string, string>)[field];\n if (value !== undefined && value !== \"\") {\n await writeFile(join(langDir, fileName), value + \"\\n\", \"utf-8\");\n }\n }\n }\n}\n\nexport function diffListings(local: Listing[], remote: Listing[]): ListingDiff[] {\n const diffs: ListingDiff[] = [];\n const remoteMap = new Map(remote.map((l) => [l.language, l]));\n const localMap = new Map(local.map((l) => [l.language, l]));\n\n // Check all local listings against remote\n for (const localListing of local) {\n const remoteListing = remoteMap.get(localListing.language);\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const localVal = (\n (localListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n const remoteVal = remoteListing\n ? ((remoteListing as unknown as Record<string, string>)[field] ?? \"\").toString()\n : \"\";\n if (localVal !== remoteVal) {\n diffs.push({\n language: localListing.language,\n field,\n local: localVal,\n remote: remoteVal,\n });\n }\n }\n }\n\n // Check for remote-only languages\n for (const remoteListing of remote) {\n if (!localMap.has(remoteListing.language)) {\n for (const [field] of Object.entries(FIELD_TO_FILE)) {\n const remoteVal = (\n (remoteListing as unknown as Record<string, string>)[field] ?? \"\"\n ).toString();\n if (remoteVal) {\n diffs.push({\n language: remoteListing.language,\n field,\n local: \"\",\n remote: remoteVal,\n });\n }\n }\n }\n }\n\n return diffs;\n}\n","import type {\n PlayApiClient,\n Listing,\n Image,\n ImageType,\n AppDetails,\n CountryAvailability,\n} from \"@gpc-cli/api\";\nimport { isValidBcp47 } from \"../utils/bcp47.js\";\nimport { validateImage } from \"../utils/image-validation.js\";\nimport { readListingsFromDir, writeListingsToDir, diffListings } from \"../utils/fastlane.js\";\nimport type { ListingDiff } from \"../utils/fastlane.js\";\n\nexport interface ListingsResult {\n listings: Listing[];\n}\n\nexport interface PushResult {\n updated: number;\n languages: string[];\n}\n\nexport interface DryRunResult {\n diffs: ListingDiff[];\n}\n\nfunction validateLanguage(lang: string): void {\n if (!isValidBcp47(lang)) {\n throw new Error(`Invalid language tag \"${lang}\". Must be a valid Google Play BCP 47 code.`);\n }\n}\n\nexport async function getListings(\n client: PlayApiClient,\n packageName: string,\n language?: string,\n): Promise<Listing[]> {\n const edit = await client.edits.insert(packageName);\n try {\n let listings: Listing[];\n if (language) {\n validateLanguage(language);\n const listing = await client.listings.get(packageName, edit.id, language);\n listings = [listing];\n } else {\n listings = await client.listings.list(packageName, edit.id);\n }\n await client.edits.delete(packageName, edit.id);\n return listings;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n data: Partial<Omit<Listing, \"language\">>,\n): Promise<Listing> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const listing = await client.listings.patch(packageName, edit.id, language, data);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return listing;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteListing(\n client: PlayApiClient,\n packageName: string,\n language: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.listings.delete(packageName, edit.id, language);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pullListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n): Promise<ListingsResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const listings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n await writeListingsToDir(dir, listings);\n return { listings };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function pushListings(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<PushResult | DryRunResult> {\n const localListings = await readListingsFromDir(dir);\n\n if (localListings.length === 0) {\n throw new Error(`No listings found in directory \"${dir}\"`);\n }\n\n // Validate all languages\n for (const listing of localListings) {\n validateLanguage(listing.language);\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n if (options?.dryRun) {\n const remoteListings = await client.listings.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n const diffs = diffListings(localListings, remoteListings);\n return { diffs };\n }\n\n for (const listing of localListings) {\n const { language, ...data } = listing;\n await client.listings.update(packageName, edit.id, language, data);\n }\n\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n\n return {\n updated: localListings.length,\n languages: localListings.map((l) => l.language),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listImages(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n): Promise<Image[]> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n const images = await client.images.list(packageName, edit.id, language, imageType);\n await client.edits.delete(packageName, edit.id);\n return images;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n): Promise<Image> {\n validateLanguage(language);\n\n // Validate image before upload\n const imageCheck = await validateImage(filePath, imageType);\n if (!imageCheck.valid) {\n throw new Error(`Image validation failed: ${imageCheck.errors.join(\"; \")}`);\n }\n if (imageCheck.warnings.length > 0) {\n for (const w of imageCheck.warnings) {\n console.warn(`Warning: ${w}`);\n }\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return image;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function deleteImage(\n client: PlayApiClient,\n packageName: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n): Promise<void> {\n validateLanguage(language);\n const edit = await client.edits.insert(packageName);\n try {\n await client.images.delete(packageName, edit.id, language, imageType, imageId);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getCountryAvailability(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<CountryAvailability> {\n const edit = await client.edits.insert(packageName);\n try {\n const availability = await client.countryAvailability.get(packageName, edit.id, track);\n await client.edits.delete(packageName, edit.id);\n return availability;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateAppDetails(\n client: PlayApiClient,\n packageName: string,\n details: Partial<AppDetails>,\n): Promise<AppDetails> {\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.details.patch(packageName, edit.id, details);\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { extname, basename, join } from \"node:path\";\n\nexport interface ReleaseNote {\n language: string;\n text: string;\n}\n\nexport interface ReleaseNotesValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nconst MAX_NOTES_LENGTH = 500;\n\nexport async function readReleaseNotesFromDir(dir: string): Promise<ReleaseNote[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new Error(`Release notes directory not found: ${dir}`);\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n if (extname(entry) !== \".txt\") continue;\n\n const language = basename(entry, \".txt\");\n const filePath = join(dir, entry);\n\n const stats = await stat(filePath);\n if (!stats.isFile()) continue;\n\n const text = (await readFile(filePath, \"utf-8\")).trim();\n if (text.length === 0) continue;\n\n notes.push({ language, text });\n }\n\n return notes;\n}\n\nexport function validateReleaseNotes(notes: ReleaseNote[]): ReleaseNotesValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const seen = new Set<string>();\n for (const note of notes) {\n if (seen.has(note.language)) {\n errors.push(`Duplicate language code: ${note.language}`);\n }\n seen.add(note.language);\n\n if (note.text.length > MAX_NOTES_LENGTH) {\n errors.push(\n `Release notes for \"${note.language}\" exceed ${MAX_NOTES_LENGTH} chars (${note.text.length} chars)`,\n );\n }\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n","import { stat } from \"node:fs/promises\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { readReleaseNotesFromDir, validateReleaseNotes } from \"../utils/release-notes.js\";\n\nexport interface ValidateOptions {\n filePath: string;\n mappingFile?: string;\n track?: string;\n notes?: { language: string; text: string }[];\n notesDir?: string;\n}\n\nexport interface ValidateCheck {\n name: string;\n passed: boolean;\n message: string;\n}\n\nexport interface ValidateResult {\n valid: boolean;\n checks: ValidateCheck[];\n}\n\nconst STANDARD_TRACKS = new Set([\n \"internal\",\n \"alpha\",\n \"beta\",\n \"production\",\n // Form factor tracks\n \"wear:internal\",\n \"wear:alpha\",\n \"wear:beta\",\n \"wear:production\",\n \"automotive:internal\",\n \"automotive:alpha\",\n \"automotive:beta\",\n \"automotive:production\",\n \"tv:internal\",\n \"tv:alpha\",\n \"tv:beta\",\n \"tv:production\",\n \"android_xr:internal\",\n \"android_xr:alpha\",\n \"android_xr:beta\",\n \"android_xr:production\",\n]);\nconst TRACK_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_:-]*$/;\n\nexport async function validatePreSubmission(options: ValidateOptions): Promise<ValidateResult> {\n const checks: ValidateCheck[] = [];\n\n // 1. File validation\n const fileResult = await validateUploadFile(options.filePath);\n checks.push({\n name: \"file\",\n passed: fileResult.valid,\n message: fileResult.valid\n ? `Valid ${fileResult.fileType} file (${formatSize(fileResult.sizeBytes)})`\n : fileResult.errors.join(\"; \"),\n });\n\n // 2. Mapping file\n if (options.mappingFile) {\n try {\n const stats = await stat(options.mappingFile);\n checks.push({\n name: \"mapping\",\n passed: stats.isFile(),\n message: stats.isFile()\n ? `Mapping file found (${formatSize(stats.size)})`\n : \"Mapping path is not a file\",\n });\n } catch {\n checks.push({\n name: \"mapping\",\n passed: false,\n message: `Mapping file not found: ${options.mappingFile}`,\n });\n }\n }\n\n // 3. Track validation\n if (options.track) {\n const isValid = STANDARD_TRACKS.has(options.track) || TRACK_PATTERN.test(options.track);\n checks.push({\n name: \"track\",\n passed: isValid,\n message: isValid\n ? `Track \"${options.track}\" is valid`\n : `Invalid track name \"${options.track}\". Use: internal, alpha, beta, production, or a custom track ID`,\n });\n }\n\n // 4. Release notes validation\n let resolvedNotes = options.notes;\n if (options.notesDir) {\n try {\n resolvedNotes = await readReleaseNotesFromDir(options.notesDir);\n checks.push({\n name: \"notes-dir\",\n passed: true,\n message: `Read release notes for ${resolvedNotes.length} language(s)`,\n });\n } catch (err) {\n checks.push({\n name: \"notes-dir\",\n passed: false,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n if (resolvedNotes && resolvedNotes.length > 0) {\n const notesResult = validateReleaseNotes(resolvedNotes);\n checks.push({\n name: \"notes\",\n passed: notesResult.valid,\n message: notesResult.valid\n ? `Release notes valid (${resolvedNotes.length} language(s))`\n : notesResult.errors.join(\"; \"),\n });\n }\n\n return {\n valid: checks.every((c) => c.passed),\n checks,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n","import type { PlayApiClient } from \"@gpc-cli/api\";\nimport { uploadRelease } from \"./releases.js\";\nimport type { UploadResult } from \"./releases.js\";\nimport { validatePreSubmission } from \"./validate.js\";\nimport type { ValidateResult } from \"./validate.js\";\nimport { readReleaseNotesFromDir } from \"../utils/release-notes.js\";\n\nexport interface PublishOptions {\n track?: string;\n rolloutPercent?: number;\n notes?: string;\n notesDir?: string;\n releaseName?: string;\n mappingFile?: string;\n}\n\nexport interface PublishResult {\n validation: ValidateResult;\n upload?: UploadResult;\n}\n\nexport async function publish(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: PublishOptions,\n): Promise<PublishResult> {\n // Resolve release notes\n let releaseNotes: { language: string; text: string }[] | undefined;\n if (options.notesDir) {\n releaseNotes = await readReleaseNotesFromDir(options.notesDir);\n } else if (options.notes) {\n releaseNotes = [{ language: \"en-US\", text: options.notes }];\n }\n\n // Validate\n const validation = await validatePreSubmission({\n filePath,\n mappingFile: options.mappingFile,\n track: options.track || \"internal\",\n notes: releaseNotes,\n });\n\n if (!validation.valid) {\n return { validation };\n }\n\n // Upload\n const upload = await uploadRelease(client, packageName, filePath, {\n track: options.track || \"internal\",\n userFraction: options.rolloutPercent ? options.rolloutPercent / 100 : undefined,\n releaseNotes,\n releaseName: options.releaseName,\n mappingFile: options.mappingFile,\n });\n\n return { validation, upload };\n}\n","import type { PlayApiClient, Review, ReviewsListOptions, ReviewReplyResponse } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ReviewsFilterOptions {\n stars?: number;\n language?: string;\n since?: string;\n translationLanguage?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport interface ReviewExportOptions extends ReviewsFilterOptions {\n format?: \"json\" | \"csv\";\n}\n\nexport async function listReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewsFilterOptions,\n): Promise<Review[]> {\n const apiOptions: ReviewsListOptions = {};\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n if (options?.maxResults) apiOptions.maxResults = options.maxResults;\n\n const response = await client.reviews.list(packageName, apiOptions);\n let reviews = response.reviews || [];\n\n // Client-side filters\n if (options?.stars !== undefined) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && userComment.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n reviews = reviews.filter((r) => {\n const userComment = r.comments?.[0]?.userComment;\n return userComment && Number(userComment.lastModified.seconds) >= sinceTime;\n });\n }\n\n return reviews;\n}\n\nexport async function getReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n translationLanguage?: string,\n): Promise<Review> {\n return client.reviews.get(packageName, reviewId, translationLanguage);\n}\n\nconst MAX_REPLY_LENGTH = 350;\n\nexport async function replyToReview(\n client: PlayApiClient,\n packageName: string,\n reviewId: string,\n replyText: string,\n): Promise<ReviewReplyResponse> {\n if (replyText.length > MAX_REPLY_LENGTH) {\n throw new Error(\n `Reply text exceeds ${MAX_REPLY_LENGTH} characters (${replyText.length}). Google Play limits replies to ${MAX_REPLY_LENGTH} characters.`,\n );\n }\n if (replyText.length === 0) {\n throw new Error(\"Reply text cannot be empty.\");\n }\n return client.reviews.reply(packageName, reviewId, replyText);\n}\n\nexport async function exportReviews(\n client: PlayApiClient,\n packageName: string,\n options?: ReviewExportOptions,\n): Promise<string> {\n const { items: allReviews } = await paginateAll<Review>(async (pageToken) => {\n const apiOptions: ReviewsListOptions = { token: pageToken };\n if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;\n const response = await client.reviews.list(packageName, apiOptions);\n return {\n items: response.reviews || [],\n nextPageToken: response.tokenPagination?.nextPageToken,\n };\n });\n\n let filtered = allReviews;\n\n if (options?.stars !== undefined) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && uc.starRating === options.stars;\n });\n }\n\n if (options?.language) {\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc?.reviewerLanguage === options.language;\n });\n }\n\n if (options?.since) {\n const sinceTime = new Date(options.since).getTime() / 1000;\n filtered = filtered.filter((r) => {\n const uc = r.comments?.[0]?.userComment;\n return uc && Number(uc.lastModified.seconds) >= sinceTime;\n });\n }\n\n if (options?.format === \"csv\") {\n return reviewsToCsv(filtered);\n }\n\n return JSON.stringify(filtered, null, 2);\n}\n\nfunction reviewsToCsv(reviews: Review[]): string {\n const header = \"reviewId,authorName,starRating,text,language,date,device,appVersionName\";\n const rows = reviews.map((r) => {\n const uc = r.comments?.[0]?.userComment;\n const fields = [\n r.reviewId,\n csvEscape(r.authorName),\n uc?.starRating ?? \"\",\n csvEscape(uc?.text ?? \"\"),\n uc?.reviewerLanguage ?? \"\",\n uc ? new Date(Number(uc.lastModified.seconds) * 1000).toISOString() : \"\",\n csvEscape(uc?.device ?? \"\"),\n csvEscape(uc?.appVersionName ?? \"\"),\n ];\n return fields.join(\",\");\n });\n return [header, ...rows].join(\"\\n\");\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(\",\") || value.includes('\"') || value.includes(\"\\n\")) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","import type {\n ReportingApiClient,\n VitalsMetricSet,\n MetricSetQuery,\n MetricSetResponse,\n MetricRow,\n AnomalyDetectionResponse,\n ErrorIssuesResponse,\n ReportingDimension,\n ReportingAggregation,\n} from \"@gpc-cli/api\";\n\nexport interface VitalsQueryOptions {\n dimension?: ReportingDimension;\n days?: number;\n aggregation?: ReportingAggregation;\n}\n\nexport interface VitalsOverview {\n crashRate?: MetricRow[];\n anrRate?: MetricRow[];\n slowStartRate?: MetricRow[];\n slowRenderingRate?: MetricRow[];\n excessiveWakeupRate?: MetricRow[];\n stuckWakelockRate?: MetricRow[];\n}\n\nexport interface ThresholdResult {\n breached: boolean;\n value: number | undefined;\n threshold: number;\n}\n\nfunction buildQuery(options?: VitalsQueryOptions): MetricSetQuery {\n const query: MetricSetQuery = {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n };\n\n if (options?.dimension) {\n query.dimensions = [options.dimension];\n }\n\n if (options?.days) {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - options.days);\n query.timelineSpec = {\n aggregationPeriod: options.aggregation ?? \"DAILY\",\n startTime: {\n year: start.getFullYear(),\n month: start.getMonth() + 1,\n day: start.getDate(),\n },\n endTime: {\n year: end.getFullYear(),\n month: end.getMonth() + 1,\n day: end.getDate(),\n },\n };\n }\n\n return query;\n}\n\nasync function queryMetric(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n const query = buildQuery(options);\n return reporting.queryMetricSet(packageName, metricSet, query);\n}\n\nexport async function getVitalsOverview(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<VitalsOverview> {\n const metricSets: [VitalsMetricSet, keyof VitalsOverview][] = [\n [\"vitals.crashrate\", \"crashRate\"],\n [\"vitals.anrrate\", \"anrRate\"],\n [\"vitals.slowstartrate\", \"slowStartRate\"],\n [\"vitals.slowrenderingrate\", \"slowRenderingRate\"],\n [\"vitals.excessivewakeuprate\", \"excessiveWakeupRate\"],\n [\"vitals.stuckbackgroundwakelockrate\", \"stuckWakelockRate\"],\n ];\n\n const results = await Promise.allSettled(\n metricSets.map(([metric]) =>\n reporting.queryMetricSet(packageName, metric, {\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n }),\n ),\n );\n\n const overview: VitalsOverview = {};\n for (let i = 0; i < metricSets.length; i++) {\n const entry = metricSets[i];\n if (!entry) continue;\n const key = entry[1];\n const result = results[i];\n if (!result) continue;\n if (result.status === \"fulfilled\") {\n overview[key] = result.value.rows || [];\n }\n }\n\n return overview;\n}\n\nexport async function getVitalsCrashes(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.crashrate\", options);\n}\n\nexport async function getVitalsAnr(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.anrrate\", options);\n}\n\nexport async function getVitalsStartup(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowstartrate\", options);\n}\n\nexport async function getVitalsRendering(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.slowrenderingrate\", options);\n}\n\nexport async function getVitalsBattery(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.excessivewakeuprate\", options);\n}\n\nexport async function getVitalsMemory(\n reporting: ReportingApiClient,\n packageName: string,\n options?: VitalsQueryOptions,\n): Promise<MetricSetResponse> {\n return queryMetric(reporting, packageName, \"vitals.stuckbackgroundwakelockrate\", options);\n}\n\nexport async function getVitalsAnomalies(\n reporting: ReportingApiClient,\n packageName: string,\n): Promise<AnomalyDetectionResponse> {\n return reporting.getAnomalies(packageName);\n}\n\nexport async function searchVitalsErrors(\n reporting: ReportingApiClient,\n packageName: string,\n options?: { filter?: string; maxResults?: number },\n): Promise<ErrorIssuesResponse> {\n return reporting.searchErrorIssues(packageName, options?.filter, options?.maxResults);\n}\n\nexport interface VitalsTrendComparison {\n metric: string;\n current: number | undefined;\n previous: number | undefined;\n changePercent: number | undefined;\n direction: \"improved\" | \"degraded\" | \"unchanged\" | \"unknown\";\n}\n\nexport async function compareVitalsTrend(\n reporting: ReportingApiClient,\n packageName: string,\n metricSet: VitalsMetricSet,\n days: number = 7,\n): Promise<VitalsTrendComparison> {\n const now = new Date();\n\n // Current period\n const currentEnd = new Date(now);\n const currentStart = new Date(now);\n currentStart.setDate(currentStart.getDate() - days);\n\n // Previous period\n const previousEnd = new Date(currentStart);\n const previousStart = new Date(previousEnd);\n previousStart.setDate(previousStart.getDate() - days);\n\n const makeQuery = (start: Date, end: Date): MetricSetQuery => ({\n metrics: [\"errorReportCount\", \"distinctUsers\"],\n timelineSpec: {\n aggregationPeriod: \"DAILY\",\n startTime: { year: start.getFullYear(), month: start.getMonth() + 1, day: start.getDate() },\n endTime: { year: end.getFullYear(), month: end.getMonth() + 1, day: end.getDate() },\n },\n });\n\n const [currentResult, previousResult] = await Promise.all([\n reporting.queryMetricSet(packageName, metricSet, makeQuery(currentStart, currentEnd)),\n reporting.queryMetricSet(packageName, metricSet, makeQuery(previousStart, previousEnd)),\n ]);\n\n const extractAvg = (rows: MetricRow[] | undefined): number | undefined => {\n if (!rows || rows.length === 0) return undefined;\n const values = rows\n .map((r) => {\n const keys = Object.keys(r.metrics);\n const first = keys[0];\n return first ? Number(r.metrics[first]?.decimalValue?.value) : NaN;\n })\n .filter((v) => !isNaN(v));\n if (values.length === 0) return undefined;\n return values.reduce((a, b) => a + b, 0) / values.length;\n };\n\n const current = extractAvg(currentResult.rows);\n const previous = extractAvg(previousResult.rows);\n\n let changePercent: number | undefined;\n let direction: VitalsTrendComparison[\"direction\"] = \"unknown\";\n\n if (current !== undefined && previous !== undefined && previous !== 0) {\n changePercent = ((current - previous) / previous) * 100;\n if (Math.abs(changePercent) < 1) {\n direction = \"unchanged\";\n } else if (changePercent < 0) {\n direction = \"improved\"; // lower error rate = better\n } else {\n direction = \"degraded\";\n }\n }\n\n return {\n metric: metricSet,\n current,\n previous,\n changePercent: changePercent !== undefined ? Math.round(changePercent * 10) / 10 : undefined,\n direction,\n };\n}\n\nexport function checkThreshold(value: number | undefined, threshold: number): ThresholdResult {\n return {\n breached: value !== undefined && value > threshold,\n value,\n threshold,\n };\n}\n","import type {\n PlayApiClient,\n Subscription,\n BasePlanMigratePricesRequest,\n SubscriptionOffer,\n OffersListResponse,\n} from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListSubscriptionsOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listSubscriptions(\n client: PlayApiClient,\n packageName: string,\n options?: ListSubscriptionsOptions,\n): Promise<{ subscriptions: Subscription[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<Subscription>(\n async (pageToken) => {\n const resp = await client.subscriptions.list(packageName, {\n pageToken,\n pageSize: options?.pageSize,\n });\n return { items: resp.subscriptions || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { subscriptions: result.items, nextPageToken: result.nextPageToken };\n }\n return client.subscriptions.list(packageName, {\n pageToken: options?.pageToken,\n pageSize: options?.pageSize,\n });\n}\n\nexport async function getSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<Subscription> {\n return client.subscriptions.get(packageName, productId);\n}\n\nexport async function createSubscription(\n client: PlayApiClient,\n packageName: string,\n data: Subscription,\n): Promise<Subscription> {\n return client.subscriptions.create(packageName, data);\n}\n\nexport async function updateSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n): Promise<Subscription> {\n return client.subscriptions.update(packageName, productId, data, updateMask);\n}\n\nexport async function deleteSubscription(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n): Promise<void> {\n return client.subscriptions.delete(packageName, productId);\n}\n\nexport async function activateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.activateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deactivateBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<Subscription> {\n return client.subscriptions.deactivateBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function deleteBasePlan(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<void> {\n return client.subscriptions.deleteBasePlan(packageName, productId, basePlanId);\n}\n\nexport async function migratePrices(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: BasePlanMigratePricesRequest,\n): Promise<Subscription> {\n return client.subscriptions.migratePrices(packageName, productId, basePlanId, data);\n}\n\nexport async function listOffers(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n): Promise<OffersListResponse> {\n return client.subscriptions.listOffers(packageName, productId, basePlanId);\n}\n\nexport async function getOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.getOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function createOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.createOffer(packageName, productId, basePlanId, data);\n}\n\nexport async function updateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.updateOffer(\n packageName,\n productId,\n basePlanId,\n offerId,\n data,\n updateMask,\n );\n}\n\nexport async function deleteOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<void> {\n return client.subscriptions.deleteOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function activateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.activateOffer(packageName, productId, basePlanId, offerId);\n}\n\nexport async function deactivateOffer(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n): Promise<SubscriptionOffer> {\n return client.subscriptions.deactivateOffer(packageName, productId, basePlanId, offerId);\n}\n","import { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { PlayApiClient, InAppProduct } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListIapOptions {\n token?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listInAppProducts(\n client: PlayApiClient,\n packageName: string,\n options?: ListIapOptions,\n): Promise<{ inappproduct: InAppProduct[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<InAppProduct>(\n async (pageToken) => {\n const resp = await client.inappproducts.list(packageName, {\n token: pageToken,\n maxResults: options?.maxResults,\n });\n return {\n items: resp.inappproduct || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { inappproduct: result.items, nextPageToken: result.nextPageToken };\n }\n return client.inappproducts.list(packageName, {\n token: options?.token,\n maxResults: options?.maxResults,\n });\n}\n\nexport async function getInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<InAppProduct> {\n return client.inappproducts.get(packageName, sku);\n}\n\nexport async function createInAppProduct(\n client: PlayApiClient,\n packageName: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.create(packageName, data);\n}\n\nexport async function updateInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n data: InAppProduct,\n): Promise<InAppProduct> {\n return client.inappproducts.update(packageName, sku, data);\n}\n\nexport async function deleteInAppProduct(\n client: PlayApiClient,\n packageName: string,\n sku: string,\n): Promise<void> {\n return client.inappproducts.delete(packageName, sku);\n}\n\nexport interface SyncResult {\n created: number;\n updated: number;\n unchanged: number;\n skus: string[];\n}\n\nexport async function syncInAppProducts(\n client: PlayApiClient,\n packageName: string,\n dir: string,\n options?: { dryRun?: boolean },\n): Promise<SyncResult> {\n const files = await readdir(dir);\n const jsonFiles = files.filter((f) => f.endsWith(\".json\"));\n\n if (jsonFiles.length === 0) {\n return { created: 0, updated: 0, unchanged: 0, skus: [] };\n }\n\n const localProducts: InAppProduct[] = [];\n for (const file of jsonFiles) {\n const content = await readFile(join(dir, file), \"utf-8\");\n localProducts.push(JSON.parse(content) as InAppProduct);\n }\n\n const response = await client.inappproducts.list(packageName);\n const remoteSkus = new Set((response.inappproduct || []).map((p) => p.sku));\n\n let created = 0;\n let updated = 0;\n const unchanged = 0;\n const skus: string[] = [];\n\n for (const product of localProducts) {\n skus.push(product.sku);\n if (remoteSkus.has(product.sku)) {\n if (!options?.dryRun) {\n await client.inappproducts.update(packageName, product.sku, product);\n }\n updated++;\n } else {\n if (!options?.dryRun) {\n await client.inappproducts.create(packageName, product);\n }\n created++;\n }\n }\n\n return { created, updated, unchanged, skus };\n}\n","import type {\n PlayApiClient,\n ProductPurchase,\n SubscriptionPurchaseV2,\n SubscriptionDeferResponse,\n} from \"@gpc-cli/api\";\n\nexport async function getProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<ProductPurchase> {\n return client.purchases.getProduct(packageName, productId, token);\n}\n\nexport async function acknowledgeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n payload?: string,\n): Promise<void> {\n const body = payload ? { developerPayload: payload } : undefined;\n return client.purchases.acknowledgeProduct(packageName, productId, token, body);\n}\n\nexport async function consumeProductPurchase(\n client: PlayApiClient,\n packageName: string,\n productId: string,\n token: string,\n): Promise<void> {\n return client.purchases.consumeProduct(packageName, productId, token);\n}\n\nexport async function getSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<SubscriptionPurchaseV2> {\n return client.purchases.getSubscriptionV2(packageName, token);\n}\n\nexport async function cancelSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n): Promise<void> {\n return client.purchases.cancelSubscription(packageName, subscriptionId, token);\n}\n\nexport async function deferSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n subscriptionId: string,\n token: string,\n desiredExpiry: string,\n): Promise<SubscriptionDeferResponse> {\n const sub = await client.purchases.getSubscriptionV1(packageName, subscriptionId, token);\n return client.purchases.deferSubscription(packageName, subscriptionId, token, {\n deferralInfo: {\n expectedExpiryTimeMillis: sub.expiryTimeMillis,\n desiredExpiryTimeMillis: String(new Date(desiredExpiry).getTime()),\n },\n });\n}\n\nexport async function revokeSubscriptionPurchase(\n client: PlayApiClient,\n packageName: string,\n token: string,\n): Promise<void> {\n return client.purchases.revokeSubscriptionV2(packageName, token);\n}\n\nimport type { VoidedPurchase } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport interface ListVoidedOptions {\n startTime?: string;\n endTime?: string;\n maxResults?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listVoidedPurchases(\n client: PlayApiClient,\n packageName: string,\n options?: ListVoidedOptions,\n): Promise<{ voidedPurchases: VoidedPurchase[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<VoidedPurchase>(\n async (pageToken) => {\n const resp = await client.purchases.listVoided(packageName, {\n startTime: options?.startTime,\n endTime: options?.endTime,\n maxResults: options?.maxResults,\n token: pageToken,\n });\n return {\n items: resp.voidedPurchases || [],\n nextPageToken: resp.tokenPagination?.nextPageToken,\n };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { voidedPurchases: result.items, nextPageToken: result.nextPageToken };\n }\n return client.purchases.listVoided(packageName, options);\n}\n\nexport async function refundOrder(\n client: PlayApiClient,\n packageName: string,\n orderId: string,\n options?: { fullRefund?: boolean; proratedRefund?: boolean },\n): Promise<void> {\n return client.orders.refund(packageName, orderId, options);\n}\n","import type { PlayApiClient, ConvertRegionPricesResponse } from \"@gpc-cli/api\";\n\nexport async function convertRegionPrices(\n client: PlayApiClient,\n packageName: string,\n currencyCode: string,\n amount: string,\n): Promise<ConvertRegionPricesResponse> {\n const units = amount.split(\".\")[0] || \"0\";\n const fractional = amount.split(\".\")[1] || \"0\";\n const nanos = Number(fractional.padEnd(9, \"0\").slice(0, 9));\n\n return client.monetization.convertRegionPrices(packageName, {\n price: {\n currencyCode,\n units,\n nanos,\n },\n });\n}\n","import type { PlayApiClient, ReportBucket, ReportType, StatsDimension } from \"@gpc-cli/api\";\n\nconst FINANCIAL_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"earnings\",\n \"sales\",\n \"estimated_sales\",\n \"play_balance\",\n]);\n\nconst STATS_REPORT_TYPES: ReadonlySet<string> = new Set([\n \"installs\",\n \"crashes\",\n \"ratings\",\n \"reviews\",\n \"store_performance\",\n \"subscriptions\",\n]);\n\nconst VALID_DIMENSIONS: ReadonlySet<string> = new Set([\n \"country\",\n \"language\",\n \"os_version\",\n \"device\",\n \"app_version\",\n \"carrier\",\n \"overview\",\n]);\n\nexport function isFinancialReportType(type: string): boolean {\n return FINANCIAL_REPORT_TYPES.has(type);\n}\n\nexport function isStatsReportType(type: string): boolean {\n return STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidReportType(type: string): type is ReportType {\n return FINANCIAL_REPORT_TYPES.has(type) || STATS_REPORT_TYPES.has(type);\n}\n\nexport function isValidStatsDimension(dim: string): dim is StatsDimension {\n return VALID_DIMENSIONS.has(dim);\n}\n\nexport interface ParsedMonth {\n year: number;\n month: number;\n}\n\nexport function parseMonth(monthStr: string): ParsedMonth {\n const match = /^(\\d{4})-(\\d{2})$/.exec(monthStr);\n if (!match) {\n throw new Error(`Invalid month format \"${monthStr}\". Expected YYYY-MM (e.g., 2026-03).`);\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n if (month < 1 || month > 12) {\n throw new Error(`Invalid month \"${month}\". Must be between 01 and 12.`);\n }\n return { year, month };\n}\n\nexport async function listReports(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<ReportBucket[]> {\n const response = await client.reports.list(packageName, reportType, year, month);\n return response.reports || [];\n}\n\nexport async function downloadReport(\n client: PlayApiClient,\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n): Promise<string> {\n const reports = await listReports(client, packageName, reportType, year, month);\n\n if (reports.length === 0) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n\n // Download the first report bucket (signed URI — no auth needed)\n const bucket = reports[0];\n if (!bucket) {\n throw new Error(\n `No ${reportType} reports found for ${year}-${String(month).padStart(2, \"0\")}.`,\n );\n }\n const uri = bucket.uri;\n const response = await fetch(uri);\n\n if (!response.ok) {\n throw new Error(`Failed to download report from signed URI: HTTP ${response.status}`);\n }\n\n return response.text();\n}\n","import type { UsersApiClient, User, DeveloperPermission, Grant } from \"@gpc-cli/api\";\nimport { paginateAll } from \"@gpc-cli/api\";\n\nexport const PERMISSION_PROPAGATION_WARNING =\n \"Note: Permission changes may take up to 48 hours to propagate.\";\n\nexport interface ListUsersOptions {\n pageToken?: string;\n pageSize?: number;\n limit?: number;\n nextPage?: string;\n}\n\nexport async function listUsers(\n client: UsersApiClient,\n developerId: string,\n options?: ListUsersOptions,\n): Promise<{ users: User[]; nextPageToken?: string }> {\n if (options?.limit || options?.nextPage) {\n const result = await paginateAll<User>(\n async (pageToken) => {\n const resp = await client.list(developerId, { pageToken, pageSize: options?.pageSize });\n return { items: resp.users || [], nextPageToken: resp.nextPageToken };\n },\n { limit: options.limit, startPageToken: options.nextPage },\n );\n return { users: result.items, nextPageToken: result.nextPageToken };\n }\n const response = await client.list(developerId, options);\n return { users: response.users || [], nextPageToken: response.nextPageToken };\n}\n\nexport async function getUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<User> {\n return client.get(developerId, userId);\n}\n\nexport async function inviteUser(\n client: UsersApiClient,\n developerId: string,\n email: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const user: Partial<User> = { email };\n if (permissions) user.developerAccountPermission = permissions;\n if (grants) user.grants = grants;\n return client.create(developerId, user);\n}\n\nexport async function updateUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n permissions?: DeveloperPermission[],\n grants?: Grant[],\n): Promise<User> {\n const updates: Partial<User> = {};\n const masks: string[] = [];\n\n if (permissions) {\n updates.developerAccountPermission = permissions;\n masks.push(\"developerAccountPermission\");\n }\n if (grants) {\n updates.grants = grants;\n masks.push(\"grants\");\n }\n\n const updateMask = masks.length > 0 ? masks.join(\",\") : undefined;\n return client.update(developerId, userId, updates, updateMask);\n}\n\nexport async function removeUser(\n client: UsersApiClient,\n developerId: string,\n userId: string,\n): Promise<void> {\n return client.delete(developerId, userId);\n}\n\nexport function parseGrantArg(grantStr: string): Grant {\n const colonIdx = grantStr.indexOf(\":\");\n if (colonIdx === -1) {\n throw new Error(\n `Invalid grant format \"${grantStr}\". Expected <packageName>:<PERMISSION>[,<PERMISSION>...]`,\n );\n }\n const packageName = grantStr.slice(0, colonIdx);\n const perms = grantStr.slice(colonIdx + 1).split(\",\") as DeveloperPermission[];\n return { packageName, appLevelPermissions: perms };\n}\n","import type { PlayApiClient, Testers } from \"@gpc-cli/api\";\nimport { readFile } from \"node:fs/promises\";\n\nexport async function listTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const testers = await client.testers.get(packageName, edit.id, track);\n return testers;\n } finally {\n await client.edits.delete(packageName, edit.id);\n }\n}\n\nexport async function addTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const existing = new Set(current.googleGroups || []);\n for (const email of groupEmails) {\n existing.add(email.trim());\n }\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: [...existing],\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function removeTesters(\n client: PlayApiClient,\n packageName: string,\n track: string,\n groupEmails: string[],\n): Promise<Testers> {\n const edit = await client.edits.insert(packageName);\n try {\n const current = await client.testers.get(packageName, edit.id, track);\n const toRemove = new Set(groupEmails.map((e) => e.trim()));\n const filtered = (current.googleGroups || []).filter((g) => !toRemove.has(g));\n const updated = await client.testers.update(packageName, edit.id, track, {\n googleGroups: filtered,\n });\n await client.edits.validate(packageName, edit.id);\n await client.edits.commit(packageName, edit.id);\n return updated;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function importTestersFromCsv(\n client: PlayApiClient,\n packageName: string,\n track: string,\n csvPath: string,\n): Promise<{ added: number; testers: Testers }> {\n const content = await readFile(csvPath, \"utf-8\");\n const emails = content\n .split(/[,\\n\\r]+/)\n .map((e) => e.trim())\n .filter((e) => e.length > 0 && e.includes(\"@\"));\n\n if (emails.length === 0) {\n throw new Error(`No valid email addresses found in ${csvPath}.`);\n }\n\n const testers = await addTesters(client, packageName, track, emails);\n return { added: emails.length, testers };\n}\n","import { resolve, normalize } from \"node:path\";\n\n/**\n * Normalize and resolve a user-supplied path.\n * Prevents path traversal by normalizing `.` and `..` components.\n */\nexport function safePath(userPath: string): string {\n return resolve(normalize(userPath));\n}\n\n/**\n * Validate that a resolved path is within an expected base directory.\n * Returns the resolved path or throws if it escapes the base.\n */\nexport function safePathWithin(userPath: string, baseDir: string): string {\n const resolved = safePath(userPath);\n const base = safePath(baseDir);\n\n if (!resolved.startsWith(base + \"/\") && resolved !== base) {\n throw new Error(`Path \"${userPath}\" resolves outside the expected directory \"${baseDir}\"`);\n }\n\n return resolved;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface ScaffoldOptions {\n name: string;\n dir: string;\n description?: string;\n}\n\nexport interface ScaffoldResult {\n dir: string;\n files: string[];\n}\n\n/**\n * Scaffold a new GPC plugin project.\n */\nexport async function scaffoldPlugin(options: ScaffoldOptions): Promise<ScaffoldResult> {\n const { name, dir, description = `GPC plugin: ${name}` } = options;\n\n // Ensure name follows convention\n const pluginName = name.startsWith(\"gpc-plugin-\") ? name : `gpc-plugin-${name}`;\n const shortName = pluginName.replace(/^gpc-plugin-/, \"\");\n\n const srcDir = join(dir, \"src\");\n const testDir = join(dir, \"tests\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n const files: string[] = [];\n\n // package.json\n const pkg = {\n name: pluginName,\n version: \"0.1.0\",\n description,\n type: \"module\",\n main: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n exports: {\n \".\": {\n import: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n },\n },\n files: [\"dist\"],\n scripts: {\n build: \"tsup src/index.ts --format esm --dts\",\n dev: \"tsup src/index.ts --format esm --dts --watch\",\n test: \"vitest run\",\n \"test:watch\": \"vitest\",\n },\n keywords: [\"gpc\", \"gpc-plugin\", \"google-play\"],\n license: \"MIT\",\n peerDependencies: {\n \"@gpc-cli/plugin-sdk\": \">=0.8.0\",\n },\n devDependencies: {\n \"@gpc-cli/plugin-sdk\": \"^0.8.0\",\n tsup: \"^8.0.0\",\n typescript: \"^5.0.0\",\n vitest: \"^3.0.0\",\n },\n };\n await writeFile(join(dir, \"package.json\"), JSON.stringify(pkg, null, 2) + \"\\n\");\n files.push(\"package.json\");\n\n // tsconfig.json\n const tsconfig = {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n declaration: true,\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n outDir: \"./dist\",\n rootDir: \"./src\",\n },\n include: [\"src\"],\n };\n await writeFile(join(dir, \"tsconfig.json\"), JSON.stringify(tsconfig, null, 2) + \"\\n\");\n files.push(\"tsconfig.json\");\n\n // src/index.ts\n const srcContent = `import { definePlugin } from \"@gpc-cli/plugin-sdk\";\nimport type { CommandEvent, CommandResult } from \"@gpc-cli/plugin-sdk\";\n\nexport const plugin = definePlugin({\n name: \"${pluginName}\",\n version: \"0.1.0\",\n\n register(hooks) {\n hooks.beforeCommand(async (event: CommandEvent) => {\n // Called before every gpc command\n // Example: log command usage, validate prerequisites, etc.\n });\n\n hooks.afterCommand(async (event: CommandEvent, result: CommandResult) => {\n // Called after every successful gpc command\n // Example: send notifications, update dashboards, etc.\n });\n\n // Uncomment to add custom commands:\n // hooks.registerCommands((registry) => {\n // registry.add({\n // name: \"${shortName}\",\n // description: \"${description}\",\n // action: async (args, opts) => {\n // console.log(\"Hello from ${pluginName}!\");\n // },\n // });\n // });\n },\n});\n`;\n await writeFile(join(srcDir, \"index.ts\"), srcContent);\n files.push(\"src/index.ts\");\n\n // tests/plugin.test.ts\n const testContent = `import { describe, it, expect, vi } from \"vitest\";\nimport { plugin } from \"../src/index\";\n\ndescribe(\"${pluginName}\", () => {\n it(\"has correct name and version\", () => {\n expect(plugin.name).toBe(\"${pluginName}\");\n expect(plugin.version).toBe(\"0.1.0\");\n });\n\n it(\"registers without errors\", () => {\n const hooks = {\n beforeCommand: vi.fn(),\n afterCommand: vi.fn(),\n onError: vi.fn(),\n beforeRequest: vi.fn(),\n afterResponse: vi.fn(),\n registerCommands: vi.fn(),\n };\n\n expect(() => plugin.register(hooks)).not.toThrow();\n });\n});\n`;\n await writeFile(join(testDir, \"plugin.test.ts\"), testContent);\n files.push(\"tests/plugin.test.ts\");\n\n return { dir, files };\n}\n","import { appendFile, chmod, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport interface AuditEntry {\n timestamp: string;\n command: string;\n app?: string;\n args: Record<string, unknown>;\n user?: string;\n success?: boolean;\n durationMs?: number;\n error?: string;\n}\n\nlet auditDir: string | null = null;\n\n/**\n * Initialize audit logging with a directory path.\n * Typically ~/.config/gpc/ or the XDG config dir.\n */\nexport function initAudit(configDir: string): void {\n auditDir = configDir;\n}\n\n/**\n * Write an audit log entry. Non-blocking — errors are silently ignored.\n */\nexport async function writeAuditLog(entry: AuditEntry): Promise<void> {\n if (!auditDir) return;\n\n try {\n await mkdir(auditDir, { recursive: true, mode: 0o700 });\n const logPath = join(auditDir, \"audit.log\");\n const redactedEntry = redactAuditArgs(entry);\n const line = JSON.stringify(redactedEntry) + \"\\n\";\n await appendFile(logPath, line, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(logPath, 0o600).catch(() => {});\n } catch {\n // Audit logging must never break the CLI\n }\n}\n\nconst SENSITIVE_ARG_KEYS = new Set([\n \"key\",\n \"keyFile\",\n \"key-file\",\n \"serviceAccount\",\n \"service-account\",\n \"token\",\n \"password\",\n \"secret\",\n \"credentials\",\n \"private_key\",\n \"client_secret\",\n]);\n\nfunction redactAuditArgs(entry: AuditEntry): AuditEntry {\n const redacted: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(entry.args)) {\n redacted[k] = SENSITIVE_ARG_KEYS.has(k) ? \"[REDACTED]\" : v;\n }\n return { ...entry, args: redacted };\n}\n\n/**\n * Convenience: create an audit entry for a write command.\n */\nexport function createAuditEntry(\n command: string,\n args: Record<string, unknown>,\n app?: string,\n): AuditEntry {\n return {\n timestamp: new Date().toISOString(),\n command,\n app,\n args,\n };\n}\n"],"mappings":"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","names":["stat","extname","formatSize","readFile","stat","readdir","readFile","stat","extname","join","stat","formatSize","stat","paginateAll","readdir","readFile","join","paginateAll","paginateAll","paginateAll","readFile","mkdir","writeFile","join","mkdir","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gpc-cli/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Business logic and command orchestration for GPC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@gpc-cli/api": "1.0.
|
|
19
|
-
"@gpc-cli/auth": "0.1.
|
|
20
|
-
"@gpc-cli/config": "0.1.
|
|
21
|
-
"@gpc-cli/plugin-sdk": "0.1.
|
|
18
|
+
"@gpc-cli/api": "1.0.3",
|
|
19
|
+
"@gpc-cli/auth": "0.1.3",
|
|
20
|
+
"@gpc-cli/config": "0.1.3",
|
|
21
|
+
"@gpc-cli/plugin-sdk": "0.1.2"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"google-play",
|