-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathindex.ts
More file actions
157 lines (143 loc) · 5.76 KB
/
Copy pathindex.ts
File metadata and controls
157 lines (143 loc) · 5.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import type { ProgramConfig } from '@lib/programs/program-step';
import type { ProgramRun } from '@lib/agent/agent-runner';
import type { WizardSession } from '@lib/wizard-session';
import { OutroKind } from '@lib/wizard-session';
import { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';
import {
buildSourceMapsUploadPrompt,
SOURCE_MAPS_DETECTION_FAILED_PROMPT,
} from './prompt.js';
import {
SOURCE_MAPS_ABORT_CASES,
SOURCE_MAPS_CONTEXT_KEYS,
VARIANTS_REQUIRING_POSTHOG_CLI,
type SkillVariant,
} from './detect.js';
import { getContentBlocks } from './content/index.js';
import { getUI } from '@ui';
import { installOrUpdatePostHogCli } from '@steps/install-cli-steering';
import { analytics } from '@utils/analytics';
const REPORT_FILE = 'posthog-source-maps-report.md';
const DOCS_URL = 'https://posthog.com/docs/error-tracking/upload-source-maps';
let postHogCliInstallAttempted = false;
/**
* Pre-install posthog-cli for variants that need a machine-global copy
* (`VARIANTS_REQUIRING_POSTHOG_CLI`). The agent can't — warlock blocks
* `npm install -g` — so the wizard does it in-process. Warn, don't fail.
*/
function ensurePostHogCli(variant: SkillVariant): void {
if (postHogCliInstallAttempted) return;
postHogCliInstallAttempted = true;
const result = installOrUpdatePostHogCli();
if (!result.success) {
analytics.wizardCapture('source maps posthog-cli preinstall failed', {
variant,
error: String(result.error).slice(0, 500),
});
analytics.captureException(
result.errorObject ??
new Error(`posthog-cli pre-install failed: ${result.error}`),
{ source: 'source_maps_cli_preinstall', variant },
);
getUI().log.warn(
`Could not pre-install posthog-cli (${result.error}). Your Xcode build ` +
`will fail to upload dSYMs until it's installed: npm install -g @posthog/cli@latest`,
);
}
}
export const errorTrackingUploadSourceMapsConfig: ProgramConfig = {
command: 'upload-source-maps',
description: 'Upload source maps to PostHog Error Tracking',
id: 'error-tracking-upload-source-maps',
requiresAi: true,
steps: ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM,
reportFile: REPORT_FILE,
getContentBlocks,
requires: ['posthog-integration'],
run: (_session: WizardSession): Promise<ProgramRun> => {
// Read the picked project LIVE at prompt-build time, not here: the picker
// screen runs AFTER this run config is resolved (post-auth), and the store
// forks the session reference, so the `session` passed in never sees the
// choice. getUI().getFrameworkContext reads the live store session.
const readSelection = () => {
const variant = getUI().getFrameworkContext(
SOURCE_MAPS_CONTEXT_KEYS.selectedVariant,
) as SkillVariant | undefined;
const displayName = getUI().getFrameworkContext(
SOURCE_MAPS_CONTEXT_KEYS.selectedDisplayName,
) as string | undefined;
const projectPath = getUI().getFrameworkContext(
SOURCE_MAPS_CONTEXT_KEYS.selectedPath,
) as string | undefined;
const skillId = variant
? `error-tracking-upload-source-maps-${variant}`
: undefined;
return { variant, displayName, projectPath, skillId };
};
return Promise.resolve({
integrationLabel: 'error-tracking-upload-source-maps',
// Skill is installed by the agent (after the API-key choice is made)
// rather than pre-installed by the runner, so leave skillId unset.
successMessage: 'Source maps wired up!',
reportFile: REPORT_FILE,
docsUrl: DOCS_URL,
spinnerMessage: 'Wiring up source maps...',
estimatedDurationMinutes: 3,
abortCases: SOURCE_MAPS_ABORT_CASES,
// The flow parks on wizard_ask while the user does slow work — create
// a personal API key in the browser (STEP 1), or run a production
// build, trigger the test error, and check Error Tracking (STEP 8).
// The 5-minute default cancels the question mid-task and the agent
// wraps up to the outro, so give these answers half an hour.
askTimeoutMs: 30 * 60 * 1000,
customPrompt: (ctx) => {
const { variant, displayName, projectPath, skillId } = readSelection();
if (!skillId || !variant) {
// No project was selected — abort with a structured signal so the
// runner renders a friendly outro.
return SOURCE_MAPS_DETECTION_FAILED_PROMPT;
}
if (VARIANTS_REQUIRING_POSTHOG_CLI.has(variant))
ensurePostHogCli(variant);
const uiHost = ctx.host.appHost.replace(/\/$/, '');
return buildSourceMapsUploadPrompt({
displayName,
variant,
skillId,
projectPath,
projectId: ctx.projectId,
host: ctx.host.apiHost,
settingsUrl: `${uiHost}/project/${ctx.projectId}/settings/user-api-keys`,
uiHost,
});
},
postRun: () => {
// Stash a hint for the outro about what variant we shipped.
const { variant } = readSelection();
if (variant) {
getUI().setFrameworkContext('sourceMapsCompletedVariant', variant);
}
return Promise.resolve();
},
buildOutroData: () => {
// SourceMapsOutroScreen renders static "what we did + how it works"
// guidance, so no per-run `changes` list is needed here.
return {
kind: OutroKind.Success as const,
message: 'Source maps wired up!',
reportFile: REPORT_FILE,
docsUrl: DOCS_URL,
};
},
});
},
};
export { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';
export {
detectSourceMapsPrerequisites,
SOURCE_MAPS_ABORT_CASES,
SOURCE_MAPS_CONTEXT_KEYS,
VARIANT_DISPLAY_NAME,
type SkillVariant,
type SourceMapsDetectError,
} from './detect.js';