Console pages as absolute deep links: cli/lib/console-links.ts builds the URL of every App Store Connect / Play Console page from apple.appId, play.developerId and play.appId (app list / developer chooser when unknown); every plain 'App Store Connect > ...' / 'Play Console > ...' direction in cli/ and templates/ now carries it, validate / scan / declarations / release plan JSON carry url, the desktop shows an Open button; tests/console-links.test.ts guards it; AGENTS rule 11, pitfall 71

This commit is contained in:
CRTkafa 2026-10-15 19:55:37 +08:00
parent f898d642d0
commit e500e88e40
50 changed files with 381 additions and 92 deletions

View file

@ -16,6 +16,7 @@
// https://developer.apple.com/documentation/appstoreconnectapi/power-and-performance-metrics-and-logs
// GET /v1/apps/{id}/perfPowerMetrics (application/vnd.apple.xcode-metrics+json), GET /v1/builds/{id}/diagnosticSignatures
// (HANGS, DISK_WRITES, LAUNCHES), GET /v1/apps/{id}/betaFeedbackCrashSubmissions (TestFlight crash reports, API 4.0+).
import { CONSOLE_PAGES, consoleLink, consoleUrl } from './console-links';
import { type CrashHint, crashHint } from './crash-hints';
import type { Finding, Level } from './validate-rules';
import { mdTable, pct, textTable } from './table';
@ -265,7 +266,7 @@ export function healthFindings(h: HealthSnapshot, o: { unreadable?: Level; princ
f.push(g?.kind === 'api-disabled' || REPORTING_OFF.test(raw)
? { store: 'play', level: o.unreadable ?? 'warn', code: 'vitals', msg: `vitals not readable: the ${g?.kind === 'api-disabled' ? g.serviceTitle : 'Google Play Developer Reporting API'} is off in the service account's Google Cloud project${(g?.kind === 'api-disabled' ? g.projectId : on.project) ? ` ${g?.kind === 'api-disabled' ? g.projectId : on.project}` : ''}`,
fix: `enable "${g?.kind === 'api-disabled' ? g.serviceTitle : 'Google Play Developer Reporting API'}" for that project: ${g?.kind === 'api-disabled' ? g.activationUrl : on.url} (Enable), wait a few minutes (store-health skill)` }
: { store: 'play', level: o.unreadable ?? 'warn', code: 'vitals', msg: `vitals not readable: ${g ? findingText(g) : plainError(raw).slice(0, 200)}`, fix: g?.kind === 'no-app-access' ? `Play Console > Users and permissions: ${g.usersUrl}` : 'store-health skill (Reporting API access)' });
: { store: 'play', level: o.unreadable ?? 'warn', code: 'vitals', msg: `vitals not readable: ${g ? findingText(g) : plainError(raw).slice(0, 200)}`, fix: g?.kind === 'no-app-access' ? `${CONSOLE_PAGES['play.users'].label}: ${g.usersUrl}` : 'store-health skill (Reporting API access)' });
}
else if (play) {
const crash = lastValue(play.crash, 'userPerceivedCrashRate28dUserWeighted') ?? weightedAverage(play.crash, 'userPerceivedCrashRate');
@ -276,7 +277,7 @@ export function healthFindings(h: HealthSnapshot, o: { unreadable?: Level; princ
f.push({ store: 'play', level: levelFor(crash, PLAY_THRESHOLDS.crash), code: 'vitals-crash', msg: `user-perceived crash rate (28 d) ${pct(crash)} (bad behavior threshold ${pct(PLAY_THRESHOLDS.crash)})`, fix });
f.push({ store: 'play', level: levelFor(anr, PLAY_THRESHOLDS.anr), code: 'vitals-anr', msg: `user-perceived ANR rate (28 d) ${pct(anr)} (bad behavior threshold ${pct(PLAY_THRESHOLDS.anr)})`, fix });
}
if (play.issuesError) f.push({ store: 'play', level: 'warn', code: 'vitals-issues', msg: `top crash / ANR issues not read: ${play.issuesError.slice(0, 200)}`, fix: 'Play Console > Android vitals > Crashes and ANRs' });
if (play.issuesError) f.push({ store: 'play', level: 'warn', code: 'vitals-issues', msg: `top crash / ANR issues not read: ${play.issuesError.slice(0, 200)}`, fix: consoleLink('play.vitals', '> Crashes and ANRs'), url: consoleUrl('play.vitals') });
for (const d of play.devices ?? []) {
const worst = Math.max(d.crash ?? 0, d.anr ?? 0);
const lv = levelFor(worst, PLAY_THRESHOLDS.perDevice);