@posthog/wizard 2.61.0 → 2.63.0
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 +48 -6
- package/dist/{add-mcp-server-to-clients-KNIHRN_m.js → add-mcp-server-to-clients-Bsyb6MUB.js} +4 -4
- package/dist/{add-mcp-server-to-clients-KNIHRN_m.js.map → add-mcp-server-to-clients-Bsyb6MUB.js.map} +1 -1
- package/dist/{agent-interface-Bm4ZNGGV.js → agent-interface-C4CV7p0L.js} +24 -16
- package/dist/agent-interface-C4CV7p0L.js.map +1 -0
- package/dist/{agent-prompt-loader-CsHNQXsY.js → agent-prompt-loader-DiM8xfSZ.js} +13 -6
- package/dist/agent-prompt-loader-DiM8xfSZ.js.map +1 -0
- package/dist/{agent-runner-DLAvB67P.js → agent-runner-X3JWS2WK.js} +230 -40
- package/dist/agent-runner-X3JWS2WK.js.map +1 -0
- package/dist/analytics-F6z7o3N2.js +2 -0
- package/dist/{analytics-CkIyBHfd.js → analytics-YLZKudpE.js} +2 -2
- package/dist/{analytics-CkIyBHfd.js.map → analytics-YLZKudpE.js.map} +1 -1
- package/dist/{api-XHQTOfIi.js → api-DlRJAFI2.js} +3 -3
- package/dist/{api-XHQTOfIi.js.map → api-DlRJAFI2.js.map} +1 -1
- package/dist/bin.js +101 -42
- package/dist/bin.js.map +1 -1
- package/dist/{bounded-fs-B-qlKB0h.js → bounded-fs-CYxETRCt.js} +3 -3
- package/dist/{bounded-fs-B-qlKB0h.js.map → bounded-fs-CYxETRCt.js.map} +1 -1
- package/dist/{ci-install-C9BAoO2n.js → ci-install-Cyqj_i3I.js} +4 -4
- package/dist/{ci-install-C9BAoO2n.js.map → ci-install-Cyqj_i3I.js.map} +1 -1
- package/dist/{debug-npp6DzBK.js → debug-2W78IhFE.js} +1 -1
- package/dist/{debug-8-IIHr5f.js → debug-BYIoy1Oz.js} +19 -9
- package/dist/debug-BYIoy1Oz.js.map +1 -0
- package/dist/{env-api-key-cjD4yfGT.js → env-api-key-B7kiHT3h.js} +4 -7
- package/dist/env-api-key-B7kiHT3h.js.map +1 -0
- package/dist/{environment-BffaZJRI.js → environment-Br-oy8cq.js} +4 -4
- package/dist/{environment-BffaZJRI.js.map → environment-Br-oy8cq.js.map} +1 -1
- package/dist/{file-watcher-BreVxzh0.js → file-watcher-HaK4IrJ5.js} +2 -2
- package/dist/{file-watcher-BreVxzh0.js.map → file-watcher-HaK4IrJ5.js.map} +1 -1
- package/dist/{headless-ui-Dy_l8hCT.js → headless-ui-Dd7Ty4Ul.js} +2 -2
- package/dist/{headless-ui-Dy_l8hCT.js.map → headless-ui-Dd7Ty4Ul.js.map} +1 -1
- package/dist/{interactive-C9vepji1.js → interactive-CYv97Lgg.js} +2 -2
- package/dist/{interactive-C9vepji1.js.map → interactive-CYv97Lgg.js.map} +1 -1
- package/dist/{mcp-C5Zheplw.js → mcp-D6cJWFPn.js} +2 -2
- package/dist/{mcp-C5Zheplw.js.map → mcp-D6cJWFPn.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-DEerU5M3.js → mcp-prompt-streaming-DYX7vQ3q.js} +3 -3
- package/dist/{mcp-prompt-streaming-DEerU5M3.js.map → mcp-prompt-streaming-DYX7vQ3q.js.map} +1 -1
- package/dist/{non-interactive-C9vQaJF2.js → non-interactive-BR4kmPE5.js} +2 -2
- package/dist/{non-interactive-C9vQaJF2.js.map → non-interactive-BR4kmPE5.js.map} +1 -1
- package/dist/{orchestrator-tools-Bw97ahf6.js → orchestrator-tools-CTP6l34W.js} +13 -10
- package/dist/orchestrator-tools-CTP6l34W.js.map +1 -0
- package/dist/{package-manager-Cm5vbUDR.js → package-manager-FyJmIsqr.js} +2 -2
- package/dist/{package-manager-Cm5vbUDR.js.map → package-manager-FyJmIsqr.js.map} +1 -1
- package/dist/{pi-BocAqBQZ.js → pi-DeF8mWlb.js} +11 -11
- package/dist/{pi-BocAqBQZ.js.map → pi-DeF8mWlb.js.map} +1 -1
- package/dist/{playground-DmGHAiON.js → playground-BSMgOp3k.js} +6 -6
- package/dist/{playground-DmGHAiON.js.map → playground-BSMgOp3k.js.map} +1 -1
- package/dist/{posthog-BitL59Wc.js → posthog-ZWC-083a.js} +2 -2
- package/dist/{posthog-BitL59Wc.js.map → posthog-ZWC-083a.js.map} +1 -1
- package/dist/{posthog-integration-DbZ2oza2.js → posthog-integration-CoAID6XW.js} +53 -23
- package/dist/{posthog-integration-DbZ2oza2.js.map → posthog-integration-CoAID6XW.js.map} +1 -1
- package/dist/{provisioning-CzRepu4H.js → provisioning-xirriYQJ.js} +3 -3
- package/dist/{provisioning-CzRepu4H.js.map → provisioning-xirriYQJ.js.map} +1 -1
- package/dist/{queue-tools-OynU-m2F.js → queue-tools-BtJ7_fEW.js} +119 -20
- package/dist/queue-tools-BtJ7_fEW.js.map +1 -0
- package/dist/{registry-eXzDlt51.js → registry-B-whj8SQ.js} +5 -5
- package/dist/{registry-eXzDlt51.js.map → registry-B-whj8SQ.js.map} +1 -1
- package/dist/{security-DqByFEpl.js → security-CPtn_dUW.js} +5 -5
- package/dist/{security-DqByFEpl.js.map → security-CPtn_dUW.js.map} +1 -1
- package/dist/{setup-utils-C2rGcUFv.js → setup-utils-e78zkZoW.js} +71 -18
- package/dist/setup-utils-e78zkZoW.js.map +1 -0
- package/dist/{start-tui-BYTQwgvK.js → start-tui-B08PfE0V.js} +21 -18
- package/dist/start-tui-B08PfE0V.js.map +1 -0
- package/dist/{steps-CMIyEySI.js → steps-Dpd8UTJX.js} +7 -7
- package/dist/{steps-CMIyEySI.js.map → steps-Dpd8UTJX.js.map} +1 -1
- package/dist/{store-pBYMaEuj.js → store-B-ctPH7O.js} +5 -4
- package/dist/store-B-ctPH7O.js.map +1 -0
- package/dist/{subagent-ZDD6XCmm.js → subagent-B18BesIW.js} +2 -2
- package/dist/{subagent-ZDD6XCmm.js.map → subagent-B18BesIW.js.map} +1 -1
- package/dist/{task-1KHCdViK.js → task-D1RaGg3b.js} +12 -12
- package/dist/{task-1KHCdViK.js.map → task-D1RaGg3b.js.map} +1 -1
- package/dist/{task-stream-DAU6Z6u6.js → task-stream-DvTf1NOr.js} +4 -4
- package/dist/{task-stream-DAU6Z6u6.js.map → task-stream-DvTf1NOr.js.map} +1 -1
- package/dist/{tasks-BBo0_AZG.js → tasks-CvHNNFB1.js} +2 -2
- package/dist/{tasks-BBo0_AZG.js.map → tasks-CvHNNFB1.js.map} +1 -1
- package/dist/{telemetry-DSzPCPtQ.js → telemetry-C72C8V6q.js} +3 -3
- package/dist/{telemetry-DSzPCPtQ.js.map → telemetry-C72C8V6q.js.map} +1 -1
- package/dist/{terminal-DW8x0UNT.js → terminal-BJms6G77.js} +16 -13
- package/dist/terminal-BJms6G77.js.map +1 -0
- package/dist/{tools-DCH7Ji4V.js → tools-BcXOgEvh.js} +6 -6
- package/dist/{tools-DCH7Ji4V.js.map → tools-BcXOgEvh.js.map} +1 -1
- package/dist/{wizard-abort-B_Sa9K3g.js → wizard-abort-BUpx4qnK.js} +1 -1
- package/dist/{wizard-abort-Cz916Qq5.js → wizard-abort-DRscjUOg.js} +3 -3
- package/dist/{wizard-abort-Cz916Qq5.js.map → wizard-abort-DRscjUOg.js.map} +1 -1
- package/dist/wizard-session-BUhlJ1b8.js.map +1 -1
- package/dist/{yara-hooks-DgScuNrT.js → yara-hooks-DKRsn0lN.js} +4 -4
- package/dist/{yara-hooks-DgScuNrT.js.map → yara-hooks-DKRsn0lN.js.map} +1 -1
- package/dist/yara-hooks-fHntXaFu.js +2 -0
- package/package.json +1 -1
- package/dist/agent-interface-Bm4ZNGGV.js.map +0 -1
- package/dist/agent-prompt-loader-CsHNQXsY.js.map +0 -1
- package/dist/agent-runner-DLAvB67P.js.map +0 -1
- package/dist/analytics-kxgLEv7U.js +0 -2
- package/dist/debug-8-IIHr5f.js.map +0 -1
- package/dist/env-api-key-cjD4yfGT.js.map +0 -1
- package/dist/orchestrator-tools-Bw97ahf6.js.map +0 -1
- package/dist/queue-tools-OynU-m2F.js.map +0 -1
- package/dist/setup-utils-C2rGcUFv.js.map +0 -1
- package/dist/start-tui-BYTQwgvK.js.map +0 -1
- package/dist/store-pBYMaEuj.js.map +0 -1
- package/dist/terminal-DW8x0UNT.js.map +0 -1
- package/dist/yara-hooks-BVEBOdMu.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics-CkIyBHfd.js","names":["uuidv4"],"sources":["../src/utils/ci-flag-overrides.ts","../src/utils/analytics.ts"],"sourcesContent":["/**\n * CI-only feature-flag overrides.\n *\n * CI must route deterministically: a run that tests the orchestrator arm says\n * so explicitly instead of depending on a live feature flag someone can edit\n * mid-week. The override env var (see the allowlist in `env.ts`) is a JSON\n * object of flag key → value, merged over whatever PostHog returned.\n *\n * The override path exists only in CI builds (`pnpm build:ci`). Published\n * builds inline NODE_ENV as the literal \"production\", the guards collapse,\n * and tsdown strips the rest from the bundle — and the smoke test asserts the\n * env var names are physically absent from production output (which is also\n * why no comment in this file may spell them out), so this can never quietly\n * become a production surface.\n */\nimport { runtimeEnv } from '@env';\nimport { logToFile } from './debug';\n\nexport function applyCiFlagOverrides(\n flags: Record<string, string>,\n payloads: Record<string, unknown> = {},\n): { flags: Record<string, string>; payloads: Record<string, unknown> } {\n // Compared inline (not via env.ts's IS_PRODUCTION_BUILD) so tsdown replaces\n // it with a literal right here and the bundler can prove the rest of this\n // function unreachable in production builds. The smoke test enforces that.\n if (process.env.NODE_ENV === 'production') return { flags, payloads };\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return { flags, payloads };\n\n let overrides: Record<string, unknown>;\n try {\n overrides = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // A malformed override is a CI misconfiguration. Fail the run loudly\n // rather than silently testing whatever the live flags happen to say.\n throw new Error(\n 'The CI flag-override env var is not valid JSON (expected {\"flag-key\": value, ...}).',\n );\n }\n\n const mergedFlags = { ...flags };\n const mergedPayloads = { ...payloads };\n for (const [key, value] of Object.entries(overrides)) {\n // An object/array override is a flag payload: the flag reads 'true' and\n // the value rides the payload channel, matching a payload-carrying flag.\n if (typeof value === 'object' && value !== null) {\n mergedFlags[key] = 'true';\n mergedPayloads[key] = value;\n } else {\n mergedFlags[key] = String(value);\n }\n }\n logToFile('[flags] CI overrides applied', overrides);\n return { flags: mergedFlags, payloads: mergedPayloads };\n}\n\n/**\n * Task types excluded from this run. The exclusion env var (see the allowlist\n * in `env.ts`) is a comma-separated list (e.g. `dashboard`), set by the CI\n * harness that owns the policy — the wizard and the served content stay\n * run-mode agnostic. CI-build only, same as the flag overrides: published\n * builds strip this path.\n */\nexport function ciExcludedTaskTypes(): readonly string[] {\n if (process.env.NODE_ENV === 'production') return [];\n\n const raw = runtimeEnv('WIZARD_CI_EXCLUDE_TASKS');\n if (!raw) return [];\n const types = raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n if (types.length > 0) logToFile('[flags] CI task exclusions', types);\n return types;\n}\n","import { PostHog } from 'posthog-node';\nimport {\n ANALYTICS_HOST_URL,\n ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,\n ANALYTICS_TEAM_TAG,\n WIZARD_FLAG_KEYS,\n} from '@lib/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { ApiUser } from '@lib/api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IS_PRODUCTION_BUILD, RUN_SURFACE, TASK_ID, TASK_RUN_ID } from '@env';\nimport { VERSION } from '@lib/version';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\n\n/**\n * The invocation, reduced to flag-safe strings: the command word (first\n * positional, 'default' for the bare flow) and the sorted option NAMES.\n * Option values never leave the machine — they carry paths, emails, keys.\n */\nfunction invocationProperties(): { command: string; cli_flags: string } {\n const args = process.argv.slice(2);\n const command = args.find((a) => !a.startsWith('-')) ?? 'default';\n const flags = [\n ...new Set(\n args\n .filter((a) => a.startsWith('--'))\n .map((a) => a.slice(2).split('=')[0]),\n ),\n ].sort();\n return { command, cli_flags: flags.join(',') };\n}\n\n/**\n * Extract a standard property bag from the current session.\n * Used by store-level analytics and available for ad-hoc captures.\n */\nexport function sessionProperties(\n session: WizardSession,\n): Record<string, unknown> {\n return {\n integration: session.integration,\n detected_framework: session.detectedFrameworkLabel,\n typescript: session.typescript,\n project_id: session.credentials?.projectId,\n discovered_features: session.discoveredFeatures,\n additional_features: session.additionalFeatureQueue,\n run_phase: session.runPhase,\n };\n}\n\nexport function groupsFromUser(\n user: ApiUser | null,\n host: string,\n): Record<string, string> {\n const groups: Record<string, string> = { instance: host };\n if (!user) return groups;\n\n const organizationId = user.organization?.id;\n if (organizationId) groups.organization = organizationId;\n\n const customerId = user.organization?.customer_id;\n if (customerId) groups.customer = customerId;\n\n const projectUuid = user.team?.uuid;\n if (projectUuid) groups.project = projectUuid;\n\n return groups;\n}\n\nconst WIZARD_FLAGS: ReadonlySet<string> = new Set(WIZARD_FLAG_KEYS);\n\n// Widen back to the SDK's shape — a filter on `true` never matches `'true'`.\nfunction flagResponseValue(value: string): string | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return value;\n}\n\nexport class Analytics {\n private client: PostHog;\n private tags: Record<string, string | boolean | number | null | undefined> =\n {};\n private distinctId?: string;\n private anonymousId: string;\n private _runId: string;\n private sessionId: string | null = null;\n private appName = 'wizard';\n private activeFlags: Record<string, string> | null = null;\n private activeFlagPayloads: Record<string, unknown> = {};\n private groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\n private terminalEventSent = false;\n\n constructor() {\n this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {\n host: ANALYTICS_HOST_URL,\n flushAt: 1,\n flushInterval: 0,\n enableExceptionAutocapture: true,\n before_send: (event) => {\n if (!event) return event;\n if (Object.keys(this.groups).length > 0) {\n event.groups = { ...this.groups, ...event.groups };\n }\n // Autocaptured exceptions arrive with a random uuid and\n // `$process_person_profile: false` — reattach the run's identity\n // and tags so they land on the same person as everything else.\n if (event.event === '$exception') {\n event.distinctId = this.distinctId ?? this.anonymousId;\n const { $process_person_profile, ...properties } =\n event.properties ?? {};\n void $process_person_profile;\n event.properties = { ...this.tags, ...properties };\n }\n // The SDK captures this one itself, bypassing capture(), so tags merge here.\n if (event.event === '$feature_flag_called') {\n event.properties = { ...this.tags, ...(event.properties ?? {}) };\n }\n return event;\n },\n });\n\n this.tags = { $app_name: this.appName };\n // Tag every run with its build type so prod / dev / ci / headless segment\n // cleanly in analytics. tsdown inlines IS_PRODUCTION_BUILD to `true` in\n // published builds and `false` for dev/tsx/test runs. Non-interactive runs\n // upgrade this in runWizardCI: dev `--ci` runs to 'ci', published headless\n // runs to 'headless'.\n this.tags.build = IS_PRODUCTION_BUILD ? 'prod' : 'dev';\n\n this.tags.run_surface = RUN_SURFACE;\n\n // The build this run came from. Already sent as a flag-evaluation person\n // property; tagging it makes every event attributable to a release, so a\n // regression can be bounded to the versions that carry it.\n this.tags.version = VERSION;\n\n // Cloud runs only: the task run that owns the sandbox. Without these the\n // wizard's events and the run that launched it are two unrelated streams,\n // since run_id above is minted per process and never leaves the wizard.\n // Left unset for local runs so the properties stay absent rather than null.\n if (TASK_RUN_ID) this.tags.task_run_id = TASK_RUN_ID;\n if (TASK_ID) this.tags.task_id = TASK_ID;\n\n this.anonymousId = uuidv4();\n\n // One id per process = one id per wizard run, registered in the tag bag\n // so it rides on every capture, exception, and autocaptured exception\n // (all of which merge `this.tags`). Lets you separate two runs by the\n // same logged-in user, who otherwise share one distinct id. Distinct\n // from `anonymousId`, the pre-login *person* id that gets aliased onto\n // the real user at login. `$session_id` is intentionally not set here —\n // it stays null until OAuth completes (see identifyUser).\n this._runId = uuidv4();\n this.tags.run_id = this._runId;\n\n this.distinctId = undefined;\n }\n\n /** Per-process run id, tagged on every event and gateway trace. */\n get runId(): string {\n return this._runId;\n }\n\n /** Build type for this run ('prod' | 'dev' | 'ci' | 'headless') — the same value tagged on every analytics event. */\n get build(): string {\n return String(this.tags.build ?? 'dev');\n }\n\n /**\n * Associate the run with the logged-in user, once per id. Identifies them\n * (email, name) and records those person properties so events carry them and\n * feature flags can target the individual user — without the email here the\n * wizard only sends `$app_name`, so email-targeted flags never match. Opens\n * the analytics session on first login, then aliases the run's anonymous id\n * onto the identified person so pre-login events merge in.\n */\n identifyUser(user: ApiUser) {\n const distinctId = user.distinct_id;\n if (this.distinctId === distinctId || distinctId === this.anonymousId) {\n return;\n }\n this.distinctId = distinctId;\n // Open the analytics session on first login. Null until here, so\n // pre-OAuth events carry only `run_id`; from now on every event also\n // carries `$session_id` and PostHog groups the authenticated run into a\n // native Session. Stored in the tag bag so it rides on every subsequent\n // capture and exception.\n if (!this.sessionId) {\n this.sessionId = uuidv4();\n this.tags.$session_id = this.sessionId;\n }\n const props: Record<string, string> = {};\n if (user.email) props.email = user.email;\n const name = [user.first_name, user.last_name]\n .filter(Boolean)\n .join(' ')\n .trim();\n if (name) props.name = name;\n this.personProperties = props;\n this.client.identify({ distinctId, properties: { $set: props } });\n this.client.alias({\n distinctId,\n alias: this.anonymousId,\n });\n // The flag snapshot is per identity. Anything evaluated before login (the\n // intro screen reads the tools-menu flag) was anonymous — drop it so the\n // next read re-evaluates as this user.\n this.activeFlags = null;\n this.activeFlagPayloads = {};\n }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n // Environment facts flag conditions can scope on — evaluation-time only,\n // never persisted onto the person. `version` gates an experiment to\n // releases that implement it (a condition on a property older builds\n // don't send silently never matches them); `run_surface`/`build` scope\n // by invocation surface; `command`/`cli_flags` by subcommand; `os`/\n // `node_major` by platform.\n return {\n $app_name: this.appName,\n version: VERSION,\n run_surface: RUN_SURFACE,\n build: String(this.tags.build ?? 'dev'),\n os: process.platform,\n node_major: process.versions.node.split('.')[0],\n ...invocationProperties(),\n ...this.personProperties,\n };\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n setGroups(groups: Record<string, string>) {\n this.groups = groups;\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n this.client.captureException(error, this.distinctId ?? this.anonymousId, {\n team: ANALYTICS_TEAM_TAG,\n ...this.tags,\n ...properties,\n });\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: eventName,\n properties: {\n ...this.tags,\n ...this.wizardFlagFeatureProperties(),\n ...properties,\n },\n });\n }\n\n // Built from the resolved map, not the SDK snapshot: a CI-forced variant must own its events.\n private wizardFlagFeatureProperties(): Record<string, unknown> {\n if (this.activeFlags === null) return {};\n const props: Record<string, unknown> = {};\n const active: string[] = [];\n for (const [key, value] of Object.entries(this.activeFlags)) {\n if (!WIZARD_FLAGS.has(key)) continue;\n const resolved = flagResponseValue(value);\n props[`$feature/${key}`] = resolved;\n if (resolved !== false) active.push(key);\n }\n if (active.length > 0) props.$active_feature_flags = active.sort();\n return props;\n }\n\n /**\n * Capture a wizard-specific event. Automatically prepends \"wizard: \" to the event name.\n * All new wizard analytics should use this method instead of capture() directly.\n */\n wizardCapture(eventName: string, properties?: Record<string, unknown>): void {\n this.capture(`wizard: ${eventName}`, properties);\n }\n\n /**\n * Flush pending events without firing the \"setup wizard finished\" terminal\n * event. Use this from CLI error paths that exit before any wizard run\n * starts — `shutdown()` would inflate the run count with a \"finished\" event\n * for a parse error that never actually ran the wizard.\n */\n async flush(): Promise<void> {\n await this.client.shutdown();\n }\n\n /**\n * Evaluate all feature flags for the current user at the start of a run.\n * Result is cached; subsequent calls in the same run return the same map.\n * Returns flag key -> string value (booleans become 'true'/'false').\n */\n // Only a getFlag read records an exposure; the bulk response records none.\n async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\n const payloads: Record<string, unknown> = {};\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n logToFile('[flags] evaluating as', {\n distinctId,\n identified: this.distinctId !== undefined,\n personProperties: this.flagPersonProperties(),\n });\n const evaluation = await this.client.evaluateFlags(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n for (const key of WIZARD_FLAG_KEYS) {\n const value = evaluation.getFlag(key);\n if (value === undefined) continue;\n out[key] = String(value);\n const payload = evaluation.getFlagPayload(key);\n if (payload !== undefined) payloads[key] = payload;\n }\n } catch (error) {\n debug('Failed to get all feature flags:', error);\n this.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'get_all_flags' },\n );\n }\n // Outside the fetch guard on purpose: a malformed CI override must fail\n // the run loudly, and a valid one applies even when the fetch failed —\n // CI routing stays deterministic either way.\n const merged = applyCiFlagOverrides(out, payloads);\n this.activeFlags = merged.flags;\n this.activeFlagPayloads = merged.payloads;\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n /** Flag payloads from the same snapshot; empty until the flags are fetched. */\n getWizardFlagPayloads(): Record<string, unknown> {\n return this.activeFlagPayloads;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\n\n // First status wins. The interrupt fallback in start-tui fires\n // shutdown('cancelled') on every TUI teardown; without this guard a run\n // that already reported 'success' would emit a second, contradicting\n // terminal event when the user dismisses the outro.\n if (this.terminalEventSent) {\n return;\n }\n this.terminalEventSent = true;\n\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: 'setup wizard finished',\n properties: {\n // Flat for filtering; the nested `tags` snapshot stays for back-compat.\n ...this.tags,\n run_id: this._runId,\n ...(this.sessionId ? { $session_id: this.sessionId } : {}),\n status,\n tags: this.tags,\n },\n });\n\n await this.client.shutdown();\n }\n}\n\nexport const analytics = new Analytics();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"analytics-YLZKudpE.js","names":["uuidv4"],"sources":["../src/utils/ci-flag-overrides.ts","../src/utils/analytics.ts"],"sourcesContent":["/**\n * CI-only feature-flag overrides.\n *\n * CI must route deterministically: a run that tests the orchestrator arm says\n * so explicitly instead of depending on a live feature flag someone can edit\n * mid-week. The override env var (see the allowlist in `env.ts`) is a JSON\n * object of flag key → value, merged over whatever PostHog returned.\n *\n * The override path exists only in CI builds (`pnpm build:ci`). Published\n * builds inline NODE_ENV as the literal \"production\", the guards collapse,\n * and tsdown strips the rest from the bundle — and the smoke test asserts the\n * env var names are physically absent from production output (which is also\n * why no comment in this file may spell them out), so this can never quietly\n * become a production surface.\n */\nimport { runtimeEnv } from '@env';\nimport { logToFile } from './debug';\n\nexport function applyCiFlagOverrides(\n flags: Record<string, string>,\n payloads: Record<string, unknown> = {},\n): { flags: Record<string, string>; payloads: Record<string, unknown> } {\n // Compared inline (not via env.ts's IS_PRODUCTION_BUILD) so tsdown replaces\n // it with a literal right here and the bundler can prove the rest of this\n // function unreachable in production builds. The smoke test enforces that.\n if (process.env.NODE_ENV === 'production') return { flags, payloads };\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return { flags, payloads };\n\n let overrides: Record<string, unknown>;\n try {\n overrides = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // A malformed override is a CI misconfiguration. Fail the run loudly\n // rather than silently testing whatever the live flags happen to say.\n throw new Error(\n 'The CI flag-override env var is not valid JSON (expected {\"flag-key\": value, ...}).',\n );\n }\n\n const mergedFlags = { ...flags };\n const mergedPayloads = { ...payloads };\n for (const [key, value] of Object.entries(overrides)) {\n // An object/array override is a flag payload: the flag reads 'true' and\n // the value rides the payload channel, matching a payload-carrying flag.\n if (typeof value === 'object' && value !== null) {\n mergedFlags[key] = 'true';\n mergedPayloads[key] = value;\n } else {\n mergedFlags[key] = String(value);\n }\n }\n logToFile('[flags] CI overrides applied', overrides);\n return { flags: mergedFlags, payloads: mergedPayloads };\n}\n\n/**\n * Task types excluded from this run. The exclusion env var (see the allowlist\n * in `env.ts`) is a comma-separated list (e.g. `dashboard`), set by the CI\n * harness that owns the policy — the wizard and the served content stay\n * run-mode agnostic. CI-build only, same as the flag overrides: published\n * builds strip this path.\n */\nexport function ciExcludedTaskTypes(): readonly string[] {\n if (process.env.NODE_ENV === 'production') return [];\n\n const raw = runtimeEnv('WIZARD_CI_EXCLUDE_TASKS');\n if (!raw) return [];\n const types = raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n if (types.length > 0) logToFile('[flags] CI task exclusions', types);\n return types;\n}\n","import { PostHog } from 'posthog-node';\nimport {\n ANALYTICS_HOST_URL,\n ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,\n ANALYTICS_TEAM_TAG,\n WIZARD_FLAG_KEYS,\n} from '@lib/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { ApiUser } from '@lib/api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IS_PRODUCTION_BUILD, RUN_SURFACE, TASK_ID, TASK_RUN_ID } from '@env';\nimport { VERSION } from '@lib/version';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\n\n/**\n * The invocation, reduced to flag-safe strings: the command word (first\n * positional, 'default' for the bare flow) and the sorted option NAMES.\n * Option values never leave the machine — they carry paths, emails, keys.\n */\nfunction invocationProperties(): { command: string; cli_flags: string } {\n const args = process.argv.slice(2);\n const command = args.find((a) => !a.startsWith('-')) ?? 'default';\n const flags = [\n ...new Set(\n args\n .filter((a) => a.startsWith('--'))\n .map((a) => a.slice(2).split('=')[0]),\n ),\n ].sort();\n return { command, cli_flags: flags.join(',') };\n}\n\n/**\n * Extract a standard property bag from the current session.\n * Used by store-level analytics and available for ad-hoc captures.\n */\nexport function sessionProperties(\n session: WizardSession,\n): Record<string, unknown> {\n return {\n integration: session.integration,\n detected_framework: session.detectedFrameworkLabel,\n typescript: session.typescript,\n project_id: session.credentials?.projectId,\n discovered_features: session.discoveredFeatures,\n additional_features: session.additionalFeatureQueue,\n run_phase: session.runPhase,\n };\n}\n\nexport function groupsFromUser(\n user: ApiUser | null,\n host: string,\n): Record<string, string> {\n const groups: Record<string, string> = { instance: host };\n if (!user) return groups;\n\n const organizationId = user.organization?.id;\n if (organizationId) groups.organization = organizationId;\n\n const customerId = user.organization?.customer_id;\n if (customerId) groups.customer = customerId;\n\n const projectUuid = user.team?.uuid;\n if (projectUuid) groups.project = projectUuid;\n\n return groups;\n}\n\nconst WIZARD_FLAGS: ReadonlySet<string> = new Set(WIZARD_FLAG_KEYS);\n\n// Widen back to the SDK's shape — a filter on `true` never matches `'true'`.\nfunction flagResponseValue(value: string): string | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return value;\n}\n\nexport class Analytics {\n private client: PostHog;\n private tags: Record<string, string | boolean | number | null | undefined> =\n {};\n private distinctId?: string;\n private anonymousId: string;\n private _runId: string;\n private sessionId: string | null = null;\n private appName = 'wizard';\n private activeFlags: Record<string, string> | null = null;\n private activeFlagPayloads: Record<string, unknown> = {};\n private groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\n private terminalEventSent = false;\n\n constructor() {\n this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {\n host: ANALYTICS_HOST_URL,\n flushAt: 1,\n flushInterval: 0,\n enableExceptionAutocapture: true,\n before_send: (event) => {\n if (!event) return event;\n if (Object.keys(this.groups).length > 0) {\n event.groups = { ...this.groups, ...event.groups };\n }\n // Autocaptured exceptions arrive with a random uuid and\n // `$process_person_profile: false` — reattach the run's identity\n // and tags so they land on the same person as everything else.\n if (event.event === '$exception') {\n event.distinctId = this.distinctId ?? this.anonymousId;\n const { $process_person_profile, ...properties } =\n event.properties ?? {};\n void $process_person_profile;\n event.properties = { ...this.tags, ...properties };\n }\n // The SDK captures this one itself, bypassing capture(), so tags merge here.\n if (event.event === '$feature_flag_called') {\n event.properties = { ...this.tags, ...(event.properties ?? {}) };\n }\n return event;\n },\n });\n\n this.tags = { $app_name: this.appName };\n // Tag every run with its build type so prod / dev / ci / headless segment\n // cleanly in analytics. tsdown inlines IS_PRODUCTION_BUILD to `true` in\n // published builds and `false` for dev/tsx/test runs. Non-interactive runs\n // upgrade this in runWizardCI: dev `--ci` runs to 'ci', published headless\n // runs to 'headless'.\n this.tags.build = IS_PRODUCTION_BUILD ? 'prod' : 'dev';\n\n this.tags.run_surface = RUN_SURFACE;\n\n // The build this run came from. Already sent as a flag-evaluation person\n // property; tagging it makes every event attributable to a release, so a\n // regression can be bounded to the versions that carry it.\n this.tags.version = VERSION;\n\n // Cloud runs only: the task run that owns the sandbox. Without these the\n // wizard's events and the run that launched it are two unrelated streams,\n // since run_id above is minted per process and never leaves the wizard.\n // Left unset for local runs so the properties stay absent rather than null.\n if (TASK_RUN_ID) this.tags.task_run_id = TASK_RUN_ID;\n if (TASK_ID) this.tags.task_id = TASK_ID;\n\n this.anonymousId = uuidv4();\n\n // One id per process = one id per wizard run, registered in the tag bag\n // so it rides on every capture, exception, and autocaptured exception\n // (all of which merge `this.tags`). Lets you separate two runs by the\n // same logged-in user, who otherwise share one distinct id. Distinct\n // from `anonymousId`, the pre-login *person* id that gets aliased onto\n // the real user at login. `$session_id` is intentionally not set here —\n // it stays null until OAuth completes (see identifyUser).\n this._runId = uuidv4();\n this.tags.run_id = this._runId;\n\n this.distinctId = undefined;\n }\n\n /** Per-process run id, tagged on every event and gateway trace. */\n get runId(): string {\n return this._runId;\n }\n\n /** Build type for this run ('prod' | 'dev' | 'ci' | 'headless') — the same value tagged on every analytics event. */\n get build(): string {\n return String(this.tags.build ?? 'dev');\n }\n\n /**\n * Associate the run with the logged-in user, once per id. Identifies them\n * (email, name) and records those person properties so events carry them and\n * feature flags can target the individual user — without the email here the\n * wizard only sends `$app_name`, so email-targeted flags never match. Opens\n * the analytics session on first login, then aliases the run's anonymous id\n * onto the identified person so pre-login events merge in.\n */\n identifyUser(user: ApiUser) {\n const distinctId = user.distinct_id;\n if (this.distinctId === distinctId || distinctId === this.anonymousId) {\n return;\n }\n this.distinctId = distinctId;\n // Open the analytics session on first login. Null until here, so\n // pre-OAuth events carry only `run_id`; from now on every event also\n // carries `$session_id` and PostHog groups the authenticated run into a\n // native Session. Stored in the tag bag so it rides on every subsequent\n // capture and exception.\n if (!this.sessionId) {\n this.sessionId = uuidv4();\n this.tags.$session_id = this.sessionId;\n }\n const props: Record<string, string> = {};\n if (user.email) props.email = user.email;\n const name = [user.first_name, user.last_name]\n .filter(Boolean)\n .join(' ')\n .trim();\n if (name) props.name = name;\n this.personProperties = props;\n this.client.identify({ distinctId, properties: { $set: props } });\n this.client.alias({\n distinctId,\n alias: this.anonymousId,\n });\n // The flag snapshot is per identity. Anything evaluated before login (the\n // intro screen reads the tools-menu flag) was anonymous — drop it so the\n // next read re-evaluates as this user.\n this.activeFlags = null;\n this.activeFlagPayloads = {};\n }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n // Environment facts flag conditions can scope on — evaluation-time only,\n // never persisted onto the person. `version` gates an experiment to\n // releases that implement it (a condition on a property older builds\n // don't send silently never matches them); `run_surface`/`build` scope\n // by invocation surface; `command`/`cli_flags` by subcommand; `os`/\n // `node_major` by platform.\n return {\n $app_name: this.appName,\n version: VERSION,\n run_surface: RUN_SURFACE,\n build: String(this.tags.build ?? 'dev'),\n os: process.platform,\n node_major: process.versions.node.split('.')[0],\n ...invocationProperties(),\n ...this.personProperties,\n };\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n setGroups(groups: Record<string, string>) {\n this.groups = groups;\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n this.client.captureException(error, this.distinctId ?? this.anonymousId, {\n team: ANALYTICS_TEAM_TAG,\n ...this.tags,\n ...properties,\n });\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: eventName,\n properties: {\n ...this.tags,\n ...this.wizardFlagFeatureProperties(),\n ...properties,\n },\n });\n }\n\n // Built from the resolved map, not the SDK snapshot: a CI-forced variant must own its events.\n private wizardFlagFeatureProperties(): Record<string, unknown> {\n if (this.activeFlags === null) return {};\n const props: Record<string, unknown> = {};\n const active: string[] = [];\n for (const [key, value] of Object.entries(this.activeFlags)) {\n if (!WIZARD_FLAGS.has(key)) continue;\n const resolved = flagResponseValue(value);\n props[`$feature/${key}`] = resolved;\n if (resolved !== false) active.push(key);\n }\n if (active.length > 0) props.$active_feature_flags = active.sort();\n return props;\n }\n\n /**\n * Capture a wizard-specific event. Automatically prepends \"wizard: \" to the event name.\n * All new wizard analytics should use this method instead of capture() directly.\n */\n wizardCapture(eventName: string, properties?: Record<string, unknown>): void {\n this.capture(`wizard: ${eventName}`, properties);\n }\n\n /**\n * Flush pending events without firing the \"setup wizard finished\" terminal\n * event. Use this from CLI error paths that exit before any wizard run\n * starts — `shutdown()` would inflate the run count with a \"finished\" event\n * for a parse error that never actually ran the wizard.\n */\n async flush(): Promise<void> {\n await this.client.shutdown();\n }\n\n /**\n * Evaluate all feature flags for the current user at the start of a run.\n * Result is cached; subsequent calls in the same run return the same map.\n * Returns flag key -> string value (booleans become 'true'/'false').\n */\n // Only a getFlag read records an exposure; the bulk response records none.\n async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\n const payloads: Record<string, unknown> = {};\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n logToFile('[flags] evaluating as', {\n distinctId,\n identified: this.distinctId !== undefined,\n personProperties: this.flagPersonProperties(),\n });\n const evaluation = await this.client.evaluateFlags(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n for (const key of WIZARD_FLAG_KEYS) {\n const value = evaluation.getFlag(key);\n if (value === undefined) continue;\n out[key] = String(value);\n const payload = evaluation.getFlagPayload(key);\n if (payload !== undefined) payloads[key] = payload;\n }\n } catch (error) {\n debug('Failed to get all feature flags:', error);\n this.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'get_all_flags' },\n );\n }\n // Outside the fetch guard on purpose: a malformed CI override must fail\n // the run loudly, and a valid one applies even when the fetch failed —\n // CI routing stays deterministic either way.\n const merged = applyCiFlagOverrides(out, payloads);\n this.activeFlags = merged.flags;\n this.activeFlagPayloads = merged.payloads;\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n /** Flag payloads from the same snapshot; empty until the flags are fetched. */\n getWizardFlagPayloads(): Record<string, unknown> {\n return this.activeFlagPayloads;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\n\n // First status wins. The interrupt fallback in start-tui fires\n // shutdown('cancelled') on every TUI teardown; without this guard a run\n // that already reported 'success' would emit a second, contradicting\n // terminal event when the user dismisses the outro.\n if (this.terminalEventSent) {\n return;\n }\n this.terminalEventSent = true;\n\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: 'setup wizard finished',\n properties: {\n // Flat for filtering; the nested `tags` snapshot stays for back-compat.\n ...this.tags,\n run_id: this._runId,\n ...(this.sessionId ? { $session_id: this.sessionId } : {}),\n status,\n tags: this.tags,\n },\n });\n\n await this.client.shutdown();\n }\n}\n\nexport const analytics = new Analytics();\n"],"mappings":";;;;AAkBA,SAAgB,qBACd,OACA,WAAoC,EAAE,EACgC;AAI3B,QAAO;EAAE;EAAO;EAAU;;;;;;;;;AAuCvE,SAAgB,sBAAyC;AACZ,QAAO,EAAE;;;;;;;;;AC7CtD,SAAS,uBAA+D;CACtE,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AASlC,QAAO;EAAE,SARO,KAAK,MAAM,MAAM,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI;EAQtC,WAPJ,CACZ,GAAG,IAAI,IACL,KACG,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC,CACjC,KAAK,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,CACxC,CACF,CAAC,MAAM,CAC2B,KAAK,IAAI;EAAE;;;;;;AAOhD,SAAgB,kBACd,SACyB;AACzB,QAAO;EACL,aAAa,QAAQ;EACrB,oBAAoB,QAAQ;EAC5B,YAAY,QAAQ;EACpB,YAAY,QAAQ,aAAa;EACjC,qBAAqB,QAAQ;EAC7B,qBAAqB,QAAQ;EAC7B,WAAW,QAAQ;EACpB;;AAGH,SAAgB,eACd,MACA,MACwB;CACxB,MAAM,SAAiC,EAAE,UAAU,MAAM;AACzD,KAAI,CAAC,KAAM,QAAO;CAElB,MAAM,iBAAiB,KAAK,cAAc;AAC1C,KAAI,eAAgB,QAAO,eAAe;CAE1C,MAAM,aAAa,KAAK,cAAc;AACtC,KAAI,WAAY,QAAO,WAAW;CAElC,MAAM,cAAc,KAAK,MAAM;AAC/B,KAAI,YAAa,QAAO,UAAU;AAElC,QAAO;;AAGT,MAAM,eAAoC,IAAI,IAAI,iBAAiB;AAGnE,SAAS,kBAAkB,OAAiC;AAC1D,KAAI,UAAU,OAAQ,QAAO;AAC7B,KAAI,UAAU,QAAS,QAAO;AAC9B,QAAO;;AAGT,IAAa,YAAb,MAAuB;CACrB;CACA,OACE,EAAE;CACJ;CACA;CACA;CACA,YAAmC;CACnC,UAAkB;CAClB,cAAqD;CACrD,qBAAsD,EAAE;CACxD,SAAyC,EAAE;CAC3C,mBAAmD,EAAE;CACrD,oBAA4B;CAE5B,cAAc;AACZ,OAAK,SAAS,IAAI,QAAQ,4CAA4C;GACpE,MAAM;GACN,SAAS;GACT,eAAe;GACf,4BAA4B;GAC5B,cAAc,UAAU;AACtB,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,OAAO,KAAK,KAAK,OAAO,CAAC,SAAS,EACpC,OAAM,SAAS;KAAE,GAAG,KAAK;KAAQ,GAAG,MAAM;KAAQ;AAKpD,QAAI,MAAM,UAAU,cAAc;AAChC,WAAM,aAAa,KAAK,cAAc,KAAK;KAC3C,MAAM,EAAE,yBAAyB,GAAG,eAClC,MAAM,cAAc,EAAE;AAExB,WAAM,aAAa;MAAE,GAAG,KAAK;MAAM,GAAG;MAAY;;AAGpD,QAAI,MAAM,UAAU,uBAClB,OAAM,aAAa;KAAE,GAAG,KAAK;KAAM,GAAI,MAAM,cAAc,EAAE;KAAG;AAElE,WAAO;;GAEV,CAAC;AAEF,OAAK,OAAO,EAAE,WAAW,KAAK,SAAS;AAMvC,OAAK,KAAK,QAA8B;AAExC,OAAK,KAAK,cAAc;AAKxB,OAAK,KAAK,UAAU;AAMpB,MAAI,YAAa,MAAK,KAAK,cAAc;AACzC,MAAI,QAAS,MAAK,KAAK,UAAU;AAEjC,OAAK,cAAcA,IAAQ;AAS3B,OAAK,SAASA,IAAQ;AACtB,OAAK,KAAK,SAAS,KAAK;AAExB,OAAK,aAAa,KAAA;;;CAIpB,IAAI,QAAgB;AAClB,SAAO,KAAK;;;CAId,IAAI,QAAgB;AAClB,SAAO,OAAO,KAAK,KAAK,SAAS,MAAM;;;;;;;;;;CAWzC,aAAa,MAAe;EAC1B,MAAM,aAAa,KAAK;AACxB,MAAI,KAAK,eAAe,cAAc,eAAe,KAAK,YACxD;AAEF,OAAK,aAAa;AAMlB,MAAI,CAAC,KAAK,WAAW;AACnB,QAAK,YAAYA,IAAQ;AACzB,QAAK,KAAK,cAAc,KAAK;;EAE/B,MAAM,QAAgC,EAAE;AACxC,MAAI,KAAK,MAAO,OAAM,QAAQ,KAAK;EACnC,MAAM,OAAO,CAAC,KAAK,YAAY,KAAK,UAAU,CAC3C,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,MAAM;AACT,MAAI,KAAM,OAAM,OAAO;AACvB,OAAK,mBAAmB;AACxB,OAAK,OAAO,SAAS;GAAE;GAAY,YAAY,EAAE,MAAM,OAAO;GAAE,CAAC;AACjE,OAAK,OAAO,MAAM;GAChB;GACA,OAAO,KAAK;GACb,CAAC;AAIF,OAAK,cAAc;AACnB,OAAK,qBAAqB,EAAE;;;CAI9B,uBAAuD;AAOrD,SAAO;GACL,WAAW,KAAK;GAChB,SAAS;GACT,aAAa;GACb,OAAO,OAAO,KAAK,KAAK,SAAS,MAAM;GACvC,IAAI,QAAQ;GACZ,YAAY,QAAQ,SAAS,KAAK,MAAM,IAAI,CAAC;GAC7C,GAAG,sBAAsB;GACzB,GAAG,KAAK;GACT;;CAGH,OAAO,KAAa,OAAqD;AACvE,OAAK,KAAK,OAAO;;CAGnB,UAAU,QAAgC;AACxC,OAAK,SAAS;;CAGhB,iBAAiB,OAAc,aAAsC,EAAE,EAAE;AACvE,OAAK,OAAO,iBAAiB,OAAO,KAAK,cAAc,KAAK,aAAa;GACvE,MAAM;GACN,GAAG,KAAK;GACR,GAAG;GACJ,CAAC;;CAGJ,QAAQ,WAAmB,YAAsC;AAC/D,OAAK,OAAO,QAAQ;GAClB,YAAY,KAAK,cAAc,KAAK;GACpC,OAAO;GACP,YAAY;IACV,GAAG,KAAK;IACR,GAAG,KAAK,6BAA6B;IACrC,GAAG;IACJ;GACF,CAAC;;CAIJ,8BAA+D;AAC7D,MAAI,KAAK,gBAAgB,KAAM,QAAO,EAAE;EACxC,MAAM,QAAiC,EAAE;EACzC,MAAM,SAAmB,EAAE;AAC3B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,YAAY,EAAE;AAC3D,OAAI,CAAC,aAAa,IAAI,IAAI,CAAE;GAC5B,MAAM,WAAW,kBAAkB,MAAM;AACzC,SAAM,YAAY,SAAS;AAC3B,OAAI,aAAa,MAAO,QAAO,KAAK,IAAI;;AAE1C,MAAI,OAAO,SAAS,EAAG,OAAM,wBAAwB,OAAO,MAAM;AAClE,SAAO;;;;;;CAOT,cAAc,WAAmB,YAA4C;AAC3E,OAAK,QAAQ,WAAW,aAAa,WAAW;;;;;;;;CASlD,MAAM,QAAuB;AAC3B,QAAM,KAAK,OAAO,UAAU;;;;;;;CAS9B,MAAM,uBAAwD;AAC5D,MAAI,KAAK,gBAAgB,KACvB,QAAO,KAAK;EAEd,MAAM,MAA8B,EAAE;EACtC,MAAM,WAAoC,EAAE;AAC5C,MAAI;GACF,MAAM,aAAa,KAAK,cAAc,KAAK;AAC3C,aAAU,yBAAyB;IACjC;IACA,YAAY,KAAK,eAAe,KAAA;IAChC,kBAAkB,KAAK,sBAAsB;IAC9C,CAAC;GACF,MAAM,aAAa,MAAM,KAAK,OAAO,cAAc,YAAY,EAC7D,kBAAkB,KAAK,sBAAsB,EAC9C,CAAC;AACF,QAAK,MAAM,OAAO,kBAAkB;IAClC,MAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,QAAI,UAAU,KAAA,EAAW;AACzB,QAAI,OAAO,OAAO,MAAM;IACxB,MAAM,UAAU,WAAW,eAAe,IAAI;AAC9C,QAAI,YAAY,KAAA,EAAW,UAAS,OAAO;;WAEtC,OAAO;AACd,SAAM,oCAAoC,MAAM;AAChD,QAAK,iBACH,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,EACzD,EAAE,MAAM,iBAAiB,CAC1B;;EAKH,MAAM,SAAS,qBAAqB,KAAK,SAAS;AAClD,OAAK,cAAc,OAAO;AAC1B,OAAK,qBAAqB,OAAO;AACjC,YAAU,qBAAqB,KAAK,YAAY;AAChD,SAAO,KAAK;;;CAId,wBAAiD;AAC/C,SAAO,KAAK;;CAGd,MAAM,SAAS,QAA2C;AACxD,MAAI,OAAO,KAAK,KAAK,KAAK,CAAC,WAAW,EACpC;AAOF,MAAI,KAAK,kBACP;AAEF,OAAK,oBAAoB;AAEzB,OAAK,OAAO,QAAQ;GAClB,YAAY,KAAK,cAAc,KAAK;GACpC,OAAO;GACP,YAAY;IAEV,GAAG,KAAK;IACR,QAAQ,KAAK;IACb,GAAI,KAAK,YAAY,EAAE,aAAa,KAAK,WAAW,GAAG,EAAE;IACzD;IACA,MAAM,KAAK;IACZ;GACF,CAAC;AAEF,QAAM,KAAK,OAAO,UAAU;;;AAIhC,MAAa,YAAY,IAAI,WAAW"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
|
|
2
|
-
import {
|
|
3
|
-
import { n as analytics } from "./analytics-
|
|
2
|
+
import { pt as WIZARD_USER_AGENT } from "./debug-BYIoy1Oz.js";
|
|
3
|
+
import { n as analytics } from "./analytics-YLZKudpE.js";
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/lib/api.ts
|
|
@@ -170,4 +170,4 @@ function handleApiError(error, operation) {
|
|
|
170
170
|
//#endregion
|
|
171
171
|
export { fetchUserData as a, fetchSlackConnected as i, api_exports as n, handleApiError as o, fetchProjectData as r, ApiError as t };
|
|
172
172
|
|
|
173
|
-
//# sourceMappingURL=api-
|
|
173
|
+
//# sourceMappingURL=api-DlRJAFI2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-XHQTOfIi.js","names":[],"sources":["../src/lib/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,MAAa,gBAAgB,EAC1B,OAAO;CAEN,aAAa,EAAE,QAAQ;CACvB,MAAM,EAAE,QAAQ,CAAC,SAAS;CAC1B,IAAI,EAAE,QAAQ,CAAC,SAAS;CAGxB,OAAO,EAAE,QAAQ,CAAC,SAAS;CAC3B,YAAY,EAAE,QAAQ,CAAC,SAAS;CAChC,WAAW,EAAE,QAAQ,CAAC,SAAS;CAC/B,aAAa,EAAE,QAAQ,CAAC,SAAS;CACjC,mBAAmB,EAAE,SAAS,CAAC,SAAS;CACxC,gBAAgB,EAAE,SAAS,CAAC,SAAS;CACrC,UAAU,EAAE,SAAS,CAAC,SAAS;CAG/B,YAAY,EAAE,QAAQ,CAAC,SAAS;CAChC,cAAc,EAAE,QAAQ,CAAC,SAAS;CAClC,gBAAgB,EAAE,SAAS,CAAC,SAAS;CAKrC,sBAAsB,EAAE,QAAQ,CAAC,SAAS;CAI1C,MAAM,EACH,OAAO;EACN,IAAI,EAAE,QAAQ;EACd,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,cAAc,EAAE,QAAQ,CAAC,MAAM;EAC/B,WAAW,EAAE,QAAQ,CAAC,SAAS;EAC/B,YAAY,EAAE,QAAQ,CAAC,SAAS;EAChC,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,UAAU,EAAE,QAAQ,CAAC,SAAS;EAC/B,CAAC,CACD,aAAa;CAChB,cAAc,EACX,OAAO;EACN,IAAI,EAAE,QAAQ,CAAC,MAAM;EACrB,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,kBAAkB,EAAE,QAAQ,CAAC,SAAS;EACtC,aAAa,EAAE,QAAQ,CAAC,SAAS;EAIjC,gCAAgC,EAAE,SAAS,CAAC,SAAS;EACtD,CAAC,CACD,aAAa;CAChB,eAAe,EAAE,MACf,EACG,OAAO;EACN,IAAI,EAAE,QAAQ,CAAC,MAAM;EACrB,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,kBAAkB,EAAE,QAAQ,CAAC,SAAS;EACvC,CAAC,CACD,aAAa,CACjB;CACF,CAAC,CACD,aAAa;;;;;;;;;;;;AAahB,MAAa,yBAAyB,EACnC,OAAO;CACN,OAAO,EAAE,QAAQ,CAAC,SAAS;CAC3B,UAAU,EAAE,QAAQ,CAAC,SAAS;CAC9B,YAAY,EAAE,QAAQ,CAAC,SAAS;CACjC,CAAC,CACD,aAAa;AAGyB,EAAE,OAAO,EAChD,SAAS,EAAE,MAAM,uBAAuB,EACzC,CAAC;AAKF,MAAa,mBAAmB,EAAE,OAAO;CACvC,IAAI,EAAE,QAAQ;CACd,MAAM,EAAE,QAAQ,CAAC,MAAM;CACvB,cAAc,EAAE,QAAQ,CAAC,MAAM;CAC/B,WAAW,EAAE,QAAQ;CACrB,MAAM,EAAE,QAAQ;CAOhB,0BAA0B,EAAE,SAAS,CAAC,SAAS;CAC/C,+BAA+B,EAAE,SAAS,CAAC,SAAS;CACpD,gBAAgB,EAAE,SAAS,CAAC,SAAS;CACtC,CAAC;AAKF,IAAa,WAAb,cAA8B,MAAM;CAClC,YACE,SACA,YACA,UACA;AACA,QAAM,QAAQ;AAHE,OAAA,aAAA;AACA,OAAA,WAAA;AAGhB,OAAK,OAAO;;;AAIhB,eAAsB,cACpB,aACA,SACkB;AAClB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,IAAI,GAAG,QAAQ,kBAAkB,EAC5D,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf,EACF,CAAC;AAEF,SAAO,cAAc,MAAM,SAAS,KAAK;UAClC,OAAO;EACd,MAAM,WAAW,eAAe,OAAO,kBAAkB;AACzD,YAAU,iBAAiB,UAAU;GACnC,UAAU;GACV;GACD,CAAC;AACF,QAAM;;;AA+CV,eAAsB,iBACpB,aACA,WACA,SACqB;AACrB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,IAAI,GAAG,QAAQ,gBAAgB,UAAU,IAAI,EACxE,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf,EACF,CAAC;AAEF,SAAO,iBAAiB,MAAM,SAAS,KAAK;UACrC,OAAO;EACd,MAAM,WAAW,eAAe,OAAO,qBAAqB;AAC5D,YAAU,iBAAiB,UAAU;GACnC,UAAU,iBAAiB,UAAU;GACrC;GACA;GACD,CAAC;AACF,QAAM;;;;AAKV,MAAM,6BAA6B,EAAE,OAAO,EAC1C,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC,EACzE,CAAC;;;;;;;AAQF,eAAsB,oBACpB,aACA,WACA,SACA,QACkB;CAClB,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,QAAQ,gBAAgB,UAAU,iBACrC;EACE,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf;EACD;EACD,CACF;CACD,MAAM,SAAS,2BAA2B,UAAU,SAAS,KAAK;AAClE,KAAI,CAAC,OAAO,QAAS,QAAO;AAC5B,QAAO,OAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,QAAQ;;AAG5D,SAAgB,eAAe,OAAgB,WAA6B;AAC1E,KAAI,MAAM,aAAa,MAAM,EAAE;EAC7B,MAAM,aAAa;EACnB,MAAM,SAAS,WAAW,UAAU;EACpC,MAAM,SAAS,WAAW,UAAU,MAAM;EAC1C,MAAM,WAAW,WAAW,QAAQ;AAEpC,MAAI,WAAW,IACb,QAAO,IAAI,SACT,yCAAyC,aACzC,QACA,SACD;AAGH,MAAI,WAAW,IACb,QAAO,IAAI,SACT,iCAAiC,aACjC,QACA,SACD;AAGH,MAAI,WAAW,IACb,QAAO,IAAI,SACT,sCAAsC,aACtC,QACA,SACD;AAIH,SAAO,IAAI,SADK,UAAU,aAAa,aACV,QAAQ,SAAS;;AAGhD,KAAI,iBAAiB,EAAE,SACrB,QAAO,IAAI,SAAS,2CAA2C,YAAY;AAG7E,QAAO,IAAI,SACT,oCAAoC,UAAU,IAC5C,iBAAiB,QAAQ,MAAM,UAAU,kBAE5C"}
|
|
1
|
+
{"version":3,"file":"api-DlRJAFI2.js","names":[],"sources":["../src/lib/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,MAAa,gBAAgB,EAC1B,OAAO;CAEN,aAAa,EAAE,QAAQ;CACvB,MAAM,EAAE,QAAQ,CAAC,SAAS;CAC1B,IAAI,EAAE,QAAQ,CAAC,SAAS;CAGxB,OAAO,EAAE,QAAQ,CAAC,SAAS;CAC3B,YAAY,EAAE,QAAQ,CAAC,SAAS;CAChC,WAAW,EAAE,QAAQ,CAAC,SAAS;CAC/B,aAAa,EAAE,QAAQ,CAAC,SAAS;CACjC,mBAAmB,EAAE,SAAS,CAAC,SAAS;CACxC,gBAAgB,EAAE,SAAS,CAAC,SAAS;CACrC,UAAU,EAAE,SAAS,CAAC,SAAS;CAG/B,YAAY,EAAE,QAAQ,CAAC,SAAS;CAChC,cAAc,EAAE,QAAQ,CAAC,SAAS;CAClC,gBAAgB,EAAE,SAAS,CAAC,SAAS;CAKrC,sBAAsB,EAAE,QAAQ,CAAC,SAAS;CAI1C,MAAM,EACH,OAAO;EACN,IAAI,EAAE,QAAQ;EACd,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,cAAc,EAAE,QAAQ,CAAC,MAAM;EAC/B,WAAW,EAAE,QAAQ,CAAC,SAAS;EAC/B,YAAY,EAAE,QAAQ,CAAC,SAAS;EAChC,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,UAAU,EAAE,QAAQ,CAAC,SAAS;EAC/B,CAAC,CACD,aAAa;CAChB,cAAc,EACX,OAAO;EACN,IAAI,EAAE,QAAQ,CAAC,MAAM;EACrB,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,kBAAkB,EAAE,QAAQ,CAAC,SAAS;EACtC,aAAa,EAAE,QAAQ,CAAC,SAAS;EAIjC,gCAAgC,EAAE,SAAS,CAAC,SAAS;EACtD,CAAC,CACD,aAAa;CAChB,eAAe,EAAE,MACf,EACG,OAAO;EACN,IAAI,EAAE,QAAQ,CAAC,MAAM;EACrB,MAAM,EAAE,QAAQ,CAAC,SAAS;EAC1B,kBAAkB,EAAE,QAAQ,CAAC,SAAS;EACvC,CAAC,CACD,aAAa,CACjB;CACF,CAAC,CACD,aAAa;;;;;;;;;;;;AAahB,MAAa,yBAAyB,EACnC,OAAO;CACN,OAAO,EAAE,QAAQ,CAAC,SAAS;CAC3B,UAAU,EAAE,QAAQ,CAAC,SAAS;CAC9B,YAAY,EAAE,QAAQ,CAAC,SAAS;CACjC,CAAC,CACD,aAAa;AAGyB,EAAE,OAAO,EAChD,SAAS,EAAE,MAAM,uBAAuB,EACzC,CAAC;AAKF,MAAa,mBAAmB,EAAE,OAAO;CACvC,IAAI,EAAE,QAAQ;CACd,MAAM,EAAE,QAAQ,CAAC,MAAM;CACvB,cAAc,EAAE,QAAQ,CAAC,MAAM;CAC/B,WAAW,EAAE,QAAQ;CACrB,MAAM,EAAE,QAAQ;CAOhB,0BAA0B,EAAE,SAAS,CAAC,SAAS;CAC/C,+BAA+B,EAAE,SAAS,CAAC,SAAS;CACpD,gBAAgB,EAAE,SAAS,CAAC,SAAS;CACtC,CAAC;AAKF,IAAa,WAAb,cAA8B,MAAM;CAClC,YACE,SACA,YACA,UACA;AACA,QAAM,QAAQ;AAHE,OAAA,aAAA;AACA,OAAA,WAAA;AAGhB,OAAK,OAAO;;;AAIhB,eAAsB,cACpB,aACA,SACkB;AAClB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,IAAI,GAAG,QAAQ,kBAAkB,EAC5D,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf,EACF,CAAC;AAEF,SAAO,cAAc,MAAM,SAAS,KAAK;UAClC,OAAO;EACd,MAAM,WAAW,eAAe,OAAO,kBAAkB;AACzD,YAAU,iBAAiB,UAAU;GACnC,UAAU;GACV;GACD,CAAC;AACF,QAAM;;;AA+CV,eAAsB,iBACpB,aACA,WACA,SACqB;AACrB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,IAAI,GAAG,QAAQ,gBAAgB,UAAU,IAAI,EACxE,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf,EACF,CAAC;AAEF,SAAO,iBAAiB,MAAM,SAAS,KAAK;UACrC,OAAO;EACd,MAAM,WAAW,eAAe,OAAO,qBAAqB;AAC5D,YAAU,iBAAiB,UAAU;GACnC,UAAU,iBAAiB,UAAU;GACrC;GACA;GACD,CAAC;AACF,QAAM;;;;AAKV,MAAM,6BAA6B,EAAE,OAAO,EAC1C,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC,EACzE,CAAC;;;;;;;AAQF,eAAsB,oBACpB,aACA,WACA,SACA,QACkB;CAClB,MAAM,WAAW,MAAM,MAAM,IAC3B,GAAG,QAAQ,gBAAgB,UAAU,iBACrC;EACE,SAAS;GACP,eAAe,UAAU;GACzB,cAAc;GACf;EACD;EACD,CACF;CACD,MAAM,SAAS,2BAA2B,UAAU,SAAS,KAAK;AAClE,KAAI,CAAC,OAAO,QAAS,QAAO;AAC5B,QAAO,OAAO,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,QAAQ;;AAG5D,SAAgB,eAAe,OAAgB,WAA6B;AAC1E,KAAI,MAAM,aAAa,MAAM,EAAE;EAC7B,MAAM,aAAa;EACnB,MAAM,SAAS,WAAW,UAAU;EACpC,MAAM,SAAS,WAAW,UAAU,MAAM;EAC1C,MAAM,WAAW,WAAW,QAAQ;AAEpC,MAAI,WAAW,IACb,QAAO,IAAI,SACT,yCAAyC,aACzC,QACA,SACD;AAGH,MAAI,WAAW,IACb,QAAO,IAAI,SACT,iCAAiC,aACjC,QACA,SACD;AAGH,MAAI,WAAW,IACb,QAAO,IAAI,SACT,sCAAsC,aACtC,QACA,SACD;AAIH,SAAO,IAAI,SADK,UAAU,aAAa,aACV,QAAQ,SAAS;;AAGhD,KAAI,iBAAiB,EAAE,SACrB,QAAO,IAAI,SAAS,2CAA2C,YAAY;AAG7E,QAAO,IAAI,SACT,oCAAoC,UAAU,IAC5C,iBAAiB,QAAQ,MAAM,UAAU,kBAE5C"}
|
package/dist/bin.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Ct as
|
|
3
|
-
import { n as analytics } from "./analytics-
|
|
4
|
-
import { r as setEntryCommand } from "./telemetry-
|
|
5
|
-
import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-
|
|
6
|
-
import { n as isUsingTypeScript } from "./setup-utils-
|
|
7
|
-
import { o as handleApiError } from "./api-
|
|
2
|
+
import { Ct as isHeadless, L as Integration, St as headlessOption, U as POSTHOG_DOCS_URL, a as getLogFilePath, f as getUI, gt as VERSION, m as LoggingUI, mt as getSkillsBaseUrl, p as setUI, pt as WIZARD_USER_AGENT, r as debug, s as logToFile, wt as regionOption } from "./debug-BYIoy1Oz.js";
|
|
3
|
+
import { n as analytics } from "./analytics-YLZKudpE.js";
|
|
4
|
+
import { r as setEntryCommand } from "./telemetry-C72C8V6q.js";
|
|
5
|
+
import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-CYxETRCt.js";
|
|
6
|
+
import { n as isUsingTypeScript } from "./setup-utils-e78zkZoW.js";
|
|
7
|
+
import { o as handleApiError } from "./api-DlRJAFI2.js";
|
|
8
8
|
import "./wizard-session-BUhlJ1b8.js";
|
|
9
|
-
import { r as runCleanups } from "./wizard-abort-
|
|
10
|
-
import { n as isNonInteractiveEnvironment } from "./environment-
|
|
11
|
-
import { n as runAgent, s as authenticate } from "./agent-runner-
|
|
12
|
-
import {
|
|
13
|
-
import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-
|
|
14
|
-
import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-
|
|
15
|
-
import { i as SPINNER_MESSAGE } from "./registry-
|
|
16
|
-
import { C as resolveProjectDir, _ as WAREHOUSE_ABORT_CASES, a as LINE_CHART_BLOCK, b as detectWarehouseSources, d as isClearBlock, g as HEALTH_CHECK_STEP, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as detectWarehousePrerequisites, w as gatherFrameworkContext, x as detectIntegrationProjects, y as getDetectedWarehouseSources } from "./posthog-integration-
|
|
17
|
-
import { n as readApiKeyFromEnv } from "./env-api-key-
|
|
9
|
+
import { r as runCleanups } from "./wizard-abort-DRscjUOg.js";
|
|
10
|
+
import { n as isNonInteractiveEnvironment } from "./environment-Br-oy8cq.js";
|
|
11
|
+
import { n as runAgent, s as authenticate } from "./agent-runner-X3JWS2WK.js";
|
|
12
|
+
import { p as writeJsonAtomic } from "./queue-tools-BtJ7_fEW.js";
|
|
13
|
+
import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-C4CV7p0L.js";
|
|
14
|
+
import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-DKRsn0lN.js";
|
|
15
|
+
import { i as SPINNER_MESSAGE } from "./registry-B-whj8SQ.js";
|
|
16
|
+
import { C as resolveProjectDir, _ as WAREHOUSE_ABORT_CASES, a as LINE_CHART_BLOCK, b as detectWarehouseSources, d as isClearBlock, g as HEALTH_CHECK_STEP, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as detectWarehousePrerequisites, w as gatherFrameworkContext, x as detectIntegrationProjects, y as getDetectedWarehouseSources } from "./posthog-integration-CoAID6XW.js";
|
|
17
|
+
import { n as readApiKeyFromEnv } from "./env-api-key-B7kiHT3h.js";
|
|
18
18
|
import { satisfies } from "semver";
|
|
19
19
|
import yargs from "yargs";
|
|
20
20
|
import { hideBin } from "yargs/helpers";
|
|
@@ -202,7 +202,7 @@ function runProvision(argv) {
|
|
|
202
202
|
}
|
|
203
203
|
async function provision({ email, region, name, baseUrl, jsonMode }) {
|
|
204
204
|
try {
|
|
205
|
-
const { provisionNewAccount } = await import("./provisioning-
|
|
205
|
+
const { provisionNewAccount } = await import("./provisioning-xirriYQJ.js").then((n) => n.r);
|
|
206
206
|
if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
|
|
207
207
|
emitResult(await provisionNewAccount(email, name, region, { baseUrl }), jsonMode);
|
|
208
208
|
process.exit(0);
|
|
@@ -269,22 +269,22 @@ const basicIntegrationCommand = {
|
|
|
269
269
|
setEntryCommand("integrate");
|
|
270
270
|
(async () => {
|
|
271
271
|
if (isHeadless(argv)) {
|
|
272
|
-
const { runHeadlessInstall } = await import("./ci-install-
|
|
272
|
+
const { runHeadlessInstall } = await import("./ci-install-Cyqj_i3I.js");
|
|
273
273
|
return runHeadlessInstall(argv);
|
|
274
274
|
}
|
|
275
275
|
if (argv.ci) {
|
|
276
|
-
const { runCIInstall } = await import("./ci-install-
|
|
276
|
+
const { runCIInstall } = await import("./ci-install-Cyqj_i3I.js");
|
|
277
277
|
return runCIInstall(argv);
|
|
278
278
|
}
|
|
279
279
|
if (isNonInteractiveEnvironment()) {
|
|
280
|
-
const { failNonInteractive } = await import("./non-interactive-
|
|
280
|
+
const { failNonInteractive } = await import("./non-interactive-BR4kmPE5.js");
|
|
281
281
|
return failNonInteractive();
|
|
282
282
|
}
|
|
283
283
|
if (argv.playground) {
|
|
284
|
-
const { runPlayground } = await import("./playground-
|
|
284
|
+
const { runPlayground } = await import("./playground-BSMgOp3k.js");
|
|
285
285
|
return runPlayground();
|
|
286
286
|
}
|
|
287
|
-
const { runInteractive } = await import("./interactive-
|
|
287
|
+
const { runInteractive } = await import("./interactive-CYv97Lgg.js");
|
|
288
288
|
runInteractive(argv);
|
|
289
289
|
})();
|
|
290
290
|
}
|
|
@@ -3238,6 +3238,64 @@ STEP 7 — Write the report and hand off. (skill: "Report")
|
|
|
3238
3238
|
deferred. Tell the user findings will start appearing in their inbox
|
|
3239
3239
|
at ${inboxUrl} within about 30 minutes.`;
|
|
3240
3240
|
}
|
|
3241
|
+
//#endregion
|
|
3242
|
+
//#region src/lib/programs/self-driving/step-keys.ts
|
|
3243
|
+
/**
|
|
3244
|
+
* First match wins, so order is the rule. Entity words come before verbs: "Checking GitHub
|
|
3245
|
+
* connection" is the GitHub step, not the access-check step, and "Connecting issue trackers" must
|
|
3246
|
+
* not fall through to GitHub just because a later run words it "Connecting GitHub issues".
|
|
3247
|
+
*/
|
|
3248
|
+
const RULES = [
|
|
3249
|
+
{
|
|
3250
|
+
key: "connect_issue_trackers",
|
|
3251
|
+
match: /issue[\s-]?track/i
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
key: "setup_replay_vision",
|
|
3255
|
+
match: /replay vision|vision scanner/i
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
key: "design_custom_scouts",
|
|
3259
|
+
match: /custom scout|designing scout/i
|
|
3260
|
+
},
|
|
3261
|
+
{
|
|
3262
|
+
key: "configure_scouts",
|
|
3263
|
+
match: /scout troop|tun(e|ing) .*scout|configur\w* .*scout/i
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
key: "enable_signal_sources",
|
|
3267
|
+
match: /signal source/i
|
|
3268
|
+
},
|
|
3269
|
+
{
|
|
3270
|
+
key: "connect_github",
|
|
3271
|
+
match: /github/i
|
|
3272
|
+
},
|
|
3273
|
+
{
|
|
3274
|
+
key: "enable_products",
|
|
3275
|
+
match: /product/i
|
|
3276
|
+
},
|
|
3277
|
+
{
|
|
3278
|
+
key: "write_report",
|
|
3279
|
+
match: /report/i
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
key: "check_access",
|
|
3283
|
+
match: /access/i
|
|
3284
|
+
},
|
|
3285
|
+
{
|
|
3286
|
+
key: "read_context",
|
|
3287
|
+
match: /read|context|project state/i
|
|
3288
|
+
}
|
|
3289
|
+
];
|
|
3290
|
+
/**
|
|
3291
|
+
* The canonical key for an agent-authored step label, or `undefined` when nothing matches — an
|
|
3292
|
+
* unmatched step is left unkeyed rather than bucketed, so a new step shows up as a gap to name
|
|
3293
|
+
* instead of quietly inflating whichever key it landed nearest.
|
|
3294
|
+
*/
|
|
3295
|
+
function resolveSelfDrivingStepKey(stepName) {
|
|
3296
|
+
if (!stepName) return void 0;
|
|
3297
|
+
return RULES.find((rule) => rule.match.test(stepName))?.key;
|
|
3298
|
+
}
|
|
3241
3299
|
/** One line, for a screen that only has room to say what it costs. */
|
|
3242
3300
|
const PRICING_SHORT = `Agents charge a flat $15 per pull request they ship.`;
|
|
3243
3301
|
/** The same fact at length, plus the bit people are surprised by later. */
|
|
@@ -3643,6 +3701,7 @@ const buildRun = (session) => Promise.resolve({
|
|
|
3643
3701
|
richLinks: true,
|
|
3644
3702
|
askTimeoutMs: 1800 * 1e3,
|
|
3645
3703
|
trackStepProgress: true,
|
|
3704
|
+
resolveStepKey: resolveSelfDrivingStepKey,
|
|
3646
3705
|
postRun: async (session) => {
|
|
3647
3706
|
await removeInstalledSkill(session.installDir);
|
|
3648
3707
|
},
|
|
@@ -3991,12 +4050,12 @@ const mcpAddCommand = {
|
|
|
3991
4050
|
function runMcpAdd(argv) {
|
|
3992
4051
|
const features = parseFeatures(argv.features);
|
|
3993
4052
|
(async () => {
|
|
3994
|
-
const { readApiKeyFromEnv } = await import("./env-api-key-
|
|
4053
|
+
const { readApiKeyFromEnv } = await import("./env-api-key-B7kiHT3h.js").then((n) => n.t);
|
|
3995
4054
|
const apiKey = argv.apiKey || readApiKeyFromEnv();
|
|
3996
4055
|
const debug = argv.debug;
|
|
3997
4056
|
const localMcp = argv.local;
|
|
3998
4057
|
try {
|
|
3999
|
-
const { startTUI } = await import("./start-tui-
|
|
4058
|
+
const { startTUI } = await import("./start-tui-B08PfE0V.js");
|
|
4000
4059
|
const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
|
|
4001
4060
|
const tui = startTUI(VERSION, Program.McpAdd);
|
|
4002
4061
|
tui.store.session = buildSession({
|
|
@@ -4009,7 +4068,7 @@ function runMcpAdd(argv) {
|
|
|
4009
4068
|
} catch (error) {
|
|
4010
4069
|
if (!isTUIUnavailable(error)) throw error;
|
|
4011
4070
|
setUI(new LoggingUI());
|
|
4012
|
-
const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-
|
|
4071
|
+
const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-Bsyb6MUB.js").then((n) => n.r);
|
|
4013
4072
|
await addMCPServerToClientsStep({
|
|
4014
4073
|
local: localMcp,
|
|
4015
4074
|
features,
|
|
@@ -4039,7 +4098,7 @@ function runMcpRemove(argv) {
|
|
|
4039
4098
|
const debug = argv.debug;
|
|
4040
4099
|
const localMcp = argv.local;
|
|
4041
4100
|
try {
|
|
4042
|
-
const { startTUI } = await import("./start-tui-
|
|
4101
|
+
const { startTUI } = await import("./start-tui-B08PfE0V.js");
|
|
4043
4102
|
const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
|
|
4044
4103
|
const tui = startTUI(VERSION, Program.McpRemove);
|
|
4045
4104
|
tui.store.session = buildSession({
|
|
@@ -4050,7 +4109,7 @@ function runMcpRemove(argv) {
|
|
|
4050
4109
|
} catch (error) {
|
|
4051
4110
|
if (!isTUIUnavailable(error)) throw error;
|
|
4052
4111
|
setUI(new LoggingUI());
|
|
4053
|
-
const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-
|
|
4112
|
+
const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-Bsyb6MUB.js").then((n) => n.r);
|
|
4054
4113
|
await removeMCPServerFromClientsStep({ local: localMcp });
|
|
4055
4114
|
}
|
|
4056
4115
|
})();
|
|
@@ -4072,7 +4131,7 @@ function runMcpTutorial(argv) {
|
|
|
4072
4131
|
const debug = argv.debug;
|
|
4073
4132
|
const localMcp = argv.local;
|
|
4074
4133
|
try {
|
|
4075
|
-
const { startTUI } = await import("./start-tui-
|
|
4134
|
+
const { startTUI } = await import("./start-tui-B08PfE0V.js");
|
|
4076
4135
|
const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
|
|
4077
4136
|
const tui = startTUI(VERSION, Program.McpTutorial);
|
|
4078
4137
|
tui.store.session = buildSession({
|
|
@@ -4154,10 +4213,10 @@ function runWizard(config, options) {
|
|
|
4154
4213
|
(async () => {
|
|
4155
4214
|
try {
|
|
4156
4215
|
const installDir = options.installDir || process.cwd();
|
|
4157
|
-
const { startTUI } = await import("./start-tui-
|
|
4216
|
+
const { startTUI } = await import("./start-tui-B08PfE0V.js");
|
|
4158
4217
|
const { buildSession, RunPhase } = await import("./wizard-session-DZUDtS5d.js");
|
|
4159
|
-
const { TaskStreamPush } = await import("./task-stream-
|
|
4160
|
-
const { PostHogDestination } = await import("./posthog-
|
|
4218
|
+
const { TaskStreamPush } = await import("./task-stream-DvTf1NOr.js");
|
|
4219
|
+
const { PostHogDestination } = await import("./posthog-ZWC-083a.js");
|
|
4161
4220
|
tui = startTUI(WIZARD_VERSION, config.id);
|
|
4162
4221
|
const activeTui = tui;
|
|
4163
4222
|
const session = buildSession({
|
|
@@ -4223,7 +4282,7 @@ function runWizard(config, options) {
|
|
|
4223
4282
|
if (shown(step)) await advanceStep(step, activeTui.store, config);
|
|
4224
4283
|
}
|
|
4225
4284
|
else if (skipAgent) {
|
|
4226
|
-
const { getOrAskForProjectData } = await import("./setup-utils-
|
|
4285
|
+
const { getOrAskForProjectData } = await import("./setup-utils-e78zkZoW.js").then((n) => n.r);
|
|
4227
4286
|
const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
|
|
4228
4287
|
signup: session.signup,
|
|
4229
4288
|
ci: session.ci,
|
|
@@ -4325,10 +4384,10 @@ function runNonInteractive(config, options, mode) {
|
|
|
4325
4384
|
(async () => {
|
|
4326
4385
|
const path = await import("path");
|
|
4327
4386
|
const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-DZUDtS5d.js");
|
|
4328
|
-
const { readEnvironment } = await import("./environment-
|
|
4329
|
-
const { readApiKeyFromEnv } = await import("./env-api-key-
|
|
4330
|
-
const { configureLogFileFromEnvironment, logToFile } = await import("./debug-
|
|
4331
|
-
const { wizardAbort, WizardError } = await import("./wizard-abort-
|
|
4387
|
+
const { readEnvironment } = await import("./environment-Br-oy8cq.js").then((n) => n.t);
|
|
4388
|
+
const { readApiKeyFromEnv } = await import("./env-api-key-B7kiHT3h.js").then((n) => n.t);
|
|
4389
|
+
const { configureLogFileFromEnvironment, logToFile } = await import("./debug-2W78IhFE.js");
|
|
4390
|
+
const { wizardAbort, WizardError } = await import("./wizard-abort-BUpx4qnK.js");
|
|
4332
4391
|
configureLogFileFromEnvironment();
|
|
4333
4392
|
const env = readEnvironment();
|
|
4334
4393
|
const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
|
|
@@ -4361,9 +4420,9 @@ function runNonInteractive(config, options, mode) {
|
|
|
4361
4420
|
let store = null;
|
|
4362
4421
|
let taskStream = null;
|
|
4363
4422
|
if (mode === "headless") {
|
|
4364
|
-
const { WizardStore } = await import("./store-
|
|
4365
|
-
const { HeadlessUI } = await import("./headless-ui-
|
|
4366
|
-
const { TaskStreamPush, PostHogDestination } = await import("./task-stream-
|
|
4423
|
+
const { WizardStore } = await import("./store-B-ctPH7O.js").then((n) => n.n);
|
|
4424
|
+
const { HeadlessUI } = await import("./headless-ui-Dd7Ty4Ul.js");
|
|
4425
|
+
const { TaskStreamPush, PostHogDestination } = await import("./task-stream-DvTf1NOr.js");
|
|
4367
4426
|
store = new WizardStore(config.id);
|
|
4368
4427
|
store.session = session;
|
|
4369
4428
|
setUI(new HeadlessUI(store));
|
|
@@ -4419,7 +4478,7 @@ function runNonInteractive(config, options, mode) {
|
|
|
4419
4478
|
});
|
|
4420
4479
|
}
|
|
4421
4480
|
}
|
|
4422
|
-
const { runAgent } = await import("./agent-runner-
|
|
4481
|
+
const { runAgent } = await import("./agent-runner-X3JWS2WK.js").then((n) => n.t);
|
|
4423
4482
|
await runAgent(config, session);
|
|
4424
4483
|
await settleStream(RunPhase.Completed);
|
|
4425
4484
|
} catch (error) {
|
|
@@ -5625,7 +5684,7 @@ async function runDoctorCI(options) {
|
|
|
5625
5684
|
getUI().intro("Welcome to the PostHog setup wizard");
|
|
5626
5685
|
getUI().log.info("Running posthog-doctor in CI mode");
|
|
5627
5686
|
try {
|
|
5628
|
-
const { getOrAskForProjectData } = await import("./setup-utils-
|
|
5687
|
+
const { getOrAskForProjectData } = await import("./setup-utils-e78zkZoW.js").then((n) => n.r);
|
|
5629
5688
|
const { host, accessToken, projectId } = await getOrAskForProjectData({
|
|
5630
5689
|
signup: false,
|
|
5631
5690
|
ci: true,
|
|
@@ -5643,7 +5702,7 @@ async function runDoctorCI(options) {
|
|
|
5643
5702
|
for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
|
|
5644
5703
|
process.exit(1);
|
|
5645
5704
|
} catch (error) {
|
|
5646
|
-
const { ApiError } = await import("./api-
|
|
5705
|
+
const { ApiError } = await import("./api-DlRJAFI2.js").then((n) => n.n);
|
|
5647
5706
|
const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
|
|
5648
5707
|
getUI().log.error(`Doctor failed: ${message}`);
|
|
5649
5708
|
process.exit(1);
|
|
@@ -5725,7 +5784,7 @@ function runSlackConnect(argv) {
|
|
|
5725
5784
|
(async () => {
|
|
5726
5785
|
const debug = argv.debug;
|
|
5727
5786
|
try {
|
|
5728
|
-
const { startTUI } = await import("./start-tui-
|
|
5787
|
+
const { startTUI } = await import("./start-tui-B08PfE0V.js");
|
|
5729
5788
|
const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
|
|
5730
5789
|
const tui = startTUI(VERSION, Program.SlackConnect);
|
|
5731
5790
|
tui.store.session = buildSession({
|