@gpc-cli/cli 0.9.23 → 0.9.24
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 +32 -27
- package/dist/bin.js +1 -1
- package/dist/{bundle-U7RF6HDE.js → bundle-7IF5FIB4.js} +63 -1
- package/dist/bundle-7IF5FIB4.js.map +1 -0
- package/dist/{chunk-CE2HXEJX.js → chunk-SZLO6HOO.js} +7 -7
- package/dist/config-2L7QUYWP.js +97 -0
- package/dist/config-2L7QUYWP.js.map +1 -0
- package/dist/{doctor-TEIKODLP.js → doctor-JMEHYOGJ.js} +76 -63
- package/dist/doctor-JMEHYOGJ.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/migrate-XQV7P4R7.js +135 -0
- package/dist/migrate-XQV7P4R7.js.map +1 -0
- package/dist/{publish-PZRWX3TH.js → publish-26ZPS7XX.js} +86 -16
- package/dist/publish-26ZPS7XX.js.map +1 -0
- package/dist/status-C222ZKHH.js +96 -0
- package/dist/status-C222ZKHH.js.map +1 -0
- package/package.json +5 -5
- package/dist/bundle-U7RF6HDE.js.map +0 -1
- package/dist/config-R5U7GV56.js +0 -51
- package/dist/config-R5U7GV56.js.map +0 -1
- package/dist/doctor-TEIKODLP.js.map +0 -1
- package/dist/migrate-V6G5YUVH.js +0 -80
- package/dist/migrate-V6G5YUVH.js.map +0 -1
- package/dist/publish-PZRWX3TH.js.map +0 -1
- package/dist/status-S3FAEXNH.js +0 -37
- package/dist/status-S3FAEXNH.js.map +0 -1
- /package/dist/{chunk-CE2HXEJX.js.map → chunk-SZLO6HOO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
# @gpc-cli/cli
|
|
2
2
|
|
|
3
|
-
Ship Android apps from your terminal
|
|
3
|
+
<p align="center"><strong>Ship Android apps from your terminal.</strong></p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
The complete CLI for Google Play — 187 API endpoints, one tool.<br>
|
|
7
|
+
Releases, rollouts, metadata, vitals, reviews, subscriptions, reports, and more.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/@gpc-cli/cli"><img src="https://img.shields.io/npm/v/@gpc-cli/cli?color=00D26A" alt="npm version"></a>
|
|
12
|
+
<a href="https://github.com/yasserstudio/gpc"><img src="https://img.shields.io/github/stars/yasserstudio/gpc" alt="GitHub Stars"></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/Tests-1299_passing-00D26A" alt="Tests">
|
|
14
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow" alt="License">
|
|
15
|
+
</p>
|
|
4
16
|
|
|
5
17
|
## Install
|
|
6
18
|
|
|
7
19
|
```bash
|
|
8
|
-
# npm
|
|
20
|
+
# npm (includes plugin support)
|
|
9
21
|
npm install -g @gpc-cli/cli
|
|
10
22
|
|
|
11
|
-
# Homebrew
|
|
23
|
+
# Homebrew (macOS/Linux)
|
|
12
24
|
brew install yasserstudio/tap/gpc
|
|
13
25
|
|
|
14
|
-
# Standalone binary
|
|
26
|
+
# Standalone binary (no Node.js required)
|
|
15
27
|
curl -fsSL https://raw.githubusercontent.com/yasserstudio/gpc/main/scripts/install.sh | sh
|
|
16
28
|
```
|
|
17
29
|
|
|
@@ -36,48 +48,37 @@ gpc reviews list --stars 1-3 --since 7d
|
|
|
36
48
|
|
|
37
49
|
## What You Get
|
|
38
50
|
|
|
39
|
-
187 API endpoints across
|
|
51
|
+
187 API endpoints across these command groups:
|
|
40
52
|
|
|
41
|
-
|
|
|
53
|
+
| Group | Examples |
|
|
42
54
|
| ----------------- | -------------------------------------------------------------- |
|
|
43
55
|
| **Releases** | `upload`, `promote`, `rollout increase/halt/resume`, `publish` |
|
|
44
56
|
| **Listings** | `pull`, `push`, `images upload/delete`, Fastlane format |
|
|
45
57
|
| **Reviews** | `list`, `reply`, `export --format csv` |
|
|
46
58
|
| **Vitals** | `crashes`, `anr`, `startup`, `rendering`, `battery`, `memory` |
|
|
47
|
-
| **
|
|
48
|
-
| **
|
|
49
|
-
| **
|
|
59
|
+
| **Bundle** | `analyze` (size breakdown), `compare` (size diff) |
|
|
60
|
+
| **Subscriptions** | `list`, `create`, `update`, `base-plans`, `offers` |
|
|
61
|
+
| **IAP** | `list`, `create`, `sync --dir products/`, `batch-get/update` |
|
|
62
|
+
| **Purchases** | `get`, `acknowledge`, `cancel`, `refund`, `voided list` |
|
|
50
63
|
| **Reports** | `download financial`, `download stats` |
|
|
51
64
|
| **Testers** | `add`, `remove`, `import --file testers.csv` |
|
|
52
65
|
| **Users** | `invite`, `update`, `remove`, per-app grants |
|
|
53
66
|
|
|
54
|
-
## CI/CD
|
|
67
|
+
## CI/CD Ready
|
|
55
68
|
|
|
56
|
-
JSON output
|
|
69
|
+
JSON output when piped. Formatted tables in your terminal. Semantic exit codes (0-6) your CI can react to.
|
|
57
70
|
|
|
58
71
|
```yaml
|
|
59
|
-
- name: Install GPC
|
|
60
|
-
run: npm install -g @gpc-cli/cli
|
|
61
|
-
|
|
62
72
|
- name: Upload
|
|
63
73
|
env:
|
|
64
74
|
GPC_SERVICE_ACCOUNT: ${{ secrets.GPC_SERVICE_ACCOUNT }}
|
|
65
75
|
GPC_APP: com.example.myapp
|
|
66
|
-
run:
|
|
76
|
+
run: |
|
|
77
|
+
npm install -g @gpc-cli/cli
|
|
78
|
+
gpc releases upload app.aab --track internal
|
|
67
79
|
```
|
|
68
80
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
GPC auto-detects your environment:
|
|
72
|
-
|
|
73
|
-
- **Terminal:** formatted tables
|
|
74
|
-
- **Piped/CI:** structured JSON
|
|
75
|
-
|
|
76
|
-
Override with `--output json|yaml|markdown|table`.
|
|
77
|
-
|
|
78
|
-
## Documentation
|
|
79
|
-
|
|
80
|
-
Full docs at **[yasserstudio.github.io/gpc](https://yasserstudio.github.io/gpc/)**
|
|
81
|
+
Every write operation supports `--dry-run`.
|
|
81
82
|
|
|
82
83
|
## Part of the GPC Monorepo
|
|
83
84
|
|
|
@@ -91,6 +92,10 @@ Full docs at **[yasserstudio.github.io/gpc](https://yasserstudio.github.io/gpc/)
|
|
|
91
92
|
| [@gpc-cli/plugin-sdk](https://www.npmjs.com/package/@gpc-cli/plugin-sdk) | Plugin interface and lifecycle hooks |
|
|
92
93
|
| [@gpc-cli/plugin-ci](https://www.npmjs.com/package/@gpc-cli/plugin-ci) | CI/CD helpers |
|
|
93
94
|
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
Full docs at **[yasserstudio.github.io/gpc](https://yasserstudio.github.io/gpc/)**
|
|
98
|
+
|
|
94
99
|
## License
|
|
95
100
|
|
|
96
101
|
MIT
|
package/dist/bin.js
CHANGED
|
@@ -28,6 +28,35 @@ function registerBundleCommands(program) {
|
|
|
28
28
|
const analysis = await analyzeBundle(file);
|
|
29
29
|
if (format === "json") {
|
|
30
30
|
console.log(formatOutput(analysis, format));
|
|
31
|
+
} else if (format === "markdown") {
|
|
32
|
+
const moduleRows = analysis.modules.map((m) => ({
|
|
33
|
+
module: m.name,
|
|
34
|
+
compressed: formatSize(m.compressedSize),
|
|
35
|
+
uncompressed: formatSize(m.uncompressedSize),
|
|
36
|
+
entries: m.entries
|
|
37
|
+
}));
|
|
38
|
+
const categoryRows = analysis.categories.map((c) => ({
|
|
39
|
+
category: c.name,
|
|
40
|
+
compressed: formatSize(c.compressedSize),
|
|
41
|
+
uncompressed: formatSize(c.uncompressedSize),
|
|
42
|
+
entries: c.entries
|
|
43
|
+
}));
|
|
44
|
+
console.log(`## Bundle Analysis: \`${analysis.filePath}\``);
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(`| Property | Value |`);
|
|
47
|
+
console.log(`| --- | --- |`);
|
|
48
|
+
console.log(`| Type | ${analysis.fileType.toUpperCase()} |`);
|
|
49
|
+
console.log(`| Total compressed | ${formatSize(analysis.totalCompressed)} |`);
|
|
50
|
+
console.log(`| Total uncompressed | ${formatSize(analysis.totalUncompressed)} |`);
|
|
51
|
+
console.log(`| Entries | ${analysis.entryCount} |`);
|
|
52
|
+
console.log();
|
|
53
|
+
console.log(`### Modules`);
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(formatOutput(moduleRows, "markdown"));
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(`### Categories`);
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(formatOutput(categoryRows, "markdown"));
|
|
31
60
|
} else {
|
|
32
61
|
console.log(`
|
|
33
62
|
File: ${analysis.filePath}`);
|
|
@@ -78,6 +107,39 @@ Threshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold}
|
|
|
78
107
|
const comparison = compareBundles(before, after);
|
|
79
108
|
if (format === "json") {
|
|
80
109
|
console.log(formatOutput(comparison, format));
|
|
110
|
+
} else if (format === "markdown") {
|
|
111
|
+
const sign = comparison.sizeDelta >= 0 ? "+" : "";
|
|
112
|
+
const moduleRows = comparison.moduleDeltas.filter((m) => m.delta !== 0).map((m) => ({
|
|
113
|
+
module: m.module,
|
|
114
|
+
before: formatSize(m.before),
|
|
115
|
+
after: formatSize(m.after),
|
|
116
|
+
delta: formatDelta(m.delta)
|
|
117
|
+
}));
|
|
118
|
+
const categoryRows = comparison.categoryDeltas.filter((c) => c.delta !== 0).map((c) => ({
|
|
119
|
+
category: c.category,
|
|
120
|
+
before: formatSize(c.before),
|
|
121
|
+
after: formatSize(c.after),
|
|
122
|
+
delta: formatDelta(c.delta)
|
|
123
|
+
}));
|
|
124
|
+
console.log(`## Bundle Comparison`);
|
|
125
|
+
console.log();
|
|
126
|
+
console.log(`| | Path | Size |`);
|
|
127
|
+
console.log(`| --- | --- | --- |`);
|
|
128
|
+
console.log(`| Before | \`${comparison.before.path}\` | ${formatSize(comparison.before.totalCompressed)} |`);
|
|
129
|
+
console.log(`| After | \`${comparison.after.path}\` | ${formatSize(comparison.after.totalCompressed)} |`);
|
|
130
|
+
console.log(`| **Delta** | | **${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)** |`);
|
|
131
|
+
if (moduleRows.length > 0) {
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(`### Module Changes`);
|
|
134
|
+
console.log();
|
|
135
|
+
console.log(formatOutput(moduleRows, "markdown"));
|
|
136
|
+
}
|
|
137
|
+
if (categoryRows.length > 0) {
|
|
138
|
+
console.log();
|
|
139
|
+
console.log(`### Category Changes`);
|
|
140
|
+
console.log();
|
|
141
|
+
console.log(formatOutput(categoryRows, "markdown"));
|
|
142
|
+
}
|
|
81
143
|
} else {
|
|
82
144
|
const sign = comparison.sizeDelta >= 0 ? "+" : "";
|
|
83
145
|
console.log(`
|
|
@@ -119,4 +181,4 @@ async function getConfig() {
|
|
|
119
181
|
export {
|
|
120
182
|
registerBundleCommands
|
|
121
183
|
};
|
|
122
|
-
//# sourceMappingURL=bundle-
|
|
184
|
+
//# sourceMappingURL=bundle-7IF5FIB4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/bundle.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport {\n analyzeBundle,\n compareBundles,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\n\nfunction formatSize(bytes: number): string {\n const abs = Math.abs(bytes);\n const sign = bytes < 0 ? \"-\" : \"\";\n if (abs < 1024) return `${sign}${abs} B`;\n if (abs < 1024 * 1024) return `${sign}${(abs / 1024).toFixed(1)} KB`;\n return `${sign}${(abs / (1024 * 1024)).toFixed(2)} MB`;\n}\n\nfunction formatDelta(delta: number): string {\n const prefix = delta > 0 ? \"+\" : \"\";\n return `${prefix}${formatSize(delta)}`;\n}\n\nexport function registerBundleCommands(program: Command): void {\n const bundle = program.command(\"bundle\").description(\"Analyze app bundles and APKs\");\n\n bundle\n .command(\"analyze <file>\")\n .description(\"Analyze size breakdown of an AAB or APK\")\n .option(\"--threshold <mb>\", \"Fail if compressed size exceeds threshold (MB)\", parseFloat)\n .action(async (file: string, opts: { threshold?: number }) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const analysis = await analyzeBundle(file);\n\n if (format === \"json\") {\n console.log(formatOutput(analysis, format));\n } else if (format === \"markdown\") {\n const moduleRows = analysis.modules.map((m) => ({\n module: m.name,\n compressed: formatSize(m.compressedSize),\n uncompressed: formatSize(m.uncompressedSize),\n entries: m.entries,\n }));\n const categoryRows = analysis.categories.map((c) => ({\n category: c.name,\n compressed: formatSize(c.compressedSize),\n uncompressed: formatSize(c.uncompressedSize),\n entries: c.entries,\n }));\n console.log(`## Bundle Analysis: \\`${analysis.filePath}\\``);\n console.log();\n console.log(`| Property | Value |`);\n console.log(`| --- | --- |`);\n console.log(`| Type | ${analysis.fileType.toUpperCase()} |`);\n console.log(`| Total compressed | ${formatSize(analysis.totalCompressed)} |`);\n console.log(`| Total uncompressed | ${formatSize(analysis.totalUncompressed)} |`);\n console.log(`| Entries | ${analysis.entryCount} |`);\n console.log();\n console.log(`### Modules`);\n console.log();\n console.log(formatOutput(moduleRows, \"markdown\"));\n console.log();\n console.log(`### Categories`);\n console.log();\n console.log(formatOutput(categoryRows, \"markdown\"));\n } else {\n console.log(`\\nFile: ${analysis.filePath}`);\n console.log(`Type: ${analysis.fileType.toUpperCase()}`);\n console.log(`Total compressed: ${formatSize(analysis.totalCompressed)}`);\n console.log(`Total uncompressed: ${formatSize(analysis.totalUncompressed)}`);\n console.log(`Entries: ${analysis.entryCount}\\n`);\n\n // Modules table\n const moduleRows = analysis.modules.map((m) => ({\n module: m.name,\n compressed: formatSize(m.compressedSize),\n uncompressed: formatSize(m.uncompressedSize),\n entries: m.entries,\n }));\n console.log(\"Modules:\");\n console.log(formatOutput(moduleRows, \"table\"));\n\n // Categories table\n const categoryRows = analysis.categories.map((c) => ({\n category: c.name,\n compressed: formatSize(c.compressedSize),\n uncompressed: formatSize(c.uncompressedSize),\n entries: c.entries,\n }));\n console.log(\"\\nCategories:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n\n // Threshold check\n if (opts.threshold !== undefined) {\n const thresholdBytes = opts.threshold * 1024 * 1024;\n if (analysis.totalCompressed > thresholdBytes) {\n console.error(\n `\\nThreshold breached: ${formatSize(analysis.totalCompressed)} > ${opts.threshold} MB`,\n );\n process.exit(6);\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n\n bundle\n .command(\"compare <file1> <file2>\")\n .description(\"Compare size differences between two bundles or APKs\")\n .action(async (file1: string, file2: string) => {\n const format = getOutputFormat(program, await getConfig());\n\n try {\n const [before, after] = await Promise.all([\n analyzeBundle(file1),\n analyzeBundle(file2),\n ]);\n const comparison = compareBundles(before, after);\n\n if (format === \"json\") {\n console.log(formatOutput(comparison, format));\n } else if (format === \"markdown\") {\n const sign = comparison.sizeDelta >= 0 ? \"+\" : \"\";\n const moduleRows = comparison.moduleDeltas\n .filter((m) => m.delta !== 0)\n .map((m) => ({\n module: m.module,\n before: formatSize(m.before),\n after: formatSize(m.after),\n delta: formatDelta(m.delta),\n }));\n const categoryRows = comparison.categoryDeltas\n .filter((c) => c.delta !== 0)\n .map((c) => ({\n category: c.category,\n before: formatSize(c.before),\n after: formatSize(c.after),\n delta: formatDelta(c.delta),\n }));\n console.log(`## Bundle Comparison`);\n console.log();\n console.log(`| | Path | Size |`);\n console.log(`| --- | --- | --- |`);\n console.log(`| Before | \\`${comparison.before.path}\\` | ${formatSize(comparison.before.totalCompressed)} |`);\n console.log(`| After | \\`${comparison.after.path}\\` | ${formatSize(comparison.after.totalCompressed)} |`);\n console.log(`| **Delta** | | **${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)** |`);\n if (moduleRows.length > 0) {\n console.log();\n console.log(`### Module Changes`);\n console.log();\n console.log(formatOutput(moduleRows, \"markdown\"));\n }\n if (categoryRows.length > 0) {\n console.log();\n console.log(`### Category Changes`);\n console.log();\n console.log(formatOutput(categoryRows, \"markdown\"));\n }\n } else {\n const sign = comparison.sizeDelta >= 0 ? \"+\" : \"\";\n console.log(`\\nBefore: ${comparison.before.path} (${formatSize(comparison.before.totalCompressed)})`);\n console.log(`After: ${comparison.after.path} (${formatSize(comparison.after.totalCompressed)})`);\n console.log(`Delta: ${sign}${formatSize(comparison.sizeDelta)} (${sign}${comparison.sizeDeltaPercent}%)\\n`);\n\n // Module deltas\n const moduleRows = comparison.moduleDeltas\n .filter((m) => m.delta !== 0)\n .map((m) => ({\n module: m.module,\n before: formatSize(m.before),\n after: formatSize(m.after),\n delta: formatDelta(m.delta),\n }));\n if (moduleRows.length > 0) {\n console.log(\"Module changes:\");\n console.log(formatOutput(moduleRows, \"table\"));\n }\n\n // Category deltas\n const categoryRows = comparison.categoryDeltas\n .filter((c) => c.delta !== 0)\n .map((c) => ({\n category: c.category,\n before: formatSize(c.before),\n after: formatSize(c.after),\n delta: formatDelta(c.delta),\n }));\n if (categoryRows.length > 0) {\n console.log(\"\\nCategory changes:\");\n console.log(formatOutput(categoryRows, \"table\"));\n }\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n\nasync function getConfig() {\n const { loadConfig } = await import(\"@gpc-cli/config\");\n return loadConfig();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -73,10 +73,10 @@ async function createProgram(pluginManager) {
|
|
|
73
73
|
(await import("./auth-5XAQMZRV.js")).registerAuthCommands(program);
|
|
74
74
|
},
|
|
75
75
|
config: async () => {
|
|
76
|
-
(await import("./config-
|
|
76
|
+
(await import("./config-2L7QUYWP.js")).registerConfigCommands(program);
|
|
77
77
|
},
|
|
78
78
|
doctor: async () => {
|
|
79
|
-
(await import("./doctor-
|
|
79
|
+
(await import("./doctor-JMEHYOGJ.js")).registerDoctorCommand(program);
|
|
80
80
|
},
|
|
81
81
|
docs: async () => {
|
|
82
82
|
(await import("./docs-CVTWIVMS.js")).registerDocsCommand(program);
|
|
@@ -94,7 +94,7 @@ async function createProgram(pluginManager) {
|
|
|
94
94
|
(await import("./tracks-XFUN7JJX.js")).registerTracksCommands(program);
|
|
95
95
|
},
|
|
96
96
|
status: async () => {
|
|
97
|
-
(await import("./status-
|
|
97
|
+
(await import("./status-C222ZKHH.js")).registerStatusCommand(program);
|
|
98
98
|
},
|
|
99
99
|
listings: async () => {
|
|
100
100
|
(await import("./listings-VSBHQY5H.js")).registerListingsCommands(program);
|
|
@@ -130,7 +130,7 @@ async function createProgram(pluginManager) {
|
|
|
130
130
|
(await import("./validate-UYXICKBO.js")).registerValidateCommand(program);
|
|
131
131
|
},
|
|
132
132
|
publish: async () => {
|
|
133
|
-
(await import("./publish-
|
|
133
|
+
(await import("./publish-26ZPS7XX.js")).registerPublishCommand(program);
|
|
134
134
|
},
|
|
135
135
|
recovery: async () => {
|
|
136
136
|
(await import("./recovery-S5UNJDBO.js")).registerRecoveryCommands(program);
|
|
@@ -159,13 +159,13 @@ async function createProgram(pluginManager) {
|
|
|
159
159
|
(await import("./purchase-options-CKRN4VIW.js")).registerPurchaseOptionsCommands(program);
|
|
160
160
|
},
|
|
161
161
|
bundle: async () => {
|
|
162
|
-
(await import("./bundle-
|
|
162
|
+
(await import("./bundle-7IF5FIB4.js")).registerBundleCommands(program);
|
|
163
163
|
},
|
|
164
164
|
audit: async () => {
|
|
165
165
|
(await import("./audit-DSTCSU4V.js")).registerAuditCommands(program);
|
|
166
166
|
},
|
|
167
167
|
migrate: async () => {
|
|
168
|
-
(await import("./migrate-
|
|
168
|
+
(await import("./migrate-XQV7P4R7.js")).registerMigrateCommands(program);
|
|
169
169
|
},
|
|
170
170
|
"install-skills": async () => {
|
|
171
171
|
(await import("./install-skills-OV4HVANW.js")).registerInstallSkillsCommand(program);
|
|
@@ -308,4 +308,4 @@ export {
|
|
|
308
308
|
createProgram,
|
|
309
309
|
handleCliError
|
|
310
310
|
};
|
|
311
|
-
//# sourceMappingURL=chunk-
|
|
311
|
+
//# sourceMappingURL=chunk-SZLO6HOO.js.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getOutputFormat
|
|
4
|
+
} from "./chunk-ELXAK7GI.js";
|
|
5
|
+
import {
|
|
6
|
+
isInteractive,
|
|
7
|
+
promptConfirm,
|
|
8
|
+
promptInput,
|
|
9
|
+
promptSelect
|
|
10
|
+
} from "./chunk-NV75I5VP.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/config.ts
|
|
13
|
+
import { loadConfig, setConfigValue, getUserConfigPath, initConfig } from "@gpc-cli/config";
|
|
14
|
+
import { formatOutput, writeAuditLog, createAuditEntry } from "@gpc-cli/core";
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
var ANDROID_PACKAGE_RE = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
|
|
18
|
+
function registerConfigCommands(program) {
|
|
19
|
+
const config = program.command("config").description("Manage configuration");
|
|
20
|
+
config.command("init").description("Create a configuration file").option("--global", "Create in user config directory (~/.config/gpc/)").action(async (_options) => {
|
|
21
|
+
const initialConfig = {};
|
|
22
|
+
if (isInteractive(program)) {
|
|
23
|
+
console.log("\nGPC Setup Wizard\n");
|
|
24
|
+
let app = await promptInput("Default package name (e.g. com.example.app, blank to skip)");
|
|
25
|
+
if (app) {
|
|
26
|
+
if (!ANDROID_PACKAGE_RE.test(app)) {
|
|
27
|
+
console.error(
|
|
28
|
+
` Warning: "${app}" doesn't look like a valid Android package name \u2014 continuing anyway`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
initialConfig["app"] = app;
|
|
32
|
+
}
|
|
33
|
+
const authMethod = await promptSelect(
|
|
34
|
+
"Authentication method:",
|
|
35
|
+
["service-account", "adc", "skip"],
|
|
36
|
+
"service-account"
|
|
37
|
+
);
|
|
38
|
+
if (authMethod === "service-account") {
|
|
39
|
+
let saPath = "";
|
|
40
|
+
while (true) {
|
|
41
|
+
saPath = await promptInput("Path to service account JSON key file");
|
|
42
|
+
if (!saPath) {
|
|
43
|
+
console.log(" Skipping service account setup.");
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
const resolved = resolve(saPath);
|
|
47
|
+
if (existsSync(resolved)) {
|
|
48
|
+
initialConfig["auth"] = { serviceAccount: saPath };
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
console.error(` File not found: ${resolved}`);
|
|
52
|
+
const retry = await promptConfirm("Try a different path?");
|
|
53
|
+
if (!retry) break;
|
|
54
|
+
}
|
|
55
|
+
} else if (authMethod === "adc") {
|
|
56
|
+
console.log(
|
|
57
|
+
" Using Application Default Credentials \u2014 run `gcloud auth application-default login` if not already set up."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const output = await promptSelect(
|
|
61
|
+
"Default output format:",
|
|
62
|
+
["table", "json", "yaml", "markdown"],
|
|
63
|
+
"table"
|
|
64
|
+
);
|
|
65
|
+
if (output !== "table") initialConfig["output"] = output;
|
|
66
|
+
}
|
|
67
|
+
const path = await initConfig(initialConfig);
|
|
68
|
+
const configured = [];
|
|
69
|
+
if (initialConfig["app"]) configured.push(`app: ${initialConfig["app"]}`);
|
|
70
|
+
if (initialConfig["auth"]) configured.push("auth: service account");
|
|
71
|
+
if (initialConfig["output"]) configured.push(`output: ${initialConfig["output"]}`);
|
|
72
|
+
console.log(`
|
|
73
|
+
Configuration file created: ${path}`);
|
|
74
|
+
if (configured.length > 0) {
|
|
75
|
+
console.log(` ${configured.join(" \xB7 ")}`);
|
|
76
|
+
}
|
|
77
|
+
console.log("\nRun `gpc doctor` to verify your setup.");
|
|
78
|
+
writeAuditLog(createAuditEntry("config init", { path })).catch(() => {
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
config.command("show").description("Display resolved configuration").action(async () => {
|
|
82
|
+
const resolved = await loadConfig();
|
|
83
|
+
const format = getOutputFormat(program, resolved);
|
|
84
|
+
console.log(formatOutput(resolved, format));
|
|
85
|
+
});
|
|
86
|
+
config.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
|
|
87
|
+
await setConfigValue(key, value);
|
|
88
|
+
console.log(`Set ${key} = ${value}`);
|
|
89
|
+
});
|
|
90
|
+
config.command("path").description("Show configuration file path").action(() => {
|
|
91
|
+
console.log(getUserConfigPath());
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
registerConfigCommands
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=config-2L7QUYWP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig, setConfigValue, getUserConfigPath, initConfig } from \"@gpc-cli/config\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { formatOutput, writeAuditLog, createAuditEntry } from \"@gpc-cli/core\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isInteractive, promptInput, promptSelect, promptConfirm } from \"../prompt.js\";\n\nconst ANDROID_PACKAGE_RE = /^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$/;\n\nexport function registerConfigCommands(program: Command): void {\n const config = program.command(\"config\").description(\"Manage configuration\");\n\n config\n .command(\"init\")\n .description(\"Create a configuration file\")\n .option(\"--global\", \"Create in user config directory (~/.config/gpc/)\")\n .action(async (_options: { global?: boolean }) => {\n const initialConfig: Record<string, unknown> = {};\n\n if (isInteractive(program)) {\n console.log(\"\\nGPC Setup Wizard\\n\");\n\n // Package name\n let app = await promptInput(\"Default package name (e.g. com.example.app, blank to skip)\");\n if (app) {\n if (!ANDROID_PACKAGE_RE.test(app)) {\n console.error(\n ` Warning: \"${app}\" doesn't look like a valid Android package name — continuing anyway`,\n );\n }\n initialConfig[\"app\"] = app;\n }\n\n // Auth method\n const authMethod = await promptSelect(\n \"Authentication method:\",\n [\"service-account\", \"adc\", \"skip\"],\n \"service-account\",\n );\n\n if (authMethod === \"service-account\") {\n let saPath = \"\";\n while (true) {\n saPath = await promptInput(\"Path to service account JSON key file\");\n if (!saPath) {\n console.log(\" Skipping service account setup.\");\n break;\n }\n const resolved = resolve(saPath);\n if (existsSync(resolved)) {\n initialConfig[\"auth\"] = { serviceAccount: saPath };\n break;\n }\n console.error(` File not found: ${resolved}`);\n const retry = await promptConfirm(\"Try a different path?\");\n if (!retry) break;\n }\n } else if (authMethod === \"adc\") {\n console.log(\n \" Using Application Default Credentials — run `gcloud auth application-default login` if not already set up.\",\n );\n }\n\n // Output format\n const output = await promptSelect(\n \"Default output format:\",\n [\"table\", \"json\", \"yaml\", \"markdown\"],\n \"table\",\n );\n if (output !== \"table\") initialConfig[\"output\"] = output;\n }\n\n const path = await initConfig(initialConfig as GpcConfig);\n\n // Summary\n const configured: string[] = [];\n if (initialConfig[\"app\"]) configured.push(`app: ${initialConfig[\"app\"]}`);\n if (initialConfig[\"auth\"]) configured.push(\"auth: service account\");\n if (initialConfig[\"output\"]) configured.push(`output: ${initialConfig[\"output\"]}`);\n\n console.log(`\\nConfiguration file created: ${path}`);\n if (configured.length > 0) {\n console.log(` ${configured.join(\" · \")}`);\n }\n console.log(\"\\nRun `gpc doctor` to verify your setup.\");\n\n writeAuditLog(createAuditEntry(\"config init\", { path })).catch(() => {});\n });\n\n config\n .command(\"show\")\n .description(\"Display resolved configuration\")\n .action(async () => {\n const resolved = await loadConfig();\n const format = getOutputFormat(program, resolved);\n console.log(formatOutput(resolved, format));\n });\n\n config\n .command(\"set <key> <value>\")\n .description(\"Set a configuration value\")\n .action(async (key: string, value: string) => {\n await setConfigValue(key, value);\n console.log(`Set ${key} = ${value}`);\n });\n\n config\n .command(\"path\")\n .description(\"Show configuration file path\")\n .action(() => {\n console.log(getUserConfigPath());\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -22,20 +22,44 @@ function icon(status) {
|
|
|
22
22
|
return INFO;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
var ANDROID_PACKAGE_RE = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
|
|
26
|
+
function checkNodeVersion(nodeVersion) {
|
|
27
|
+
const major = parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
28
|
+
return major >= 20 ? { name: "node", status: "pass", message: `Node.js ${nodeVersion}` } : {
|
|
29
|
+
name: "node",
|
|
30
|
+
status: "fail",
|
|
31
|
+
message: `Node.js ${nodeVersion} (requires >=20)`,
|
|
32
|
+
suggestion: "Upgrade Node.js to v20 or later: https://nodejs.org"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function checkPackageName(app) {
|
|
36
|
+
if (!app) return null;
|
|
37
|
+
return ANDROID_PACKAGE_RE.test(app) ? { name: "package-name", status: "pass", message: `Package name format OK: ${app}` } : {
|
|
38
|
+
name: "package-name",
|
|
39
|
+
status: "warn",
|
|
40
|
+
message: `Package name may be invalid: ${app}`,
|
|
41
|
+
suggestion: "Android package names must have 2+ dot-separated segments, each starting with a letter (e.g. com.example.app)"
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function checkProxy(url) {
|
|
45
|
+
if (!url) return null;
|
|
46
|
+
try {
|
|
47
|
+
new URL(url);
|
|
48
|
+
return { name: "proxy", status: "pass", message: `Proxy configured: ${url}` };
|
|
49
|
+
} catch {
|
|
50
|
+
return {
|
|
51
|
+
name: "proxy",
|
|
52
|
+
status: "warn",
|
|
53
|
+
message: `Invalid proxy URL: ${url}`,
|
|
54
|
+
suggestion: "Set HTTPS_PROXY to a valid URL (e.g. http://proxy.example.com:8080)"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
25
58
|
function registerDoctorCommand(program) {
|
|
26
59
|
program.command("doctor").description("Verify setup and connectivity").option("--json", "Output results as JSON").action(async (opts) => {
|
|
27
60
|
const results = [];
|
|
28
61
|
const jsonMode = opts.json ?? false;
|
|
29
|
-
|
|
30
|
-
const major = parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
31
|
-
results.push(
|
|
32
|
-
major >= 20 ? { name: "node", status: "pass", message: `Node.js ${nodeVersion}` } : {
|
|
33
|
-
name: "node",
|
|
34
|
-
status: "fail",
|
|
35
|
-
message: `Node.js ${nodeVersion} (requires >=20)`,
|
|
36
|
-
suggestion: "Upgrade Node.js to v20 or later: https://nodejs.org"
|
|
37
|
-
}
|
|
38
|
-
);
|
|
62
|
+
results.push(checkNodeVersion(process.versions.node));
|
|
39
63
|
let config;
|
|
40
64
|
try {
|
|
41
65
|
config = await loadConfig();
|
|
@@ -46,6 +70,8 @@ function registerDoctorCommand(program) {
|
|
|
46
70
|
status: "pass",
|
|
47
71
|
message: `Default app: ${config.app}`
|
|
48
72
|
});
|
|
73
|
+
const pkgCheck = checkPackageName(config.app);
|
|
74
|
+
if (pkgCheck) results.push(pkgCheck);
|
|
49
75
|
} else {
|
|
50
76
|
results.push({
|
|
51
77
|
name: "default-app",
|
|
@@ -58,8 +84,8 @@ function registerDoctorCommand(program) {
|
|
|
58
84
|
results.push({
|
|
59
85
|
name: "config",
|
|
60
86
|
status: "fail",
|
|
61
|
-
message: "Configuration
|
|
62
|
-
suggestion: "
|
|
87
|
+
message: "Configuration could not be loaded",
|
|
88
|
+
suggestion: "Run gpc config init to create a config file, or check .gpcrc.json for syntax errors"
|
|
63
89
|
});
|
|
64
90
|
}
|
|
65
91
|
const configDir = getConfigDir();
|
|
@@ -182,23 +208,8 @@ function registerDoctorCommand(program) {
|
|
|
182
208
|
}
|
|
183
209
|
}
|
|
184
210
|
const proxyUrl = process.env["HTTPS_PROXY"] || process.env["https_proxy"] || process.env["HTTP_PROXY"] || process.env["http_proxy"];
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
new URL(proxyUrl);
|
|
188
|
-
results.push({
|
|
189
|
-
name: "proxy",
|
|
190
|
-
status: "pass",
|
|
191
|
-
message: `Proxy configured: ${proxyUrl}`
|
|
192
|
-
});
|
|
193
|
-
} catch {
|
|
194
|
-
results.push({
|
|
195
|
-
name: "proxy",
|
|
196
|
-
status: "warn",
|
|
197
|
-
message: `Invalid proxy URL: ${proxyUrl}`,
|
|
198
|
-
suggestion: "Set HTTPS_PROXY to a valid URL (e.g., http://proxy.example.com:8080)"
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
211
|
+
const proxyCheck = checkProxy(proxyUrl);
|
|
212
|
+
if (proxyCheck) results.push(proxyCheck);
|
|
202
213
|
const caCert = process.env["GPC_CA_CERT"] || process.env["NODE_EXTRA_CA_CERTS"];
|
|
203
214
|
if (caCert) {
|
|
204
215
|
if (existsSync(caCert)) {
|
|
@@ -216,20 +227,26 @@ function registerDoctorCommand(program) {
|
|
|
216
227
|
});
|
|
217
228
|
}
|
|
218
229
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
const dnsHosts = [
|
|
231
|
+
"androidpublisher.googleapis.com",
|
|
232
|
+
"playdeveloperreporting.googleapis.com"
|
|
233
|
+
];
|
|
234
|
+
for (const host of dnsHosts) {
|
|
235
|
+
try {
|
|
236
|
+
await lookup(host);
|
|
237
|
+
results.push({
|
|
238
|
+
name: "dns",
|
|
239
|
+
status: "pass",
|
|
240
|
+
message: `DNS: ${host}`
|
|
241
|
+
});
|
|
242
|
+
} catch {
|
|
243
|
+
results.push({
|
|
244
|
+
name: "dns",
|
|
245
|
+
status: "fail",
|
|
246
|
+
message: `Cannot resolve ${host}`,
|
|
247
|
+
suggestion: "Check your DNS settings and network connection"
|
|
248
|
+
});
|
|
249
|
+
}
|
|
233
250
|
}
|
|
234
251
|
try {
|
|
235
252
|
const authConfig = config ?? await loadConfig();
|
|
@@ -264,22 +281,14 @@ function registerDoctorCommand(program) {
|
|
|
264
281
|
});
|
|
265
282
|
}
|
|
266
283
|
}
|
|
284
|
+
const errors = results.filter((r) => r.status === "fail").length;
|
|
285
|
+
const warnings = results.filter((r) => r.status === "warn").length;
|
|
286
|
+
const passed = results.filter((r) => r.status === "pass").length;
|
|
267
287
|
if (jsonMode) {
|
|
268
|
-
const errors2 = results.filter((r) => r.status === "fail").length;
|
|
269
|
-
const warnings2 = results.filter((r) => r.status === "warn").length;
|
|
270
288
|
console.log(
|
|
271
|
-
JSON.stringify(
|
|
272
|
-
{
|
|
273
|
-
success: errors2 === 0,
|
|
274
|
-
errors: errors2,
|
|
275
|
-
warnings: warnings2,
|
|
276
|
-
checks: results
|
|
277
|
-
},
|
|
278
|
-
null,
|
|
279
|
-
2
|
|
280
|
-
)
|
|
289
|
+
JSON.stringify({ success: errors === 0, errors, warnings, checks: results }, null, 2)
|
|
281
290
|
);
|
|
282
|
-
if (
|
|
291
|
+
if (errors > 0) process.exit(1);
|
|
283
292
|
return;
|
|
284
293
|
}
|
|
285
294
|
console.log("GPC Doctor\n");
|
|
@@ -289,20 +298,24 @@ function registerDoctorCommand(program) {
|
|
|
289
298
|
console.log(` ${r.suggestion}`);
|
|
290
299
|
}
|
|
291
300
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
console.log(
|
|
302
|
+
`
|
|
303
|
+
${PASS} ${passed} passed ${WARN} ${warnings} warning${warnings !== 1 ? "s" : ""} ${FAIL} ${errors} failed`
|
|
304
|
+
);
|
|
295
305
|
if (errors > 0) {
|
|
296
|
-
console.log("
|
|
306
|
+
console.log("\nSome checks failed. Fix the issues above and run again.");
|
|
297
307
|
process.exit(1);
|
|
298
308
|
} else if (warnings > 0) {
|
|
299
|
-
console.log("
|
|
309
|
+
console.log("\nAll checks passed with warnings.");
|
|
300
310
|
} else {
|
|
301
|
-
console.log("
|
|
311
|
+
console.log("\nAll checks passed!");
|
|
302
312
|
}
|
|
303
313
|
});
|
|
304
314
|
}
|
|
305
315
|
export {
|
|
316
|
+
checkNodeVersion,
|
|
317
|
+
checkPackageName,
|
|
318
|
+
checkProxy,
|
|
306
319
|
registerDoctorCommand
|
|
307
320
|
};
|
|
308
|
-
//# sourceMappingURL=doctor-
|
|
321
|
+
//# sourceMappingURL=doctor-JMEHYOGJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/doctor.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig, getCacheDir, getConfigDir } from \"@gpc-cli/config\";\nimport { resolveAuth, AuthError } from \"@gpc-cli/auth\";\nimport { existsSync, accessSync, statSync, constants } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { lookup } from \"node:dns/promises\";\n\nexport interface CheckResult {\n name: string;\n status: \"pass\" | \"fail\" | \"warn\" | \"info\";\n message: string;\n suggestion?: string;\n}\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\nconst INFO = \"-\";\n\nfunction icon(status: CheckResult[\"status\"]): string {\n switch (status) {\n case \"pass\":\n return PASS;\n case \"fail\":\n return FAIL;\n case \"warn\":\n return WARN;\n case \"info\":\n return INFO;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pure, testable check helpers\n// ---------------------------------------------------------------------------\n\nconst ANDROID_PACKAGE_RE = /^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$/;\n\nexport function checkNodeVersion(nodeVersion: string): CheckResult {\n const major = parseInt(nodeVersion.split(\".\")[0] ?? \"0\", 10);\n return major >= 20\n ? { name: \"node\", status: \"pass\", message: `Node.js ${nodeVersion}` }\n : {\n name: \"node\",\n status: \"fail\",\n message: `Node.js ${nodeVersion} (requires >=20)`,\n suggestion: \"Upgrade Node.js to v20 or later: https://nodejs.org\",\n };\n}\n\nexport function checkPackageName(app: string | undefined): CheckResult | null {\n if (!app) return null;\n return ANDROID_PACKAGE_RE.test(app)\n ? { name: \"package-name\", status: \"pass\", message: `Package name format OK: ${app}` }\n : {\n name: \"package-name\",\n status: \"warn\",\n message: `Package name may be invalid: ${app}`,\n suggestion:\n \"Android package names must have 2+ dot-separated segments, each starting with a letter (e.g. com.example.app)\",\n };\n}\n\nexport function checkProxy(url: string | undefined): CheckResult | null {\n if (!url) return null;\n try {\n new URL(url);\n return { name: \"proxy\", status: \"pass\", message: `Proxy configured: ${url}` };\n } catch {\n return {\n name: \"proxy\",\n status: \"warn\",\n message: `Invalid proxy URL: ${url}`,\n suggestion: \"Set HTTPS_PROXY to a valid URL (e.g. http://proxy.example.com:8080)\",\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command registration\n// ---------------------------------------------------------------------------\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Verify setup and connectivity\")\n .option(\"--json\", \"Output results as JSON\")\n .action(async (opts: { json?: boolean }) => {\n const results: CheckResult[] = [];\n const jsonMode = opts.json ?? false;\n\n // 1. Node.js version\n results.push(checkNodeVersion(process.versions.node));\n\n // 2. Config file\n let config;\n try {\n config = await loadConfig();\n results.push({ name: \"config\", status: \"pass\", message: \"Configuration loaded\" });\n if (config.app) {\n results.push({\n name: \"default-app\",\n status: \"pass\",\n message: `Default app: ${config.app}`,\n });\n // 2b. Package name format\n const pkgCheck = checkPackageName(config.app);\n if (pkgCheck) results.push(pkgCheck);\n } else {\n results.push({\n name: \"default-app\",\n status: \"info\",\n message: \"No default app configured\",\n suggestion: \"Use --app flag or run: gpc config set app <package>\",\n });\n }\n } catch {\n results.push({\n name: \"config\",\n status: \"fail\",\n message: \"Configuration could not be loaded\",\n suggestion: \"Run gpc config init to create a config file, or check .gpcrc.json for syntax errors\",\n });\n }\n\n // 3. Config directory permissions\n const configDir = getConfigDir();\n try {\n if (existsSync(configDir)) {\n accessSync(configDir, constants.R_OK | constants.W_OK);\n results.push({\n name: \"config-dir\",\n status: \"pass\",\n message: `Config directory: ${configDir}`,\n });\n } else {\n results.push({\n name: \"config-dir\",\n status: \"info\",\n message: `Config directory does not exist yet: ${configDir}`,\n });\n }\n } catch {\n results.push({\n name: \"config-dir\",\n status: \"warn\",\n message: `Config directory not writable: ${configDir}`,\n suggestion: `Fix permissions: chmod 755 ${configDir}`,\n });\n }\n\n // 4. Cache directory permissions\n const cacheDir = getCacheDir();\n try {\n if (existsSync(cacheDir)) {\n accessSync(cacheDir, constants.R_OK | constants.W_OK);\n results.push({\n name: \"cache-dir\",\n status: \"pass\",\n message: `Cache directory: ${cacheDir}`,\n });\n } else {\n results.push({\n name: \"cache-dir\",\n status: \"info\",\n message: `Cache directory does not exist yet: ${cacheDir}`,\n });\n }\n } catch {\n results.push({\n name: \"cache-dir\",\n status: \"warn\",\n message: `Cache directory not writable: ${cacheDir}`,\n suggestion: `Fix permissions: chmod 700 ${cacheDir}`,\n });\n }\n\n // 5. Service account file existence + permissions\n if (config?.auth?.serviceAccount) {\n const saValue = config.auth.serviceAccount;\n const looksLikePath = !saValue.trim().startsWith(\"{\");\n if (looksLikePath) {\n const saPath = resolve(saValue);\n if (existsSync(saPath)) {\n try {\n accessSync(saPath, constants.R_OK);\n results.push({\n name: \"service-account-file\",\n status: \"pass\",\n message: `Service account file: ${saPath}`,\n });\n } catch {\n results.push({\n name: \"service-account-file\",\n status: \"fail\",\n message: `Service account file not readable: ${saPath}`,\n suggestion: `Fix permissions: chmod 600 ${saPath}`,\n });\n }\n\n // 5b. SA key file permissions (Unix only)\n if (process.platform !== \"win32\") {\n try {\n const mode = statSync(saPath).mode;\n const groupRead = (mode & 0o040) !== 0;\n const worldRead = (mode & 0o004) !== 0;\n if (groupRead || worldRead) {\n results.push({\n name: \"service-account-permissions\",\n status: \"warn\",\n message: `Service account file is group/world-readable (mode: ${(mode & 0o777).toString(8)})`,\n suggestion: `Restrict permissions: chmod 600 ${saPath}`,\n });\n } else {\n results.push({\n name: \"service-account-permissions\",\n status: \"pass\",\n message: `Service account file permissions OK (mode: ${(mode & 0o777).toString(8)})`,\n });\n }\n } catch {\n // stat failed — skip permission check\n }\n }\n } else {\n results.push({\n name: \"service-account-file\",\n status: \"fail\",\n message: `Service account file not found: ${saPath}`,\n suggestion: \"Check the path in your config or GPC_SERVICE_ACCOUNT env var\",\n });\n }\n }\n }\n\n // 6. Profile validation\n const gpcProfile = process.env[\"GPC_PROFILE\"];\n if (gpcProfile && config) {\n if (config.profiles && gpcProfile in config.profiles) {\n results.push({\n name: \"profile\",\n status: \"pass\",\n message: `Profile \"${gpcProfile}\" found`,\n });\n } else {\n const available = config.profiles ? Object.keys(config.profiles).join(\", \") : \"\";\n results.push({\n name: \"profile\",\n status: \"fail\",\n message: `Profile \"${gpcProfile}\" not found`,\n suggestion: available\n ? `Available profiles: ${available}. Create with: gpc auth login --profile ${gpcProfile}`\n : `No profiles defined. Create one with: gpc auth login --profile ${gpcProfile}`,\n });\n }\n }\n\n // 7. Proxy configuration\n const proxyUrl =\n process.env[\"HTTPS_PROXY\"] ||\n process.env[\"https_proxy\"] ||\n process.env[\"HTTP_PROXY\"] ||\n process.env[\"http_proxy\"];\n const proxyCheck = checkProxy(proxyUrl);\n if (proxyCheck) results.push(proxyCheck);\n\n // 8. CA certificate\n const caCert = process.env[\"GPC_CA_CERT\"] || process.env[\"NODE_EXTRA_CA_CERTS\"];\n if (caCert) {\n if (existsSync(caCert)) {\n results.push({\n name: \"ca-cert\",\n status: \"pass\",\n message: `CA certificate: ${caCert}`,\n });\n } else {\n results.push({\n name: \"ca-cert\",\n status: \"warn\",\n message: `CA certificate file not found: ${caCert}`,\n suggestion: \"Check that GPC_CA_CERT points to an existing PEM file\",\n });\n }\n }\n\n // 9. DNS resolution — both API endpoints\n const dnsHosts = [\n \"androidpublisher.googleapis.com\",\n \"playdeveloperreporting.googleapis.com\",\n ];\n for (const host of dnsHosts) {\n try {\n await lookup(host);\n results.push({\n name: \"dns\",\n status: \"pass\",\n message: `DNS: ${host}`,\n });\n } catch {\n results.push({\n name: \"dns\",\n status: \"fail\",\n message: `Cannot resolve ${host}`,\n suggestion: \"Check your DNS settings and network connection\",\n });\n }\n }\n\n // 10. Authentication + API connectivity\n try {\n const authConfig = config ?? (await loadConfig());\n const client = await resolveAuth({\n serviceAccountPath: authConfig.auth?.serviceAccount,\n });\n results.push({\n name: \"auth\",\n status: \"pass\",\n message: `Authenticated as ${client.getClientEmail()}`,\n });\n\n await client.getAccessToken();\n results.push({\n name: \"api-connectivity\",\n status: \"pass\",\n message: \"API connectivity verified\",\n });\n } catch (error) {\n if (error instanceof AuthError) {\n results.push({\n name: \"auth\",\n status: \"fail\",\n message: `Authentication: ${error.message}`,\n suggestion: error.suggestion,\n });\n } else {\n results.push({\n name: \"api-connectivity\",\n status: \"fail\",\n message: \"API connectivity failed\",\n suggestion: \"Check your network connection and credentials\",\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Output\n // ---------------------------------------------------------------------------\n\n const errors = results.filter((r) => r.status === \"fail\").length;\n const warnings = results.filter((r) => r.status === \"warn\").length;\n const passed = results.filter((r) => r.status === \"pass\").length;\n\n if (jsonMode) {\n console.log(\n JSON.stringify({ success: errors === 0, errors, warnings, checks: results }, null, 2),\n );\n if (errors > 0) process.exit(1);\n return;\n }\n\n console.log(\"GPC Doctor\\n\");\n for (const r of results) {\n console.log(` ${icon(r.status)} ${r.message}`);\n if (r.suggestion && r.status !== \"pass\") {\n console.log(` ${r.suggestion}`);\n }\n }\n\n console.log(\n `\\n ${PASS} ${passed} passed ${WARN} ${warnings} warning${warnings !== 1 ? \"s\" : \"\"} ${FAIL} ${errors} failed`,\n );\n\n if (errors > 0) {\n console.log(\"\\nSome checks failed. Fix the issues above and run again.\");\n process.exit(1);\n } else if (warnings > 0) {\n console.log(\"\\nAll checks passed with warnings.\");\n } else {\n console.log(\"\\nAll checks passed!\");\n }\n });\n}\n"],"mappings":"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","names":[]}
|