@gpc-cli/cli 0.9.45 → 0.9.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -15
- package/dist/anomalies-V3AFS4LD.js +66 -0
- package/dist/anomalies-V3AFS4LD.js.map +1 -0
- package/dist/{apps-J2446UDA.js → apps-FKD3ZG5X.js} +31 -35
- package/dist/apps-FKD3ZG5X.js.map +1 -0
- package/dist/{audit-N2CRHWUN.js → audit-JASSHRWN.js} +47 -62
- package/dist/audit-JASSHRWN.js.map +1 -0
- package/dist/{auth-XGSTT5G5.js → auth-OTA3SV3J.js} +145 -103
- package/dist/auth-OTA3SV3J.js.map +1 -0
- package/dist/bin.js +6 -4
- package/dist/bin.js.map +1 -1
- package/dist/bundle-F7MUVC5J.js +204 -0
- package/dist/bundle-F7MUVC5J.js.map +1 -0
- package/dist/{cache-SLNFRTI2.js → cache-XKPLZYEB.js} +4 -5
- package/dist/cache-XKPLZYEB.js.map +1 -0
- package/dist/changelog-QLDFG5TV.js +48 -0
- package/dist/changelog-QLDFG5TV.js.map +1 -0
- package/dist/{chunk-4O4D5SGL.js → chunk-3SJ6OXCZ.js} +4 -5
- package/dist/chunk-3SJ6OXCZ.js.map +1 -0
- package/dist/{chunk-U6ZTQ34I.js → chunk-BCBXQC7J.js} +45 -11
- package/dist/chunk-BCBXQC7J.js.map +1 -0
- package/dist/{chunk-AA577WVQ.js → chunk-NQH4G7BI.js} +9 -3
- package/dist/chunk-NQH4G7BI.js.map +1 -0
- package/dist/chunk-SLNJEAMK.js +23 -0
- package/dist/chunk-SLNJEAMK.js.map +1 -0
- package/dist/{chunk-SEVX56VN.js → chunk-WWVURXVO.js} +56 -49
- package/dist/chunk-WWVURXVO.js.map +1 -0
- package/dist/{chunk-NV75I5VP.js → chunk-YFUBD2XB.js} +10 -8
- package/dist/chunk-YFUBD2XB.js.map +1 -0
- package/dist/{config-BUXPDN7N.js → config-NY3TZGVS.js} +8 -5
- package/dist/config-NY3TZGVS.js.map +1 -0
- package/dist/{data-safety-Q7FTCEWU.js → data-safety-AFMD6MYI.js} +12 -27
- package/dist/data-safety-AFMD6MYI.js.map +1 -0
- package/dist/{device-tiers-MIOQEXYY.js → device-tiers-AQAMUQXI.js} +23 -38
- package/dist/device-tiers-AQAMUQXI.js.map +1 -0
- package/dist/diff-6EO4ID6W.js +91 -0
- package/dist/diff-6EO4ID6W.js.map +1 -0
- package/dist/{docs-7DUXIKA3.js → docs-4D2SJ4LY.js} +4 -3
- package/dist/docs-4D2SJ4LY.js.map +1 -0
- package/dist/doctor-QCCWG6Y3.js +708 -0
- package/dist/doctor-QCCWG6Y3.js.map +1 -0
- package/dist/{enterprise-7THXNBTC.js → enterprise-7PWXMSUN.js} +11 -21
- package/dist/enterprise-7PWXMSUN.js.map +1 -0
- package/dist/{external-transactions-2GWIMUVM.js → external-transactions-LCZALS3V.js} +12 -28
- package/dist/external-transactions-LCZALS3V.js.map +1 -0
- package/dist/{feedback-DPTO6DUT.js → feedback-CET2X67K.js} +4 -4
- package/dist/{games-BT777WUO.js → games-ZSNGEI7A.js} +17 -32
- package/dist/games-ZSNGEI7A.js.map +1 -0
- package/dist/{generated-apks-RJWTIX7L.js → generated-apks-RX2IUWSF.js} +30 -38
- package/dist/generated-apks-RX2IUWSF.js.map +1 -0
- package/dist/{grants-TKQJ3IER.js → grants-EBPECI26.js} +22 -40
- package/dist/grants-EBPECI26.js.map +1 -0
- package/dist/{iap-ICAEQLK5.js → iap-OUI5YYN4.js} +30 -51
- package/dist/iap-OUI5YYN4.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{init-JZ2THPMS.js → init-WSTQTJOD.js} +5 -4
- package/dist/init-WSTQTJOD.js.map +1 -0
- package/dist/{install-skills-OV4HVANW.js → install-skills-6QDUXI5F.js} +5 -6
- package/dist/{install-skills-OV4HVANW.js.map → install-skills-6QDUXI5F.js.map} +1 -1
- package/dist/{internal-sharing-3U2XFHA4.js → internal-sharing-ONNIWIAT.js} +3 -4
- package/dist/{internal-sharing-3U2XFHA4.js.map → internal-sharing-ONNIWIAT.js.map} +1 -1
- package/dist/{listings-77HZW4S5.js → listings-7SGQ4SRX.js} +118 -157
- package/dist/listings-7SGQ4SRX.js.map +1 -0
- package/dist/migrate-ZQCJGQQS.js +138 -0
- package/dist/migrate-ZQCJGQQS.js.map +1 -0
- package/dist/{one-time-products-LHZAXQES.js → one-time-products-MGZTU7OM.js} +65 -120
- package/dist/one-time-products-MGZTU7OM.js.map +1 -0
- package/dist/{preflight-H3HEBYQW.js → preflight-N7ZRG2JI.js} +58 -55
- package/dist/preflight-N7ZRG2JI.js.map +1 -0
- package/dist/{pricing-XQSDTTK5.js → pricing-JJZFICFL.js} +8 -8
- package/dist/{pricing-XQSDTTK5.js.map → pricing-JJZFICFL.js.map} +1 -1
- package/dist/{prompt-BSV22CQZ.js → prompt-GXC2JSLA.js} +2 -2
- package/dist/{publish-Q5ZKEKZ5.js → publish-JPTI4EBT.js} +34 -30
- package/dist/publish-JPTI4EBT.js.map +1 -0
- package/dist/{purchase-options-CKRN4VIW.js → purchase-options-KFWW4JW2.js} +16 -11
- package/dist/purchase-options-KFWW4JW2.js.map +1 -0
- package/dist/{purchases-43AKV6HG.js → purchases-Z3QBM3UO.js} +121 -194
- package/dist/purchases-Z3QBM3UO.js.map +1 -0
- package/dist/{quickstart-4HB62YEL.js → quickstart-Z5Y3FYJU.js} +5 -3
- package/dist/quickstart-Z5Y3FYJU.js.map +1 -0
- package/dist/{quota-UHIQQYOY.js → quota-MZRWYJGR.js} +5 -15
- package/dist/quota-MZRWYJGR.js.map +1 -0
- package/dist/{recovery-5EV2R476.js → recovery-YE3Z7NIN.js} +32 -61
- package/dist/recovery-YE3Z7NIN.js.map +1 -0
- package/dist/{releases-C2WC2K4E.js → releases-276W3BR7.js} +188 -187
- package/dist/releases-276W3BR7.js.map +1 -0
- package/dist/{reports-2YX3RDOS.js → reports-CIB2T3XT.js} +19 -21
- package/dist/reports-CIB2T3XT.js.map +1 -0
- package/dist/reviews-YCBBM656.js +199 -0
- package/dist/reviews-YCBBM656.js.map +1 -0
- package/dist/rtdn-LID2B7XZ.js +87 -0
- package/dist/rtdn-LID2B7XZ.js.map +1 -0
- package/dist/{status-WHGLODGV.js → status-6LH5W4FU.js} +105 -83
- package/dist/status-6LH5W4FU.js.map +1 -0
- package/dist/{subscriptions-CI3JH3VQ.js → subscriptions-DZP3Y7O7.js} +142 -232
- package/dist/subscriptions-DZP3Y7O7.js.map +1 -0
- package/dist/{testers-NZOFA3EF.js → testers-LSMBXCA2.js} +24 -44
- package/dist/testers-LSMBXCA2.js.map +1 -0
- package/dist/tracks-YHMO2A6B.js +98 -0
- package/dist/tracks-YHMO2A6B.js.map +1 -0
- package/dist/{train-CJJVLY4B.js → train-MDD2EBHS.js} +35 -55
- package/dist/train-MDD2EBHS.js.map +1 -0
- package/dist/{update-NAK6CMUX.js → update-XAO5EZHC.js} +30 -15
- package/dist/update-XAO5EZHC.js.map +1 -0
- package/dist/{users-2YTC4Q36.js → users-UKG7VIQH.js} +45 -67
- package/dist/users-UKG7VIQH.js.map +1 -0
- package/dist/{validate-UOVTM6L3.js → validate-QIYSA3N7.js} +8 -10
- package/dist/validate-QIYSA3N7.js.map +1 -0
- package/dist/{version-N64UBW7A.js → version-R3P4NHCF.js} +4 -4
- package/dist/{vitals-A4CS4MSS.js → vitals-PJEQUUAK.js} +174 -165
- package/dist/vitals-PJEQUUAK.js.map +1 -0
- package/package.json +6 -6
- package/dist/anomalies-NU2IN2GJ.js +0 -54
- package/dist/anomalies-NU2IN2GJ.js.map +0 -1
- package/dist/apps-J2446UDA.js.map +0 -1
- package/dist/audit-N2CRHWUN.js.map +0 -1
- package/dist/auth-XGSTT5G5.js.map +0 -1
- package/dist/bundle-F43TD2BQ.js +0 -218
- package/dist/bundle-F43TD2BQ.js.map +0 -1
- package/dist/cache-SLNFRTI2.js.map +0 -1
- package/dist/changelog-ZYD6W5IV.js +0 -53
- package/dist/changelog-ZYD6W5IV.js.map +0 -1
- package/dist/chunk-4O4D5SGL.js.map +0 -1
- package/dist/chunk-AA577WVQ.js.map +0 -1
- package/dist/chunk-FWKYRLKY.js +0 -19
- package/dist/chunk-FWKYRLKY.js.map +0 -1
- package/dist/chunk-NV75I5VP.js.map +0 -1
- package/dist/chunk-SEVX56VN.js.map +0 -1
- package/dist/chunk-U6ZTQ34I.js.map +0 -1
- package/dist/config-BUXPDN7N.js.map +0 -1
- package/dist/data-safety-Q7FTCEWU.js.map +0 -1
- package/dist/device-tiers-MIOQEXYY.js.map +0 -1
- package/dist/diff-V77SMKAQ.js +0 -96
- package/dist/diff-V77SMKAQ.js.map +0 -1
- package/dist/docs-7DUXIKA3.js.map +0 -1
- package/dist/doctor-3Z4ARPM2.js +0 -372
- package/dist/doctor-3Z4ARPM2.js.map +0 -1
- package/dist/enterprise-7THXNBTC.js.map +0 -1
- package/dist/external-transactions-2GWIMUVM.js.map +0 -1
- package/dist/games-BT777WUO.js.map +0 -1
- package/dist/generated-apks-RJWTIX7L.js.map +0 -1
- package/dist/grants-TKQJ3IER.js.map +0 -1
- package/dist/iap-ICAEQLK5.js.map +0 -1
- package/dist/init-JZ2THPMS.js.map +0 -1
- package/dist/listings-77HZW4S5.js.map +0 -1
- package/dist/migrate-SQT6RD6T.js +0 -143
- package/dist/migrate-SQT6RD6T.js.map +0 -1
- package/dist/one-time-products-LHZAXQES.js.map +0 -1
- package/dist/preflight-H3HEBYQW.js.map +0 -1
- package/dist/publish-Q5ZKEKZ5.js.map +0 -1
- package/dist/purchase-options-CKRN4VIW.js.map +0 -1
- package/dist/purchases-43AKV6HG.js.map +0 -1
- package/dist/quickstart-4HB62YEL.js.map +0 -1
- package/dist/quota-UHIQQYOY.js.map +0 -1
- package/dist/recovery-5EV2R476.js.map +0 -1
- package/dist/releases-C2WC2K4E.js.map +0 -1
- package/dist/reports-2YX3RDOS.js.map +0 -1
- package/dist/reviews-2CWOI5CV.js +0 -213
- package/dist/reviews-2CWOI5CV.js.map +0 -1
- package/dist/status-WHGLODGV.js.map +0 -1
- package/dist/subscriptions-CI3JH3VQ.js.map +0 -1
- package/dist/testers-NZOFA3EF.js.map +0 -1
- package/dist/tracks-NERFFEDT.js +0 -107
- package/dist/tracks-NERFFEDT.js.map +0 -1
- package/dist/train-CJJVLY4B.js.map +0 -1
- package/dist/update-NAK6CMUX.js.map +0 -1
- package/dist/users-2YTC4Q36.js.map +0 -1
- package/dist/validate-UOVTM6L3.js.map +0 -1
- package/dist/vitals-A4CS4MSS.js.map +0 -1
- /package/dist/{feedback-DPTO6DUT.js.map → feedback-CET2X67K.js.map} +0 -0
- /package/dist/{prompt-BSV22CQZ.js.map → prompt-GXC2JSLA.js.map} +0 -0
- /package/dist/{version-N64UBW7A.js.map → version-R3P4NHCF.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/updater.ts"],"sourcesContent":["/**\n * Self-update logic for `gpc update`.\n *\n * All functions here are pure / testable — no Commander.js dependency.\n * The command layer in commands/update.ts handles output and flags.\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { rename, chmod, unlink, stat } from \"node:fs/promises\";\nimport { realpathSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable, Transform } from \"node:stream\";\nimport { spawn } from \"node:child_process\";\nimport { isNewerVersion } from \"./update-check.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type InstallMethod = \"npm\" | \"homebrew\" | \"binary\" | \"unknown\";\n\nexport interface GithubAsset {\n name: string;\n browser_download_url: string;\n size: number;\n}\n\nexport interface GithubRelease {\n tag_name: string;\n html_url: string;\n assets: GithubAsset[];\n}\n\nexport interface UpdateCheckResult {\n current: string;\n latest: string;\n latestTag: string;\n updateAvailable: boolean;\n installMethod: InstallMethod;\n release: GithubRelease;\n}\n\nconst GITHUB_API_URL = \"https://api.github.com/repos/yasserstudio/gpc/releases/latest\";\nconst GITHUB_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\n// ---------------------------------------------------------------------------\n// Install method detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect how gpc was installed.\n *\n * Priority:\n * 1. __GPC_BINARY env var (injected by esbuild at compile time) → \"binary\"\n * 2. npm_config_prefix env var → \"npm\"\n * 3. realpathSync(process.argv[1]) contains \"cellar\" or \"homebrew\" → \"homebrew\"\n * 4. realpathSync(process.argv[1]) contains \"node_modules\" → \"npm\"\n * 5. fallback → \"unknown\"\n *\n * Using realpathSync(process.argv[1]) instead of shelling to `which gpc`:\n * - No child process spawn\n * - Works on Windows without `where.exe`\n * - Resolves symlinks — critical for Intel Mac Homebrew where the bin path\n * is a symlink but the real path contains \"Cellar\"\n */\nexport function detectInstallMethod(): InstallMethod {\n // 1. Compiled binary — but Homebrew also distributes as compiled binary, check execPath\n if (process.env[\"__GPC_BINARY\"] === \"1\") {\n try {\n const resolved = realpathSync(process.execPath).toLowerCase();\n if (resolved.includes(\"cellar\") || resolved.includes(\"homebrew\")) return \"homebrew\";\n } catch {\n /* ignore */\n }\n return \"binary\";\n }\n\n // 2. npm global install\n if (process.env[\"npm_config_prefix\"]) return \"npm\";\n\n // 3. Resolve symlinks and inspect path\n try {\n const resolved = realpathSync(process.argv[1] ?? \"\").toLowerCase();\n if (resolved.includes(\"cellar\") || resolved.includes(\"homebrew\")) return \"homebrew\";\n if (resolved.includes(\"node_modules\")) return \"npm\";\n } catch {\n // realpathSync can throw if the path doesn't exist — fall through to unknown\n }\n\n return \"unknown\";\n}\n\n// ---------------------------------------------------------------------------\n// Platform asset mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the GitHub release asset name for the current platform/arch,\n * matching the names produced by scripts/build-binary.ts TARGETS map.\n */\nexport function getPlatformAsset(): string | null {\n const arch = process.arch === \"arm64\" ? \"arm64\" : \"x64\";\n switch (process.platform) {\n case \"darwin\":\n return `gpc-darwin-${arch}`;\n case \"linux\":\n return `gpc-linux-${arch}`;\n case \"win32\":\n return \"gpc-windows-x64.exe\";\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Current binary path\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the path of the currently running gpc binary.\n * For compiled binaries, process.execPath IS the binary.\n * For npm/dev installs, process.argv[1] is the script entrypoint.\n */\nexport function getCurrentBinaryPath(): string {\n if (process.env[\"__GPC_BINARY\"] === \"1\") return process.execPath;\n return process.argv[1] ?? process.execPath;\n}\n\n// ---------------------------------------------------------------------------\n// GitHub Releases API\n// ---------------------------------------------------------------------------\n\nfunction githubHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"User-Agent\": \"gpc-cli\",\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n };\n // Support GPC_GITHUB_TOKEN for authenticated requests (avoids 60 req/hr limit\n // on shared CI runner IPs)\n if (process.env[\"GPC_GITHUB_TOKEN\"]) {\n headers[\"Authorization\"] = `Bearer ${process.env[\"GPC_GITHUB_TOKEN\"]}`;\n }\n return headers;\n}\n\nexport async function fetchLatestRelease(): Promise<GithubRelease> {\n let response: Response;\n try {\n response = await fetch(GITHUB_API_URL, {\n headers: githubHeaders(),\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw Object.assign(new Error(`Network error checking for updates: ${msg}`), {\n code: \"NETWORK_ERROR\",\n exitCode: 5,\n });\n }\n\n if (response.status === 429) {\n throw Object.assign(\n new Error(\"GitHub API rate limit exceeded. Set GPC_GITHUB_TOKEN to increase the limit.\"),\n { code: \"UPDATE_RATE_LIMITED\", exitCode: 4 },\n );\n }\n\n if (!response.ok) {\n throw Object.assign(new Error(`GitHub API returned HTTP ${response.status}`), {\n code: \"UPDATE_API_ERROR\",\n exitCode: 4,\n });\n }\n\n return (await response.json()) as GithubRelease;\n}\n\n/**\n * Fetch and parse checksums.txt from the release assets.\n * Returns a Map of filename → lowercase sha256 hex.\n * Returns an empty Map if the asset is missing or the fetch fails.\n */\nexport async function fetchChecksums(release: GithubRelease): Promise<Map<string, string>> {\n const asset = release.assets.find((a) => a.name === \"checksums.txt\");\n if (!asset) return new Map();\n\n try {\n const response = await fetch(asset.browser_download_url, {\n headers: githubHeaders(),\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n });\n if (!response.ok) return new Map();\n\n const map = new Map<string, string>();\n for (const line of (await response.text()).split(\"\\n\")) {\n const parts = line.trim().split(/\\s+/);\n const hash = parts[0];\n const name = parts[1];\n if (hash && name) map.set(name, hash.toLowerCase());\n }\n return map;\n } catch {\n return new Map();\n }\n}\n\n// ---------------------------------------------------------------------------\n// High-level update check\n// ---------------------------------------------------------------------------\n\nexport async function checkForUpdate(currentVersion: string): Promise<UpdateCheckResult> {\n const release = await fetchLatestRelease();\n const latest = release.tag_name.replace(/^v/, \"\");\n return {\n current: currentVersion,\n latest,\n latestTag: release.tag_name,\n updateAvailable: isNewerVersion(currentVersion, latest),\n installMethod: detectInstallMethod(),\n release,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Update execution paths\n// ---------------------------------------------------------------------------\n\nexport async function updateViaNpm(options: { silent?: boolean } = {}): Promise<void> {\n return new Promise((resolve, reject) => {\n const proc = spawn(\"npm\", [\"install\", \"-g\", \"@gpc-cli/cli@latest\"], {\n // In silent (JSON) mode, redirect npm's stdout to stderr so it doesn't\n // pollute the machine-readable JSON that gpc writes to stdout.\n stdio: options.silent ? [\"inherit\", process.stderr, process.stderr] : \"inherit\",\n shell: false,\n });\n proc.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(\n Object.assign(new Error(`npm exited with code ${code}`), {\n code: \"UPDATE_NPM_FAILED\",\n exitCode: 1,\n suggestion: \"Run manually: npm install -g @gpc-cli/cli@latest\",\n }),\n );\n }\n });\n proc.on(\"error\", (err) => {\n reject(\n Object.assign(new Error(`Failed to run npm: ${err.message}`), {\n code: \"UPDATE_NPM_SPAWN_FAILED\",\n exitCode: 1,\n suggestion: \"Ensure npm is in your PATH\",\n }),\n );\n });\n });\n}\n\nexport async function updateViaBrew(options: { silent?: boolean } = {}): Promise<void> {\n return new Promise((resolve, reject) => {\n const proc = spawn(\"brew\", [\"upgrade\", \"yasserstudio/tap/gpc\"], {\n // In silent (JSON) mode, redirect brew's stdout to stderr so it doesn't\n // pollute the machine-readable JSON that gpc writes to stdout.\n stdio: options.silent ? [\"inherit\", process.stderr, process.stderr] : \"inherit\",\n shell: false,\n });\n proc.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(\n Object.assign(new Error(`brew exited with code ${code}`), {\n code: \"UPDATE_BREW_FAILED\",\n exitCode: 1,\n suggestion: \"Run manually: brew upgrade yasserstudio/tap/gpc\",\n }),\n );\n }\n });\n proc.on(\"error\", (err) => {\n reject(\n Object.assign(new Error(`Failed to run brew: ${err.message}`), {\n code: \"UPDATE_BREW_SPAWN_FAILED\",\n exitCode: 1,\n suggestion: \"Ensure Homebrew is installed: https://brew.sh\",\n }),\n );\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Binary in-place replace\n// ---------------------------------------------------------------------------\n\nfunction isPermissionError(err: unknown): boolean {\n return err instanceof Error && \"code\" in err && (err.code === \"EACCES\" || err.code === \"EPERM\");\n}\n\nasync function sha256File(filePath: string): Promise<string> {\n const hash = createHash(\"sha256\");\n const { size } = await stat(filePath);\n if (size === 0) return hash.digest(\"hex\");\n\n // Read in 64 KB chunks\n const { createReadStream } = await import(\"node:fs\");\n const stream = createReadStream(filePath);\n await new Promise<void>((resolve, reject) => {\n stream.on(\"data\", (chunk: Buffer) => hash.update(chunk));\n stream.on(\"end\", resolve);\n stream.on(\"error\", reject);\n });\n return hash.digest(\"hex\");\n}\n\n/**\n * Download a new binary and atomically replace the current one.\n *\n * macOS/Linux: rename(tmp, current) — safe because open files can be replaced\n * Windows: rename(current, .old) then rename(tmp, current) — avoids EBUSY\n * because Windows locks running executables\n */\nexport async function updateBinaryInPlace(\n assetUrl: string,\n expectedSha256: string,\n currentBinaryPath: string,\n options: { onProgress?: (downloaded: number, total: number) => void } = {},\n): Promise<void> {\n const dir = dirname(currentBinaryPath);\n const tmpPath = join(dir, `.gpc-update-${process.pid}.tmp`);\n const oldPath = join(dir, `.gpc-old-${process.pid}`);\n\n try {\n // 1. Download\n let response: Response;\n try {\n response = await fetch(assetUrl, {\n signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw Object.assign(new Error(`Download failed: ${msg}`), {\n code: \"UPDATE_DOWNLOAD_FAILED\",\n exitCode: 5,\n });\n }\n\n if (!response.ok) {\n throw Object.assign(new Error(`Download failed: HTTP ${response.status}`), {\n code: \"UPDATE_DOWNLOAD_FAILED\",\n exitCode: 4,\n });\n }\n if (!response.body) {\n throw Object.assign(new Error(\"Empty response body\"), {\n code: \"UPDATE_DOWNLOAD_FAILED\",\n exitCode: 4,\n });\n }\n\n const contentLength = response.headers.get(\"content-length\");\n const total = contentLength ? parseInt(contentLength, 10) : 0;\n let downloaded = 0;\n\n const dest = createWriteStream(tmpPath);\n const { onProgress } = options;\n\n if (onProgress) {\n const tracker = new Transform({\n transform(chunk: Buffer, _enc, cb) {\n downloaded += chunk.length;\n onProgress(downloaded, total);\n cb(null, chunk);\n },\n });\n await pipeline(\n Readable.fromWeb(response.body as Parameters<typeof Readable.fromWeb>[0]),\n tracker,\n dest,\n );\n } else {\n await pipeline(\n Readable.fromWeb(response.body as Parameters<typeof Readable.fromWeb>[0]),\n dest,\n );\n }\n\n // 2. Verify checksum (skip if no checksum available)\n if (expectedSha256) {\n const actual = await sha256File(tmpPath);\n if (actual !== expectedSha256.toLowerCase()) {\n throw Object.assign(\n new Error(`Checksum mismatch — expected ${expectedSha256}, got ${actual}`),\n {\n code: \"UPDATE_CHECKSUM_MISMATCH\",\n exitCode: 1,\n suggestion: \"The download may be corrupt. Try again.\",\n },\n );\n }\n }\n\n // 3. Set executable bit (no-op on Windows)\n if (process.platform !== \"win32\") {\n await chmod(tmpPath, 0o755);\n }\n\n // 4. Atomic replace\n if (process.platform === \"win32\") {\n // Windows locks running executables, so we rename the current binary\n // out of the way first, then move the new one into place\n await rename(currentBinaryPath, oldPath);\n try {\n await rename(tmpPath, currentBinaryPath);\n } catch (renameErr) {\n // Roll back — restore original binary\n await rename(oldPath, currentBinaryPath).catch(() => {});\n throw renameErr;\n }\n // Delete the old binary in the background (best-effort)\n unlink(oldPath).catch(() => {});\n } else {\n await rename(tmpPath, currentBinaryPath);\n }\n } catch (err) {\n // Clean up temp file on any error\n await unlink(tmpPath).catch(() => {});\n\n if (isPermissionError(err)) {\n throw Object.assign(new Error(`Permission denied replacing ${currentBinaryPath}`), {\n code: \"UPDATE_PERMISSION_DENIED\",\n exitCode: 1,\n suggestion: `Run with elevated permissions: sudo gpc update`,\n });\n }\n throw err;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport type { GpcConfig } from \"@gpc-cli/config\";\nimport { loadConfig, setConfigValue, getUserConfigPath, initConfig } 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 const 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: string;\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\n writeAuditLog(createAuditEntry(\"config init\", { path })).catch(() => {});\n\n // Run doctor inline to verify setup\n console.log(\"\\nVerifying setup...\");\n try {\n const { registerDoctorCommand } = await import(\"./doctor.js\");\n const { Command } = await import(\"commander\");\n const doctorProgram = new Command();\n doctorProgram\n .option(\"-o, --output <format>\", \"Output format\")\n .option(\"-j, --json\", \"JSON mode\");\n registerDoctorCommand(doctorProgram);\n await doctorProgram.parseAsync([\"node\", \"gpc\", \"doctor\"]);\n } catch {\n // Doctor failures should not prevent config init from succeeding\n console.log(\"Run `gpc doctor` to verify your setup.\");\n }\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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/data-safety.ts"],"sourcesContent":["import { resolvePackageName, getClient } from \"../resolve.js\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\n\nimport { importDataSafety, formatOutput } from \"@gpc-cli/core\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { getOutputFormat } from \"../format.js\";\n\n\n\nexport function registerDataSafetyCommands(program: Command): void {\n const dataSafety = program.command(\"data-safety\").description(\"Manage data safety declarations\");\n\n // Get — not supported by Google Play API (no GET endpoint for data safety)\n dataSafety\n .command(\"get\")\n .description(\"Get the current data safety declaration\")\n .action(async () => {\n console.error(\n \"Error: The Google Play Developer API does not provide a GET endpoint for data safety declarations.\",\n );\n console.error(\"\");\n console.error(\"Data safety labels can only be updated (not read) via the API.\");\n console.error(\"To view your current data safety declaration, use the Google Play Console:\");\n console.error(\" https://play.google.com/console → App content → Data safety\");\n console.error(\"\");\n console.error(\n \"To update data safety via the API, use: gpc data-safety update --file <csv-file>\",\n );\n process.exit(2);\n });\n\n // Update\n dataSafety\n .command(\"update\")\n .description(\"Update data safety declaration from a JSON file\")\n .requiredOption(\"--file <path>\", \"Path to data safety JSON file\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"data-safety update\",\n action: \"update data safety from\",\n target: options.file,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await importDataSafety(client, packageName, options.file);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // Export — not supported (no GET endpoint)\n dataSafety\n .command(\"export\")\n .description(\"Export data safety declaration to a JSON file\")\n .option(\"--output <path>\", \"Output file path\", \"data-safety.json\")\n .action(async () => {\n console.error(\n \"Error: The Google Play Developer API does not provide a GET endpoint for data safety declarations.\",\n );\n console.error(\"Data safety labels cannot be exported via the API.\");\n console.error(\"\");\n console.error(\"To export your data safety declaration, use the Google Play Console:\");\n console.error(\" App content → Data safety → Export to CSV\");\n process.exit(2);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAEA,SAAS,kBAAkB;AAE3B,SAAS,kBAAkB,oBAAoB;AAMxC,SAAS,2BAA2B,SAAwB;AACjE,QAAM,aAAa,QAAQ,QAAQ,aAAa,EAAE,YAAY,iCAAiC;AAG/F,aACG,QAAQ,KAAK,EACb,YAAY,yCAAyC,EACrD,OAAO,YAAY;AAClB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,gEAAgE;AAC9E,YAAQ,MAAM,4EAA4E;AAC1F,YAAQ,MAAM,yEAA+D;AAC7E,YAAQ,MAAM,EAAE;AAChB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGH,aACG,QAAQ,QAAQ,EAChB,YAAY,iDAAiD,EAC7D,eAAe,iBAAiB,+BAA+B,EAC/D,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAI,SAAS,OAAO,GAAG;AACrB;AAAA,QACE;AAAA,UACE,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ,QAAQ;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,UAAU,MAAM;AAErC,QAAI;AACF,YAAM,SAAS,MAAM,iBAAiB,QAAQ,aAAa,QAAQ,IAAI;AACvE,cAAQ,IAAI,aAAa,QAAQ,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,aACG,QAAQ,QAAQ,EAChB,YAAY,+CAA+C,EAC3D,OAAO,mBAAmB,oBAAoB,kBAAkB,EAChE,OAAO,YAAY;AAClB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,MAAM,oDAAoD;AAClE,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,sEAAsE;AACpF,YAAQ,MAAM,uDAA6C;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACL;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/device-tiers.ts"],"sourcesContent":["import { resolvePackageName, getClient } from \"../resolve.js\";\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\n\nimport { listDeviceTiers, getDeviceTier, createDeviceTier, formatOutput } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { readFile } from \"node:fs/promises\";\n\n\n\nexport function registerDeviceTiersCommands(program: Command): void {\n const dt = program.command(\"device-tiers\").description(\"Manage device tier configurations\");\n\n dt.command(\"list\")\n .description(\"List device tier configurations\")\n .action(async () => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await listDeviceTiers(client, packageName);\n const configs = (result as unknown as Record<string, unknown>)[\"deviceTierConfigs\"] as\n | Record<string, unknown>[]\n | undefined;\n if (format !== \"json\" && (!configs || configs.length === 0)) {\n console.log(\"No device tier configs found.\");\n return;\n }\n if (format !== \"json\" && configs) {\n const rows = configs.map((c) => ({\n deviceTierConfigId: c[\"deviceTierConfigId\"] || \"-\",\n deviceGroups: Array.isArray(c[\"deviceGroups\"])\n ? (c[\"deviceGroups\"] as unknown[]).length\n : 0,\n deviceTierSet: c[\"deviceTierSet\"] ? \"yes\" : \"no\",\n }));\n console.log(formatOutput(rows, format));\n } else {\n console.log(formatOutput(result, format));\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n dt.command(\"get <config-id>\")\n .description(\"Get a device tier configuration\")\n .action(async (configId: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const client = await getClient(config);\n const format = getOutputFormat(program, config);\n\n try {\n const result = await getDeviceTier(client, packageName, configId);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n dt.command(\"create\")\n .description(\"Create a device tier configuration from a JSON file\")\n .requiredOption(\"--file <path>\", \"Path to JSON config file\")\n .action(async (opts: { file: string }) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n if (isDryRun(program)) {\n printDryRun(\n {\n command: \"device-tiers create\",\n action: \"create device tier config from\",\n target: opts.file,\n },\n format,\n formatOutput,\n );\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const raw = await readFile(opts.file, \"utf-8\");\n const tierConfig = JSON.parse(raw);\n const result = await createDeviceTier(client, packageName, tierConfig);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/diff-V77SMKAQ.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
resolvePackageName
|
|
4
|
-
} from "./chunk-AA577WVQ.js";
|
|
5
|
-
import {
|
|
6
|
-
bold,
|
|
7
|
-
dim,
|
|
8
|
-
green,
|
|
9
|
-
red,
|
|
10
|
-
yellow
|
|
11
|
-
} from "./chunk-FAN4ZITI.js";
|
|
12
|
-
import {
|
|
13
|
-
getOutputFormat
|
|
14
|
-
} from "./chunk-ELXAK7GI.js";
|
|
15
|
-
|
|
16
|
-
// src/commands/diff.ts
|
|
17
|
-
import { loadConfig } from "@gpc-cli/config";
|
|
18
|
-
import { resolveAuth } from "@gpc-cli/auth";
|
|
19
|
-
import { createApiClient } from "@gpc-cli/api";
|
|
20
|
-
import { getReleasesStatus, diffReleases, diffListingsCommand, formatOutput } from "@gpc-cli/core";
|
|
21
|
-
function registerDiffCommand(program) {
|
|
22
|
-
program.command("diff").description("Preview current release state and pending changes (read-only)").option("--from <track>", "Compare releases from this track").option("--to <track>", "Compare releases to this track").option("--metadata <dir>", "Compare local metadata directory vs remote listings").action(async (options) => {
|
|
23
|
-
const config = await loadConfig();
|
|
24
|
-
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
25
|
-
const format = getOutputFormat(program, config);
|
|
26
|
-
const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
|
|
27
|
-
const client = createApiClient({ auth });
|
|
28
|
-
try {
|
|
29
|
-
const sections = {};
|
|
30
|
-
const releases = await getReleasesStatus(client, packageName);
|
|
31
|
-
sections["releases"] = releases;
|
|
32
|
-
let trackDiff;
|
|
33
|
-
if (options.from && options.to) {
|
|
34
|
-
trackDiff = await diffReleases(client, packageName, options.from, options.to);
|
|
35
|
-
sections["trackDiff"] = trackDiff;
|
|
36
|
-
}
|
|
37
|
-
let metadataDiff;
|
|
38
|
-
if (options.metadata) {
|
|
39
|
-
metadataDiff = await diffListingsCommand(client, packageName, options.metadata);
|
|
40
|
-
sections["metadata"] = metadataDiff;
|
|
41
|
-
}
|
|
42
|
-
if (format === "json") {
|
|
43
|
-
console.log(formatOutput(sections, format));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
console.log(bold("GPC Diff \u2014 Current State"));
|
|
47
|
-
console.log("");
|
|
48
|
-
console.log(bold("Releases"));
|
|
49
|
-
if (releases.length === 0) {
|
|
50
|
-
console.log(dim(" No releases found"));
|
|
51
|
-
} else {
|
|
52
|
-
for (const r of releases) {
|
|
53
|
-
const statusColor = r.status === "completed" ? green : r.status === "inProgress" ? yellow : dim;
|
|
54
|
-
const fraction = r.userFraction ? ` ${yellow(`${Math.round(r.userFraction * 100)}%`)}` : "";
|
|
55
|
-
const versions = r.versionCodes.join(", ");
|
|
56
|
-
console.log(
|
|
57
|
-
` ${r.track.padEnd(14)} ${statusColor(r.status.padEnd(12))} v${versions}${fraction}`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
console.log("");
|
|
62
|
-
if (trackDiff) {
|
|
63
|
-
console.log(bold(`Track Diff: ${trackDiff.fromTrack} \u2192 ${trackDiff.toTrack}`));
|
|
64
|
-
if (trackDiff.diffs.length === 0) {
|
|
65
|
-
console.log(dim(" No differences"));
|
|
66
|
-
} else {
|
|
67
|
-
for (const d of trackDiff.diffs) {
|
|
68
|
-
console.log(
|
|
69
|
-
` ${d.field.padEnd(16)} ${red(d.track1Value || "(empty)")} \u2192 ${green(d.track2Value || "(empty)")}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
console.log("");
|
|
74
|
-
}
|
|
75
|
-
if (metadataDiff && Array.isArray(metadataDiff)) {
|
|
76
|
-
console.log(bold("Metadata Diff (local vs remote)"));
|
|
77
|
-
if (metadataDiff.length === 0) {
|
|
78
|
-
console.log(dim(" No differences \u2014 local matches remote"));
|
|
79
|
-
} else {
|
|
80
|
-
for (const d of metadataDiff) {
|
|
81
|
-
const icon = d.status === "added" ? green("+") : d.status === "removed" ? red("-") : yellow("~");
|
|
82
|
-
console.log(` ${icon} ${d.language}/${d.field}: ${d.status}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
console.log("");
|
|
86
|
-
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
-
process.exit(4);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
export {
|
|
94
|
-
registerDiffCommand
|
|
95
|
-
};
|
|
96
|
-
//# sourceMappingURL=diff-V77SMKAQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/diff.ts"],"sourcesContent":["import { resolvePackageName } from \"../resolve.js\";\n// Named exports only. No default export.\n\nimport type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport { getReleasesStatus, diffReleases, diffListingsCommand, formatOutput } from \"@gpc-cli/core\";\nimport type { ReleaseDiff } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { green, red, yellow, dim, bold } from \"../colors.js\";\n\n\nexport function registerDiffCommand(program: Command): void {\n program\n .command(\"diff\")\n .description(\"Preview current release state and pending changes (read-only)\")\n .option(\"--from <track>\", \"Compare releases from this track\")\n .option(\"--to <track>\", \"Compare releases to this track\")\n .option(\"--metadata <dir>\", \"Compare local metadata directory vs remote listings\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n const client = createApiClient({ auth });\n\n try {\n const sections: Record<string, unknown> = {};\n\n // Always show release status across all tracks\n const releases = await getReleasesStatus(client, packageName);\n sections[\"releases\"] = releases;\n\n // Track-to-track diff if specified\n let trackDiff: { fromTrack: string; toTrack: string; diffs: ReleaseDiff[] } | undefined;\n if (options.from && options.to) {\n trackDiff = await diffReleases(client, packageName, options.from, options.to);\n sections[\"trackDiff\"] = trackDiff;\n }\n\n // Metadata diff if specified\n let metadataDiff: unknown;\n if (options.metadata) {\n metadataDiff = await diffListingsCommand(client, packageName, options.metadata);\n sections[\"metadata\"] = metadataDiff;\n }\n\n if (format === \"json\") {\n console.log(formatOutput(sections, format));\n return;\n }\n\n // Human-readable output\n console.log(bold(\"GPC Diff — Current State\"));\n console.log(\"\");\n\n // Release status\n console.log(bold(\"Releases\"));\n if (releases.length === 0) {\n console.log(dim(\" No releases found\"));\n } else {\n for (const r of releases) {\n const statusColor =\n r.status === \"completed\" ? green : r.status === \"inProgress\" ? yellow : dim;\n const fraction = r.userFraction\n ? ` ${yellow(`${Math.round(r.userFraction * 100)}%`)}`\n : \"\";\n const versions = r.versionCodes.join(\", \");\n console.log(\n ` ${r.track.padEnd(14)} ${statusColor(r.status.padEnd(12))} v${versions}${fraction}`,\n );\n }\n }\n console.log(\"\");\n\n // Track diff\n if (trackDiff) {\n console.log(bold(`Track Diff: ${trackDiff.fromTrack} → ${trackDiff.toTrack}`));\n if (trackDiff.diffs.length === 0) {\n console.log(dim(\" No differences\"));\n } else {\n for (const d of trackDiff.diffs) {\n console.log(\n ` ${d.field.padEnd(16)} ${red(d.track1Value || \"(empty)\")} → ${green(d.track2Value || \"(empty)\")}`,\n );\n }\n }\n console.log(\"\");\n }\n\n // Metadata diff\n if (metadataDiff && Array.isArray(metadataDiff)) {\n console.log(bold(\"Metadata Diff (local vs remote)\"));\n if (metadataDiff.length === 0) {\n console.log(dim(\" No differences — local matches remote\"));\n } else {\n for (const d of metadataDiff as Array<{\n language: string;\n field: string;\n status: string;\n }>) {\n const icon =\n d.status === \"added\" ? green(\"+\") : d.status === \"removed\" ? red(\"-\") : yellow(\"~\");\n console.log(` ${icon} ${d.language}/${d.field}: ${d.status}`);\n }\n }\n console.log(\"\");\n }\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAIA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB,cAAc,qBAAqB,oBAAoB;AAM5E,SAAS,oBAAoB,SAAwB;AAC1D,UACG,QAAQ,MAAM,EACd,YAAY,+DAA+D,EAC3E,OAAO,kBAAkB,kCAAkC,EAC3D,OAAO,gBAAgB,gCAAgC,EACvD,OAAO,oBAAoB,qDAAqD,EAChF,OAAO,OAAO,YAAY;AACzB,UAAM,SAAS,MAAM,WAAW;AAChC,UAAM,cAAc,mBAAmB,QAAQ,KAAK,EAAE,KAAK,GAAG,MAAM;AACpE,UAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,UAAM,OAAO,MAAM,YAAY,EAAE,oBAAoB,OAAO,MAAM,eAAe,CAAC;AAClF,UAAM,SAAS,gBAAgB,EAAE,KAAK,CAAC;AAEvC,QAAI;AACF,YAAM,WAAoC,CAAC;AAG3C,YAAM,WAAW,MAAM,kBAAkB,QAAQ,WAAW;AAC5D,eAAS,UAAU,IAAI;AAGvB,UAAI;AACJ,UAAI,QAAQ,QAAQ,QAAQ,IAAI;AAC9B,oBAAY,MAAM,aAAa,QAAQ,aAAa,QAAQ,MAAM,QAAQ,EAAE;AAC5E,iBAAS,WAAW,IAAI;AAAA,MAC1B;AAGA,UAAI;AACJ,UAAI,QAAQ,UAAU;AACpB,uBAAe,MAAM,oBAAoB,QAAQ,aAAa,QAAQ,QAAQ;AAC9E,iBAAS,UAAU,IAAI;AAAA,MACzB;AAEA,UAAI,WAAW,QAAQ;AACrB,gBAAQ,IAAI,aAAa,UAAU,MAAM,CAAC;AAC1C;AAAA,MACF;AAGA,cAAQ,IAAI,KAAK,+BAA0B,CAAC;AAC5C,cAAQ,IAAI,EAAE;AAGd,cAAQ,IAAI,KAAK,UAAU,CAAC;AAC5B,UAAI,SAAS,WAAW,GAAG;AACzB,gBAAQ,IAAI,IAAI,qBAAqB,CAAC;AAAA,MACxC,OAAO;AACL,mBAAW,KAAK,UAAU;AACxB,gBAAM,cACJ,EAAE,WAAW,cAAc,QAAQ,EAAE,WAAW,eAAe,SAAS;AAC1E,gBAAM,WAAW,EAAE,eACf,IAAI,OAAO,GAAG,KAAK,MAAM,EAAE,eAAe,GAAG,CAAC,GAAG,CAAC,KAClD;AACJ,gBAAM,WAAW,EAAE,aAAa,KAAK,IAAI;AACzC,kBAAQ;AAAA,YACN,KAAK,EAAE,MAAM,OAAO,EAAE,CAAC,IAAI,YAAY,EAAE,OAAO,OAAO,EAAE,CAAC,CAAC,KAAK,QAAQ,GAAG,QAAQ;AAAA,UACrF;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,EAAE;AAGd,UAAI,WAAW;AACb,gBAAQ,IAAI,KAAK,eAAe,UAAU,SAAS,WAAM,UAAU,OAAO,EAAE,CAAC;AAC7E,YAAI,UAAU,MAAM,WAAW,GAAG;AAChC,kBAAQ,IAAI,IAAI,kBAAkB,CAAC;AAAA,QACrC,OAAO;AACL,qBAAW,KAAK,UAAU,OAAO;AAC/B,oBAAQ;AAAA,cACN,KAAK,EAAE,MAAM,OAAO,EAAE,CAAC,IAAI,IAAI,EAAE,eAAe,SAAS,CAAC,WAAM,MAAM,EAAE,eAAe,SAAS,CAAC;AAAA,YACnG;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,IAAI,EAAE;AAAA,MAChB;AAGA,UAAI,gBAAgB,MAAM,QAAQ,YAAY,GAAG;AAC/C,gBAAQ,IAAI,KAAK,iCAAiC,CAAC;AACnD,YAAI,aAAa,WAAW,GAAG;AAC7B,kBAAQ,IAAI,IAAI,8CAAyC,CAAC;AAAA,QAC5D,OAAO;AACL,qBAAW,KAAK,cAIZ;AACF,kBAAM,OACJ,EAAE,WAAW,UAAU,MAAM,GAAG,IAAI,EAAE,WAAW,YAAY,IAAI,GAAG,IAAI,OAAO,GAAG;AACpF,oBAAQ,IAAI,KAAK,IAAI,IAAI,EAAE,QAAQ,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,EAAE;AAAA,UAC/D;AAAA,QACF;AACA,gBAAQ,IAAI,EAAE;AAAA,MAChB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AACL;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/docs.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport * as cp from \"node:child_process\";\n\nconst PAGE_MAP: Record<string, string> = {\n releases: \"commands/releases\",\n status: \"commands/status\",\n vitals: \"commands/vitals\",\n reviews: \"commands/reviews\",\n listings: \"commands/listings\",\n subscriptions: \"commands/subscriptions\",\n bundle: \"commands/bundle\",\n users: \"commands/users\",\n audit: \"commands/audit\",\n config: \"commands/config\",\n doctor: \"commands/doctor\",\n publish: \"commands/publish\",\n};\n\nconst BASE = \"https://yasserstudio.github.io/gpc/\";\n\nexport function registerDocsCommand(program: Command): void {\n program\n .command(\"docs [topic]\")\n .description(\"Open documentation in browser\")\n .option(\"--list\", \"List available documentation topics\")\n .action((topic?: string, opts?: { list?: boolean }) => {\n if (opts?.list) {\n console.log(\"Available topics:\");\n for (const key of Object.keys(PAGE_MAP)) console.log(` gpc docs ${key}`);\n return;\n }\n const path = topic ? PAGE_MAP[topic] : undefined;\n if (topic && !path) {\n console.error(`Unknown topic \"${topic}\". Run: gpc docs --list`);\n process.exit(2);\n }\n const url = path ? `${BASE}${path}` : BASE;\n const platform = process.platform;\n const cmd = platform === \"darwin\" ? \"open\" : platform === \"win32\" ? \"start\" : \"xdg-open\";\n cp.execFile(cmd, [url], (error) => {\n if (error) {\n console.log(`Open in your browser: ${url}`);\n }\n });\n });\n}\n"],"mappings":";;;AACA,YAAY,QAAQ;AAEpB,IAAM,WAAmC;AAAA,EACvC,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,IAAM,OAAO;AAEN,SAAS,oBAAoB,SAAwB;AAC1D,UACG,QAAQ,cAAc,EACtB,YAAY,+BAA+B,EAC3C,OAAO,UAAU,qCAAqC,EACtD,OAAO,CAAC,OAAgB,SAA8B;AACrD,QAAI,MAAM,MAAM;AACd,cAAQ,IAAI,mBAAmB;AAC/B,iBAAW,OAAO,OAAO,KAAK,QAAQ,EAAG,SAAQ,IAAI,cAAc,GAAG,EAAE;AACxE;AAAA,IACF;AACA,UAAM,OAAO,QAAQ,SAAS,KAAK,IAAI;AACvC,QAAI,SAAS,CAAC,MAAM;AAClB,cAAQ,MAAM,kBAAkB,KAAK,yBAAyB;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,MAAM,OAAO,GAAG,IAAI,GAAG,IAAI,KAAK;AACtC,UAAM,WAAW,QAAQ;AACzB,UAAM,MAAM,aAAa,WAAW,SAAS,aAAa,UAAU,UAAU;AAC9E,IAAG,YAAS,KAAK,CAAC,GAAG,GAAG,CAAC,UAAU;AACjC,UAAI,OAAO;AACT,gBAAQ,IAAI,yBAAyB,GAAG,EAAE;AAAA,MAC5C;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACL;","names":[]}
|
package/dist/doctor-3Z4ARPM2.js
DELETED
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
green,
|
|
4
|
-
red,
|
|
5
|
-
yellow
|
|
6
|
-
} from "./chunk-FAN4ZITI.js";
|
|
7
|
-
|
|
8
|
-
// src/commands/doctor.ts
|
|
9
|
-
import { loadConfig, getCacheDir, getConfigDir } from "@gpc-cli/config";
|
|
10
|
-
import { resolveAuth, AuthError } from "@gpc-cli/auth";
|
|
11
|
-
import { existsSync, accessSync, statSync, constants } from "fs";
|
|
12
|
-
import { resolve } from "path";
|
|
13
|
-
import { lookup } from "dns/promises";
|
|
14
|
-
var PASS = "\u2713";
|
|
15
|
-
var FAIL = "\u2717";
|
|
16
|
-
var WARN = "\u26A0";
|
|
17
|
-
var INFO = "-";
|
|
18
|
-
function icon(status) {
|
|
19
|
-
switch (status) {
|
|
20
|
-
case "pass":
|
|
21
|
-
return green(PASS);
|
|
22
|
-
case "fail":
|
|
23
|
-
return red(FAIL);
|
|
24
|
-
case "warn":
|
|
25
|
-
return yellow(WARN);
|
|
26
|
-
case "info":
|
|
27
|
-
return INFO;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
var ANDROID_PACKAGE_RE = /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/;
|
|
31
|
-
function checkNodeVersion(nodeVersion) {
|
|
32
|
-
const major = parseInt(nodeVersion.split(".")[0] ?? "0", 10);
|
|
33
|
-
return major >= 20 ? { name: "node", status: "pass", message: `Node.js ${nodeVersion}` } : {
|
|
34
|
-
name: "node",
|
|
35
|
-
status: "fail",
|
|
36
|
-
message: `Node.js ${nodeVersion} (requires >=20)`,
|
|
37
|
-
suggestion: "Upgrade Node.js to v20 or later: https://nodejs.org"
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function checkPackageName(app) {
|
|
41
|
-
if (!app) return null;
|
|
42
|
-
return ANDROID_PACKAGE_RE.test(app) ? { name: "package-name", status: "pass", message: `Package name format OK: ${app}` } : {
|
|
43
|
-
name: "package-name",
|
|
44
|
-
status: "warn",
|
|
45
|
-
message: `Package name may be invalid: ${app}`,
|
|
46
|
-
suggestion: "Android package names must have 2+ dot-separated segments, each starting with a letter (e.g. com.example.app)"
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function checkProxy(url) {
|
|
50
|
-
if (!url) return null;
|
|
51
|
-
try {
|
|
52
|
-
new URL(url);
|
|
53
|
-
return { name: "proxy", status: "pass", message: `Proxy configured: ${url}` };
|
|
54
|
-
} catch {
|
|
55
|
-
return {
|
|
56
|
-
name: "proxy",
|
|
57
|
-
status: "warn",
|
|
58
|
-
message: `Invalid proxy URL: ${url}`,
|
|
59
|
-
suggestion: "Set HTTPS_PROXY to a valid URL (e.g. http://proxy.example.com:8080)"
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async function applyFix(check) {
|
|
64
|
-
switch (check.name) {
|
|
65
|
-
case "config-dir":
|
|
66
|
-
case "cache-dir": {
|
|
67
|
-
const dirMatch = check.message.match(/: (.+)$/);
|
|
68
|
-
if (!dirMatch?.[1]) return null;
|
|
69
|
-
const { mkdir } = await import("fs/promises");
|
|
70
|
-
await mkdir(dirMatch[1], {
|
|
71
|
-
recursive: true,
|
|
72
|
-
mode: check.name === "cache-dir" ? 448 : 493
|
|
73
|
-
});
|
|
74
|
-
return `Created ${dirMatch[1]}`;
|
|
75
|
-
}
|
|
76
|
-
case "service-account-permissions": {
|
|
77
|
-
const saPath = check.suggestion?.match(/chmod 600 (.+)$/)?.[1];
|
|
78
|
-
if (!saPath) return null;
|
|
79
|
-
const { chmod } = await import("fs/promises");
|
|
80
|
-
await chmod(saPath, 384);
|
|
81
|
-
return `Fixed permissions on ${saPath}`;
|
|
82
|
-
}
|
|
83
|
-
case "config": {
|
|
84
|
-
const { initConfig } = await import("@gpc-cli/config");
|
|
85
|
-
await initConfig({});
|
|
86
|
-
return "Initialized config file";
|
|
87
|
-
}
|
|
88
|
-
default:
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function registerDoctorCommand(program) {
|
|
93
|
-
program.command("doctor").description("Verify setup and connectivity").option("--fix", "Attempt to auto-fix failing checks").action(async (opts, cmd) => {
|
|
94
|
-
const results = [];
|
|
95
|
-
const parentOpts = cmd.parent?.opts() ?? {};
|
|
96
|
-
const jsonMode = !!(parentOpts["json"] || parentOpts["output"] === "json");
|
|
97
|
-
results.push(checkNodeVersion(process.versions.node));
|
|
98
|
-
let config;
|
|
99
|
-
try {
|
|
100
|
-
config = await loadConfig();
|
|
101
|
-
results.push({ name: "config", status: "pass", message: "Configuration loaded" });
|
|
102
|
-
if (config.app) {
|
|
103
|
-
results.push({
|
|
104
|
-
name: "default-app",
|
|
105
|
-
status: "pass",
|
|
106
|
-
message: `Default app: ${config.app}`
|
|
107
|
-
});
|
|
108
|
-
const pkgCheck = checkPackageName(config.app);
|
|
109
|
-
if (pkgCheck) results.push(pkgCheck);
|
|
110
|
-
} else {
|
|
111
|
-
results.push({
|
|
112
|
-
name: "default-app",
|
|
113
|
-
status: "info",
|
|
114
|
-
message: "No default app configured",
|
|
115
|
-
suggestion: "Use --app flag or run: gpc config set app <package>"
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
results.push({
|
|
120
|
-
name: "config",
|
|
121
|
-
status: "fail",
|
|
122
|
-
message: "Configuration could not be loaded",
|
|
123
|
-
suggestion: "Run gpc config init to create a config file, or check .gpcrc.json for syntax errors"
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const configDir = getConfigDir();
|
|
127
|
-
try {
|
|
128
|
-
if (existsSync(configDir)) {
|
|
129
|
-
accessSync(configDir, constants.R_OK | constants.W_OK);
|
|
130
|
-
results.push({
|
|
131
|
-
name: "config-dir",
|
|
132
|
-
status: "pass",
|
|
133
|
-
message: `Config directory: ${configDir}`
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
results.push({
|
|
137
|
-
name: "config-dir",
|
|
138
|
-
status: "info",
|
|
139
|
-
message: `Config directory does not exist yet: ${configDir}`
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
} catch {
|
|
143
|
-
results.push({
|
|
144
|
-
name: "config-dir",
|
|
145
|
-
status: "warn",
|
|
146
|
-
message: `Config directory not writable: ${configDir}`,
|
|
147
|
-
suggestion: `Fix permissions: chmod 755 ${configDir}`
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
const cacheDir = getCacheDir();
|
|
151
|
-
try {
|
|
152
|
-
if (existsSync(cacheDir)) {
|
|
153
|
-
accessSync(cacheDir, constants.R_OK | constants.W_OK);
|
|
154
|
-
results.push({
|
|
155
|
-
name: "cache-dir",
|
|
156
|
-
status: "pass",
|
|
157
|
-
message: `Cache directory: ${cacheDir}`
|
|
158
|
-
});
|
|
159
|
-
} else {
|
|
160
|
-
results.push({
|
|
161
|
-
name: "cache-dir",
|
|
162
|
-
status: "info",
|
|
163
|
-
message: `Cache directory does not exist yet: ${cacheDir}`
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
} catch {
|
|
167
|
-
results.push({
|
|
168
|
-
name: "cache-dir",
|
|
169
|
-
status: "warn",
|
|
170
|
-
message: `Cache directory not writable: ${cacheDir}`,
|
|
171
|
-
suggestion: `Fix permissions: chmod 700 ${cacheDir}`
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (config?.auth?.serviceAccount) {
|
|
175
|
-
const saValue = config.auth.serviceAccount;
|
|
176
|
-
const looksLikePath = !saValue.trim().startsWith("{");
|
|
177
|
-
if (looksLikePath) {
|
|
178
|
-
const saPath = resolve(saValue);
|
|
179
|
-
if (existsSync(saPath)) {
|
|
180
|
-
try {
|
|
181
|
-
accessSync(saPath, constants.R_OK);
|
|
182
|
-
results.push({
|
|
183
|
-
name: "service-account-file",
|
|
184
|
-
status: "pass",
|
|
185
|
-
message: `Service account file: ${saPath}`
|
|
186
|
-
});
|
|
187
|
-
} catch {
|
|
188
|
-
results.push({
|
|
189
|
-
name: "service-account-file",
|
|
190
|
-
status: "fail",
|
|
191
|
-
message: `Service account file not readable: ${saPath}`,
|
|
192
|
-
suggestion: `Fix permissions: chmod 600 ${saPath}`
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
if (process.platform !== "win32") {
|
|
196
|
-
try {
|
|
197
|
-
const mode = statSync(saPath).mode;
|
|
198
|
-
const groupRead = (mode & 32) !== 0;
|
|
199
|
-
const worldRead = (mode & 4) !== 0;
|
|
200
|
-
if (groupRead || worldRead) {
|
|
201
|
-
results.push({
|
|
202
|
-
name: "service-account-permissions",
|
|
203
|
-
status: "warn",
|
|
204
|
-
message: `Service account file is group/world-readable (mode: ${(mode & 511).toString(8)})`,
|
|
205
|
-
suggestion: `Restrict permissions: chmod 600 ${saPath}`
|
|
206
|
-
});
|
|
207
|
-
} else {
|
|
208
|
-
results.push({
|
|
209
|
-
name: "service-account-permissions",
|
|
210
|
-
status: "pass",
|
|
211
|
-
message: `Service account file permissions OK (mode: ${(mode & 511).toString(8)})`
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
} else {
|
|
218
|
-
results.push({
|
|
219
|
-
name: "service-account-file",
|
|
220
|
-
status: "fail",
|
|
221
|
-
message: `Service account file not found: ${saPath}`,
|
|
222
|
-
suggestion: "Check the path in your config or GPC_SERVICE_ACCOUNT env var"
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
const gpcProfile = process.env["GPC_PROFILE"];
|
|
228
|
-
if (gpcProfile && config) {
|
|
229
|
-
if (config.profiles && gpcProfile in config.profiles) {
|
|
230
|
-
results.push({
|
|
231
|
-
name: "profile",
|
|
232
|
-
status: "pass",
|
|
233
|
-
message: `Profile "${gpcProfile}" found`
|
|
234
|
-
});
|
|
235
|
-
} else {
|
|
236
|
-
const available = config.profiles ? Object.keys(config.profiles).join(", ") : "";
|
|
237
|
-
results.push({
|
|
238
|
-
name: "profile",
|
|
239
|
-
status: "fail",
|
|
240
|
-
message: `Profile "${gpcProfile}" not found`,
|
|
241
|
-
suggestion: available ? `Available profiles: ${available}. Create with: gpc auth login --profile ${gpcProfile}` : `No profiles defined. Create one with: gpc auth login --profile ${gpcProfile}`
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
const proxyUrl = process.env["HTTPS_PROXY"] || process.env["https_proxy"] || process.env["HTTP_PROXY"] || process.env["http_proxy"];
|
|
246
|
-
const proxyCheck = checkProxy(proxyUrl);
|
|
247
|
-
if (proxyCheck) results.push(proxyCheck);
|
|
248
|
-
const caCert = process.env["GPC_CA_CERT"] || process.env["NODE_EXTRA_CA_CERTS"];
|
|
249
|
-
if (caCert) {
|
|
250
|
-
if (existsSync(caCert)) {
|
|
251
|
-
results.push({
|
|
252
|
-
name: "ca-cert",
|
|
253
|
-
status: "pass",
|
|
254
|
-
message: `CA certificate: ${caCert}`
|
|
255
|
-
});
|
|
256
|
-
} else {
|
|
257
|
-
results.push({
|
|
258
|
-
name: "ca-cert",
|
|
259
|
-
status: "warn",
|
|
260
|
-
message: `CA certificate file not found: ${caCert}`,
|
|
261
|
-
suggestion: "Check that GPC_CA_CERT points to an existing PEM file"
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
const dnsHosts = ["androidpublisher.googleapis.com", "playdeveloperreporting.googleapis.com"];
|
|
266
|
-
for (const host of dnsHosts) {
|
|
267
|
-
try {
|
|
268
|
-
await lookup(host);
|
|
269
|
-
results.push({
|
|
270
|
-
name: "dns",
|
|
271
|
-
status: "pass",
|
|
272
|
-
message: `DNS: ${host}`
|
|
273
|
-
});
|
|
274
|
-
} catch {
|
|
275
|
-
results.push({
|
|
276
|
-
name: "dns",
|
|
277
|
-
status: "fail",
|
|
278
|
-
message: `Cannot resolve ${host}`,
|
|
279
|
-
suggestion: "Check your DNS settings and network connection"
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
const authConfig = config ?? await loadConfig();
|
|
285
|
-
const client = await resolveAuth({
|
|
286
|
-
serviceAccountPath: authConfig.auth?.serviceAccount
|
|
287
|
-
});
|
|
288
|
-
results.push({
|
|
289
|
-
name: "auth",
|
|
290
|
-
status: "pass",
|
|
291
|
-
message: `Authenticated as ${client.getClientEmail()}`
|
|
292
|
-
});
|
|
293
|
-
await client.getAccessToken();
|
|
294
|
-
results.push({
|
|
295
|
-
name: "api-connectivity",
|
|
296
|
-
status: "pass",
|
|
297
|
-
message: "API connectivity verified"
|
|
298
|
-
});
|
|
299
|
-
} catch (error) {
|
|
300
|
-
if (error instanceof AuthError) {
|
|
301
|
-
results.push({
|
|
302
|
-
name: "auth",
|
|
303
|
-
status: "fail",
|
|
304
|
-
message: `Authentication: ${error.message}`,
|
|
305
|
-
suggestion: error.suggestion
|
|
306
|
-
});
|
|
307
|
-
} else {
|
|
308
|
-
results.push({
|
|
309
|
-
name: "api-connectivity",
|
|
310
|
-
status: "fail",
|
|
311
|
-
message: "API connectivity failed",
|
|
312
|
-
suggestion: "Check your network connection and credentials"
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (opts["fix"]) {
|
|
317
|
-
for (const r of results) {
|
|
318
|
-
if (r.status === "fail" || r.status === "warn") {
|
|
319
|
-
try {
|
|
320
|
-
const fixMsg = await applyFix(r);
|
|
321
|
-
if (fixMsg) {
|
|
322
|
-
console.log(` ${green("\u2192")} Fixed: ${fixMsg}`);
|
|
323
|
-
r.status = "pass";
|
|
324
|
-
r.message += " (fixed)";
|
|
325
|
-
}
|
|
326
|
-
} catch (err) {
|
|
327
|
-
console.error(
|
|
328
|
-
` ${red("\u2717")} Could not fix "${r.name}": ${err instanceof Error ? err.message : String(err)}`
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
const errors = results.filter((r) => r.status === "fail").length;
|
|
335
|
-
const warnings = results.filter((r) => r.status === "warn").length;
|
|
336
|
-
const passed = results.filter((r) => r.status === "pass").length;
|
|
337
|
-
if (jsonMode) {
|
|
338
|
-
console.log(
|
|
339
|
-
JSON.stringify({ success: errors === 0, errors, warnings, checks: results }, null, 2)
|
|
340
|
-
);
|
|
341
|
-
if (errors > 0) process.exit(1);
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
console.log("GPC Doctor\n");
|
|
345
|
-
for (const r of results) {
|
|
346
|
-
console.log(` ${icon(r.status)} ${r.message}`);
|
|
347
|
-
if (r.suggestion && r.status !== "pass") {
|
|
348
|
-
console.log(` ${r.suggestion}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
console.log(
|
|
352
|
-
`
|
|
353
|
-
${PASS} ${passed} passed ${WARN} ${warnings} warning${warnings !== 1 ? "s" : ""} ${FAIL} ${errors} failed`
|
|
354
|
-
);
|
|
355
|
-
if (errors > 0) {
|
|
356
|
-
console.log("\nSome checks failed. Fix the issues above and run again.");
|
|
357
|
-
process.exit(1);
|
|
358
|
-
} else if (warnings > 0) {
|
|
359
|
-
console.log("\nAll checks passed with warnings.");
|
|
360
|
-
} else {
|
|
361
|
-
console.log(`
|
|
362
|
-
${green("\u2713")} Ready. Try: gpc status`);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
export {
|
|
367
|
-
checkNodeVersion,
|
|
368
|
-
checkPackageName,
|
|
369
|
-
checkProxy,
|
|
370
|
-
registerDoctorCommand
|
|
371
|
-
};
|
|
372
|
-
//# sourceMappingURL=doctor-3Z4ARPM2.js.map
|