@posthog/wizard 2.38.0 → 2.38.2
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/dist/{add-mcp-server-to-clients-CeoyDLC-.js → add-mcp-server-to-clients-CHSzsAG8.js} +6 -6
- package/dist/{add-mcp-server-to-clients-CeoyDLC-.js.map → add-mcp-server-to-clients-CHSzsAG8.js.map} +1 -1
- package/dist/{agent-interface-BAodys0H.js → agent-interface-DExARWay.js} +13 -11
- package/dist/{agent-interface-BAodys0H.js.map → agent-interface-DExARWay.js.map} +1 -1
- package/dist/{agent-runner-BMuJP8fb.js → agent-runner-CMfTmM3B.js} +20 -18
- package/dist/{agent-runner-BMuJP8fb.js.map → agent-runner-CMfTmM3B.js.map} +1 -1
- package/dist/{analytics-CvIK_07d.js → analytics-Ciob-x8a.js} +3 -3
- package/dist/{analytics-CvIK_07d.js.map → analytics-Ciob-x8a.js.map} +1 -1
- package/dist/analytics-DbfiRvPS.js +2 -0
- package/dist/{api-CyA47qug.js → api-T_WHs3qZ.js} +3 -3
- package/dist/{api-CyA47qug.js.map → api-T_WHs3qZ.js.map} +1 -1
- package/dist/bin.js +176 -82
- package/dist/bin.js.map +1 -1
- package/dist/{ci-install-C_o0Q7Fg.js → ci-install-DJN5aT7W.js} +4 -4
- package/dist/{ci-install-C_o0Q7Fg.js.map → ci-install-DJN5aT7W.js.map} +1 -1
- package/dist/{debug-BhGq9rn9.js → debug-Cne-cLxd.js} +1 -1
- package/dist/{debug-CgdI7Rd-.js → debug-D1lORUFs.js} +41 -19
- package/dist/debug-D1lORUFs.js.map +1 -0
- package/dist/{defaults-b2Bo9Wid.js → defaults-a0e6IRjP.js} +1 -1
- package/dist/{defaults-b2Bo9Wid.js.map → defaults-a0e6IRjP.js.map} +1 -1
- package/dist/{env-api-key-Bezg_uXQ.js → env-api-key-DEl3LJBv.js} +1 -1
- package/dist/{env-api-key-Bezg_uXQ.js.map → env-api-key-DEl3LJBv.js.map} +1 -1
- package/dist/{environment-v22_Genm.js → environment-BEsDCA2q.js} +3 -3
- package/dist/{environment-v22_Genm.js.map → environment-BEsDCA2q.js.map} +1 -1
- package/dist/{file-utils-Gvc2Ysa_.js → file-utils-Cn3dzlo-.js} +2 -2
- package/dist/{file-utils-Gvc2Ysa_.js.map → file-utils-Cn3dzlo-.js.map} +1 -1
- package/dist/{headless-ui-BtBcuxOj.js → headless-ui-DGunpK-s.js} +2 -2
- package/dist/{headless-ui-BtBcuxOj.js.map → headless-ui-DGunpK-s.js.map} +1 -1
- package/dist/{host-resolution-CUjt6efz.js → host-resolution-BnUJZKId.js} +3 -3
- package/dist/{host-resolution-CUjt6efz.js.map → host-resolution-BnUJZKId.js.map} +1 -1
- package/dist/{interactive-CRdgBMxk.js → interactive-BB2IDSWP.js} +2 -2
- package/dist/{interactive-CRdgBMxk.js.map → interactive-BB2IDSWP.js.map} +1 -1
- package/dist/{mcp-XWzcKTeK.js → mcp-CgA5vyiS.js} +2 -2
- package/dist/{mcp-XWzcKTeK.js.map → mcp-CgA5vyiS.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-DeC85Icy.js → mcp-prompt-streaming-CGAkq9qR.js} +4 -4
- package/dist/{mcp-prompt-streaming-DeC85Icy.js.map → mcp-prompt-streaming-CGAkq9qR.js.map} +1 -1
- package/dist/{non-interactive-B-Ku4EOx.js → non-interactive-Dko5F3vh.js} +2 -2
- package/dist/{non-interactive-B-Ku4EOx.js.map → non-interactive-Dko5F3vh.js.map} +1 -1
- package/dist/{package-json-DCuoye-H.js → package-json-CDs1ZVKu.js} +1 -1
- package/dist/{package-json-DCuoye-H.js.map → package-json-CDs1ZVKu.js.map} +1 -1
- package/dist/{package-manager-Dj6dTKlK.js → package-manager-WKKQADHM.js} +2 -2
- package/dist/{package-manager-Dj6dTKlK.js.map → package-manager-WKKQADHM.js.map} +1 -1
- package/dist/{playground-CmKFAlCE.js → playground-UtT6zbjE.js} +6 -6
- package/dist/{playground-CmKFAlCE.js.map → playground-UtT6zbjE.js.map} +1 -1
- package/dist/{posthog-Dnl6wsYc.js → posthog-BESrI6-T.js} +1 -1
- package/dist/{posthog-Dnl6wsYc.js.map → posthog-BESrI6-T.js.map} +1 -1
- package/dist/{posthog-integration-CcJWXPDa.js → posthog-integration-CWXSng0W.js} +41 -22
- package/dist/posthog-integration-CWXSng0W.js.map +1 -0
- package/dist/{provisioning-BIuAPRcH.js → provisioning-CgomM26T.js} +4 -4
- package/dist/{provisioning-BIuAPRcH.js.map → provisioning-CgomM26T.js.map} +1 -1
- package/dist/{registry-ByrVTIE9.js → registry-BQIX-25O.js} +12 -9
- package/dist/{registry-ByrVTIE9.js.map → registry-BQIX-25O.js.map} +1 -1
- package/dist/{security-uSP4HiC0.js → security-pJOYY5LO.js} +3 -3
- package/dist/{security-uSP4HiC0.js.map → security-pJOYY5LO.js.map} +1 -1
- package/dist/{setup-utils-DhmigSLO.js → setup-utils-DLGNk8T6.js} +9 -9
- package/dist/{setup-utils-DhmigSLO.js.map → setup-utils-DLGNk8T6.js.map} +1 -1
- package/dist/{start-tui-DKHnPg5a.js → start-tui-BjCKghQq.js} +133 -73
- package/dist/start-tui-BjCKghQq.js.map +1 -0
- package/dist/{steps-BBzIrOMY.js → steps-XjV9CiqH.js} +7 -7
- package/dist/{steps-BBzIrOMY.js.map → steps-XjV9CiqH.js.map} +1 -1
- package/dist/{store-BV1a-8Wp.js → store-CDIxU0rr.js} +8 -6
- package/dist/{store-BV1a-8Wp.js.map → store-CDIxU0rr.js.map} +1 -1
- package/dist/{subagent-fP1QmYBX.js → subagent-S5Yh16IJ.js} +2 -2
- package/dist/{subagent-fP1QmYBX.js.map → subagent-S5Yh16IJ.js.map} +1 -1
- package/dist/{task-stream-C6s7qUCE.js → task-stream-rC4IEUHC.js} +3 -3
- package/dist/{task-stream-C6s7qUCE.js.map → task-stream-rC4IEUHC.js.map} +1 -1
- package/dist/{tasks-YFkqfEio.js → tasks-wbjOdifO.js} +2 -2
- package/dist/{tasks-YFkqfEio.js.map → tasks-wbjOdifO.js.map} +1 -1
- package/dist/{telemetry-wOdXbZLp.js → telemetry-BfnR7gtf.js} +3 -3
- package/dist/{telemetry-wOdXbZLp.js.map → telemetry-BfnR7gtf.js.map} +1 -1
- package/dist/{terminal-IAHPQoAT.js → terminal-BvRwVeQp.js} +13 -13
- package/dist/{terminal-IAHPQoAT.js.map → terminal-BvRwVeQp.js.map} +1 -1
- package/dist/{tools-DdzmiBKF.js → tools-CbtYlZV4.js} +4 -4
- package/dist/{tools-DdzmiBKF.js.map → tools-CbtYlZV4.js.map} +1 -1
- package/dist/{urls-BR7jQgQp.js → urls-CilgPbS-.js} +2 -2
- package/dist/{urls-BR7jQgQp.js.map → urls-CilgPbS-.js.map} +1 -1
- package/dist/{wizard-abort-bulz-_og.js → wizard-abort-Cp5Y7f8C.js} +4 -4
- package/dist/{wizard-abort-bulz-_og.js.map → wizard-abort-Cp5Y7f8C.js.map} +1 -1
- package/dist/{wizard-abort-lbH6n2WG.js → wizard-abort-DHrEGqaC.js} +1 -1
- package/dist/{wizard-session-DWeUEncV.js → wizard-session-BoXAZEOf.js} +1 -1
- package/dist/{wizard-session-DWeUEncV.js.map → wizard-session-BoXAZEOf.js.map} +1 -1
- package/dist/{wizard-session-B1ZM4OBv.js → wizard-session-C8W_6X0j.js} +1 -1
- package/dist/{wizard-tools-CGrE1O_b.js → wizard-tools-DQTfAzSV.js} +53 -32
- package/dist/wizard-tools-DQTfAzSV.js.map +1 -0
- package/package.json +2 -1
- package/dist/debug-CgdI7Rd-.js.map +0 -1
- package/dist/posthog-integration-CcJWXPDa.js.map +0 -1
- package/dist/start-tui-DKHnPg5a.js.map +0 -1
- package/dist/wizard-tools-CGrE1O_b.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, r as debug, s as logToFile, x as ANALYTICS_TEAM_TAG, y as ANALYTICS_HOST_URL } from "./debug-D1lORUFs.js";
|
|
2
2
|
import { PostHog } from "posthog-node";
|
|
3
3
|
import { v4 } from "uuid";
|
|
4
4
|
//#region src/utils/ci-flag-overrides.ts
|
|
@@ -227,6 +227,6 @@ var Analytics = class {
|
|
|
227
227
|
};
|
|
228
228
|
const analytics = new Analytics();
|
|
229
229
|
//#endregion
|
|
230
|
-
export { ciExcludedTaskTypes as i,
|
|
230
|
+
export { ciExcludedTaskTypes as a, sessionProperties as i, analytics as n, groupsFromUser as r, Analytics as t };
|
|
231
231
|
|
|
232
|
-
//# sourceMappingURL=analytics-
|
|
232
|
+
//# sourceMappingURL=analytics-Ciob-x8a.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics-CvIK_07d.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): Record<string, string> {\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;\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return flags;\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 merged = { ...flags };\n for (const [key, value] of Object.entries(overrides)) {\n merged[key] = String(value);\n }\n logToFile('[flags] CI overrides applied', overrides);\n return merged;\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} 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 } from '@env';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\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\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 groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\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 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.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 }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n return { $app_name: this.appName, ...this.personProperties };\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 ...properties,\n },\n });\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 async getFeatureFlag(flagKey: string): Promise<string | boolean | undefined> {\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n return await this.client.getFeatureFlag(flagKey, distinctId, {\n sendFeatureFlagEvents: true,\n personProperties: this.flagPersonProperties(),\n });\n } catch (error) {\n debug('Failed to get feature flag:', flagKey, error);\n return undefined;\n }\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 async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\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 result = await this.client.getAllFlagsAndPayloads(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n const flags = result.featureFlags ?? {};\n for (const [key, value] of Object.entries(flags)) {\n if (value === undefined) continue;\n out[key] = typeof value === 'boolean' ? String(value) : String(value);\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 this.activeFlags = applyCiFlagOverrides(out);\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\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-Ciob-x8a.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): Record<string, string> {\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;\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return flags;\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 merged = { ...flags };\n for (const [key, value] of Object.entries(overrides)) {\n merged[key] = String(value);\n }\n logToFile('[flags] CI overrides applied', overrides);\n return merged;\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} 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 } from '@env';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\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\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 groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\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 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.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 }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n return { $app_name: this.appName, ...this.personProperties };\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 ...properties,\n },\n });\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 async getFeatureFlag(flagKey: string): Promise<string | boolean | undefined> {\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n return await this.client.getFeatureFlag(flagKey, distinctId, {\n sendFeatureFlagEvents: true,\n personProperties: this.flagPersonProperties(),\n });\n } catch (error) {\n debug('Failed to get feature flag:', flagKey, error);\n return undefined;\n }\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 async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\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 result = await this.client.getAllFlagsAndPayloads(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n const flags = result.featureFlags ?? {};\n for (const [key, value] of Object.entries(flags)) {\n if (value === undefined) continue;\n out[key] = typeof value === 'boolean' ? String(value) : String(value);\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 this.activeFlags = applyCiFlagOverrides(out);\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\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,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { st as WIZARD_USER_AGENT } from "./debug-D1lORUFs.js";
|
|
3
|
+
import { n as analytics } from "./analytics-Ciob-x8a.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-T_WHs3qZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-CyA47qug.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":"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"}
|
|
1
|
+
{"version":3,"file":"api-T_WHs3qZ.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":"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"}
|
package/dist/bin.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as setEntryCommand } from "./telemetry-
|
|
5
|
-
import { n as isUsingTypeScript } from "./setup-utils-
|
|
6
|
-
import { o as getUiHostFromHost } from "./urls-
|
|
7
|
-
import { t as HostResolution } from "./host-resolution-
|
|
8
|
-
import { o as handleApiError } from "./api-
|
|
9
|
-
import "./wizard-session-
|
|
10
|
-
import { r as runCleanups } from "./wizard-abort-
|
|
11
|
-
import { n as isNonInteractiveEnvironment } from "./environment-
|
|
12
|
-
import { n as runAgent, o as authenticate } from "./agent-runner-
|
|
13
|
-
import { C as SETUP_REPORT_FILE, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-
|
|
14
|
-
import { h as AUDIT_REPORT_FILE, i as fetchSkillMenu, m as AUDIT_CHECKS_KEY, p as AUDIT_CHECKS_FILE, t as WIZARD_TOOL_NAMES } from "./wizard-tools-
|
|
15
|
-
import { i as SPINNER_MESSAGE, t as FRAMEWORK_REGISTRY } from "./registry-
|
|
16
|
-
import { _ as gatherFrameworkContext, a as LINE_CHART_BLOCK, b as parseRequirementsTxt, g as detectProjectsWithAgent, h as HEALTH_CHECK_STEP, i as FUNNEL_BLOCK, m as Icons, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, p as Colors, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as isClearBlock, v as parsePipfile, y as parsePyprojectToml } from "./posthog-integration-
|
|
17
|
-
import { n as safeReadFile, r as walkProjectFiles, t as IGNORED_DIRS } from "./file-utils-
|
|
18
|
-
import { n as readApiKeyFromEnv } from "./env-api-key-
|
|
2
|
+
import { B as POSTHOG_DOCS_URL, P as Integration, a as getLogFilePath, dt as VERSION, f as getUI, m as LoggingUI, p as setUI, r as debug, s as logToFile, st as WIZARD_USER_AGENT, ut as getSkillsBaseUrl } from "./debug-D1lORUFs.js";
|
|
3
|
+
import { n as analytics } from "./analytics-Ciob-x8a.js";
|
|
4
|
+
import { r as setEntryCommand } from "./telemetry-BfnR7gtf.js";
|
|
5
|
+
import { n as isUsingTypeScript } from "./setup-utils-DLGNk8T6.js";
|
|
6
|
+
import { o as getUiHostFromHost } from "./urls-CilgPbS-.js";
|
|
7
|
+
import { t as HostResolution } from "./host-resolution-BnUJZKId.js";
|
|
8
|
+
import { o as handleApiError } from "./api-T_WHs3qZ.js";
|
|
9
|
+
import "./wizard-session-BoXAZEOf.js";
|
|
10
|
+
import { r as runCleanups } from "./wizard-abort-Cp5Y7f8C.js";
|
|
11
|
+
import { n as isNonInteractiveEnvironment } from "./environment-BEsDCA2q.js";
|
|
12
|
+
import { n as runAgent, o as authenticate } from "./agent-runner-CMfTmM3B.js";
|
|
13
|
+
import { C as SETUP_REPORT_FILE, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-DExARWay.js";
|
|
14
|
+
import { h as AUDIT_REPORT_FILE, i as fetchSkillMenu, m as AUDIT_CHECKS_KEY, p as AUDIT_CHECKS_FILE, t as WIZARD_TOOL_NAMES } from "./wizard-tools-DQTfAzSV.js";
|
|
15
|
+
import { i as SPINNER_MESSAGE, t as FRAMEWORK_REGISTRY } from "./registry-BQIX-25O.js";
|
|
16
|
+
import { _ as gatherFrameworkContext, a as LINE_CHART_BLOCK, b as parseRequirementsTxt, g as detectProjectsWithAgent, h as HEALTH_CHECK_STEP, i as FUNNEL_BLOCK, m as Icons, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, p as Colors, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as isClearBlock, v as parsePipfile, y as parsePyprojectToml } from "./posthog-integration-CWXSng0W.js";
|
|
17
|
+
import { n as safeReadFile, r as walkProjectFiles, t as IGNORED_DIRS } from "./file-utils-Cn3dzlo-.js";
|
|
18
|
+
import { n as readApiKeyFromEnv } from "./env-api-key-DEl3LJBv.js";
|
|
19
19
|
import { satisfies } from "semver";
|
|
20
20
|
import yargs from "yargs";
|
|
21
21
|
import { hideBin } from "yargs/helpers";
|
|
22
22
|
import fs, { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
23
|
-
import path, { join, relative, resolve } from "path";
|
|
23
|
+
import path, { join, relative, resolve, sep } from "path";
|
|
24
24
|
import * as os from "node:os";
|
|
25
25
|
import * as path$1 from "node:path";
|
|
26
26
|
import { spawnSync } from "node:child_process";
|
|
@@ -243,7 +243,7 @@ function runProvision(argv) {
|
|
|
243
243
|
}
|
|
244
244
|
async function provision({ email, region, name, baseUrl, jsonMode }) {
|
|
245
245
|
try {
|
|
246
|
-
const { provisionNewAccount } = await import("./provisioning-
|
|
246
|
+
const { provisionNewAccount } = await import("./provisioning-CgomM26T.js").then((n) => n.n);
|
|
247
247
|
if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
|
|
248
248
|
emitResult(await provisionNewAccount(email, name, region, { baseUrl }), jsonMode);
|
|
249
249
|
process.exit(0);
|
|
@@ -308,22 +308,22 @@ const basicIntegrationCommand = {
|
|
|
308
308
|
setEntryCommand("integrate");
|
|
309
309
|
(async () => {
|
|
310
310
|
if (isHeadless(argv)) {
|
|
311
|
-
const { runHeadlessInstall } = await import("./ci-install-
|
|
311
|
+
const { runHeadlessInstall } = await import("./ci-install-DJN5aT7W.js");
|
|
312
312
|
return runHeadlessInstall(argv);
|
|
313
313
|
}
|
|
314
314
|
if (argv.ci) {
|
|
315
|
-
const { runCIInstall } = await import("./ci-install-
|
|
315
|
+
const { runCIInstall } = await import("./ci-install-DJN5aT7W.js");
|
|
316
316
|
return runCIInstall(argv);
|
|
317
317
|
}
|
|
318
318
|
if (isNonInteractiveEnvironment()) {
|
|
319
|
-
const { failNonInteractive } = await import("./non-interactive-
|
|
319
|
+
const { failNonInteractive } = await import("./non-interactive-Dko5F3vh.js");
|
|
320
320
|
return failNonInteractive();
|
|
321
321
|
}
|
|
322
322
|
if (argv.playground) {
|
|
323
|
-
const { runPlayground } = await import("./playground-
|
|
323
|
+
const { runPlayground } = await import("./playground-UtT6zbjE.js");
|
|
324
324
|
return runPlayground();
|
|
325
325
|
}
|
|
326
|
-
const { runInteractive } = await import("./interactive-
|
|
326
|
+
const { runInteractive } = await import("./interactive-BB2IDSWP.js");
|
|
327
327
|
runInteractive(argv);
|
|
328
328
|
})();
|
|
329
329
|
}
|
|
@@ -3295,7 +3295,102 @@ const POSTHOG_PRESENT_KEY = "postHogPresent";
|
|
|
3295
3295
|
* into the chosen sub-app, not the root.
|
|
3296
3296
|
*/
|
|
3297
3297
|
const SELF_DRIVING_INTEGRATE_PATH_KEY = "selfDrivingIntegratePath";
|
|
3298
|
-
const POSTHOG_PACKAGE_RE = /(^|["'\s
|
|
3298
|
+
const POSTHOG_PACKAGE_RE = /(^|["'\s/=:.@])posthog/im;
|
|
3299
|
+
const POSTHOG_MANIFESTS = [
|
|
3300
|
+
"package.json",
|
|
3301
|
+
"requirements.txt",
|
|
3302
|
+
"pyproject.toml",
|
|
3303
|
+
"Pipfile",
|
|
3304
|
+
"setup.py",
|
|
3305
|
+
"Gemfile",
|
|
3306
|
+
"go.mod",
|
|
3307
|
+
"composer.json",
|
|
3308
|
+
"pubspec.yaml",
|
|
3309
|
+
"Package.swift",
|
|
3310
|
+
"Podfile",
|
|
3311
|
+
"Podfile.lock",
|
|
3312
|
+
"project.yml",
|
|
3313
|
+
"build.gradle",
|
|
3314
|
+
"build.gradle.kts",
|
|
3315
|
+
"gradle/libs.versions.toml",
|
|
3316
|
+
"pom.xml",
|
|
3317
|
+
"mix.exs",
|
|
3318
|
+
"Cargo.toml"
|
|
3319
|
+
];
|
|
3320
|
+
const POSTHOG_DIRS = [
|
|
3321
|
+
".",
|
|
3322
|
+
"app",
|
|
3323
|
+
"frontend",
|
|
3324
|
+
"backend",
|
|
3325
|
+
"ios",
|
|
3326
|
+
"android",
|
|
3327
|
+
"android/app"
|
|
3328
|
+
];
|
|
3329
|
+
const WALK_SKIP_DIRS = new Set([
|
|
3330
|
+
"node_modules",
|
|
3331
|
+
"Pods",
|
|
3332
|
+
"Carthage",
|
|
3333
|
+
"DerivedData",
|
|
3334
|
+
".build",
|
|
3335
|
+
"build",
|
|
3336
|
+
"dist",
|
|
3337
|
+
"out",
|
|
3338
|
+
".next",
|
|
3339
|
+
"coverage",
|
|
3340
|
+
"vendor",
|
|
3341
|
+
".venv",
|
|
3342
|
+
"site-packages",
|
|
3343
|
+
"target",
|
|
3344
|
+
".git"
|
|
3345
|
+
]);
|
|
3346
|
+
const WALK_MAX_DEPTH = 2;
|
|
3347
|
+
/**
|
|
3348
|
+
* The install dir plus every directory within WALK_MAX_DEPTH levels (skipping
|
|
3349
|
+
* heavy/vendored trees and dotdirs), unioned with the explicit POSTHOG_DIRS. A
|
|
3350
|
+
* full shallow walk on top of the named dirs, so a monorepo's nested apps
|
|
3351
|
+
* (e.g. `apps/mobile`) are seen without hardcoding the path.
|
|
3352
|
+
*/
|
|
3353
|
+
function scanDirs(installDir) {
|
|
3354
|
+
const rels = new Set(POSTHOG_DIRS);
|
|
3355
|
+
const walk = (rel, depth) => {
|
|
3356
|
+
rels.add(rel);
|
|
3357
|
+
if (depth >= WALK_MAX_DEPTH) return;
|
|
3358
|
+
let entries;
|
|
3359
|
+
try {
|
|
3360
|
+
entries = readdirSync(join(installDir, rel), { withFileTypes: true });
|
|
3361
|
+
} catch {
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
for (const e of entries) {
|
|
3365
|
+
if (!e.isDirectory()) continue;
|
|
3366
|
+
if (e.name.startsWith(".") || WALK_SKIP_DIRS.has(e.name)) continue;
|
|
3367
|
+
if (e.name.endsWith(".xcodeproj") || e.name.endsWith(".xcworkspace")) continue;
|
|
3368
|
+
walk(rel === "." ? e.name : `${rel}/${e.name}`, depth + 1);
|
|
3369
|
+
}
|
|
3370
|
+
};
|
|
3371
|
+
walk(".", 0);
|
|
3372
|
+
return [...rels];
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* True if a `*.xcodeproj/project.pbxproj` or `*.csproj`/`*.fsproj` in
|
|
3376
|
+
* `dirPath` references PostHog.
|
|
3377
|
+
*/
|
|
3378
|
+
function projectFileHasPostHog(dirPath) {
|
|
3379
|
+
let entries;
|
|
3380
|
+
try {
|
|
3381
|
+
entries = readdirSync(dirPath);
|
|
3382
|
+
} catch {
|
|
3383
|
+
return false;
|
|
3384
|
+
}
|
|
3385
|
+
for (const entry of entries) {
|
|
3386
|
+
const target = entry.endsWith(".xcodeproj") ? join(dirPath, entry, "project.pbxproj") : entry.endsWith(".csproj") || entry.endsWith(".fsproj") ? join(dirPath, entry) : null;
|
|
3387
|
+
if (!target || !existsSync(target)) continue;
|
|
3388
|
+
try {
|
|
3389
|
+
if (POSTHOG_PACKAGE_RE.test(readFileSync(target, "utf8"))) return true;
|
|
3390
|
+
} catch {}
|
|
3391
|
+
}
|
|
3392
|
+
return false;
|
|
3393
|
+
}
|
|
3299
3394
|
/**
|
|
3300
3395
|
* Deterministic, offline check: does the project already have a PostHog SDK?
|
|
3301
3396
|
* Scans the common dependency manifests at the install dir for a `posthog`
|
|
@@ -3304,27 +3399,16 @@ const POSTHOG_PACKAGE_RE = /(^|["'\s/=])posthog/im;
|
|
|
3304
3399
|
* first ("not found") or proceeds straight to setup ("found").
|
|
3305
3400
|
*/
|
|
3306
3401
|
function detectPostHogPresent(installDir) {
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
for (const dir of [
|
|
3318
|
-
".",
|
|
3319
|
-
"app",
|
|
3320
|
-
"frontend",
|
|
3321
|
-
"backend"
|
|
3322
|
-
]) for (const name of manifests) {
|
|
3323
|
-
const path = join(installDir, dir, name);
|
|
3324
|
-
if (!existsSync(path)) continue;
|
|
3325
|
-
try {
|
|
3326
|
-
if (POSTHOG_PACKAGE_RE.test(readFileSync(path, "utf8"))) return true;
|
|
3327
|
-
} catch {}
|
|
3402
|
+
for (const dir of scanDirs(installDir)) {
|
|
3403
|
+
const dirPath = join(installDir, dir);
|
|
3404
|
+
for (const name of POSTHOG_MANIFESTS) {
|
|
3405
|
+
const path = join(dirPath, name);
|
|
3406
|
+
if (!existsSync(path)) continue;
|
|
3407
|
+
try {
|
|
3408
|
+
if (POSTHOG_PACKAGE_RE.test(readFileSync(path, "utf8"))) return true;
|
|
3409
|
+
} catch {}
|
|
3410
|
+
}
|
|
3411
|
+
if (projectFileHasPostHog(dirPath)) return true;
|
|
3328
3412
|
}
|
|
3329
3413
|
return false;
|
|
3330
3414
|
}
|
|
@@ -3421,7 +3505,7 @@ function classify(integration, hasPostHog) {
|
|
|
3421
3505
|
};
|
|
3422
3506
|
return { instrumentable: true };
|
|
3423
3507
|
}
|
|
3424
|
-
/** Map a
|
|
3508
|
+
/** Map a detection report into classified projects (exported for tests). */
|
|
3425
3509
|
function toIntegrationReport(report) {
|
|
3426
3510
|
return {
|
|
3427
3511
|
repoType: report.repoType,
|
|
@@ -3432,6 +3516,7 @@ function toIntegrationReport(report) {
|
|
|
3432
3516
|
framework: p.framework,
|
|
3433
3517
|
integration,
|
|
3434
3518
|
hasPostHog: p.hasPostHog,
|
|
3519
|
+
continuable: p.hasPostHog,
|
|
3435
3520
|
...classify(integration, p.hasPostHog)
|
|
3436
3521
|
};
|
|
3437
3522
|
})
|
|
@@ -3487,10 +3572,18 @@ async function prepSelfDrivingIntegration(session) {
|
|
|
3487
3572
|
*/
|
|
3488
3573
|
/** True once detection found PostHog already present in the project. */
|
|
3489
3574
|
const postHogPresent = (session) => session.frameworkContext[POSTHOG_PRESENT_KEY] === true;
|
|
3490
|
-
/**
|
|
3575
|
+
/**
|
|
3576
|
+
* Absolute dir to integrate into: the picked sub-app, else the repo root.
|
|
3577
|
+
* The picked path originates from LLM output; if it resolves outside the
|
|
3578
|
+
* repo (defense-in-depth on top of the coerce-layer clamp), fall back to
|
|
3579
|
+
* the root rather than run the agent elsewhere.
|
|
3580
|
+
*/
|
|
3491
3581
|
const integrationDir = (session) => {
|
|
3492
3582
|
const rel = session.frameworkContext[SELF_DRIVING_INTEGRATE_PATH_KEY];
|
|
3493
|
-
|
|
3583
|
+
if (typeof rel !== "string" || rel === ".") return session.installDir;
|
|
3584
|
+
const root = resolve(session.installDir);
|
|
3585
|
+
const dir = resolve(root, rel);
|
|
3586
|
+
return dir === root || dir.startsWith(root + sep) ? dir : root;
|
|
3494
3587
|
};
|
|
3495
3588
|
const SELF_DRIVING_PROGRAM = [
|
|
3496
3589
|
{
|
|
@@ -3524,7 +3617,7 @@ const SELF_DRIVING_PROGRAM = [
|
|
|
3524
3617
|
label: "Detecting",
|
|
3525
3618
|
screenId: "self-driving-integration-detect",
|
|
3526
3619
|
show: (session) => session.integrate === true && session.integration == null,
|
|
3527
|
-
isComplete: (session) => session.integration != null
|
|
3620
|
+
isComplete: (session) => session.integration != null || session.integrate === false
|
|
3528
3621
|
},
|
|
3529
3622
|
{
|
|
3530
3623
|
...integrationRunStep,
|
|
@@ -4043,13 +4136,13 @@ const mcpAddCommand = {
|
|
|
4043
4136
|
function runMcpAdd(argv) {
|
|
4044
4137
|
const features = parseFeatures(argv.features);
|
|
4045
4138
|
(async () => {
|
|
4046
|
-
const { readApiKeyFromEnv } = await import("./env-api-key-
|
|
4139
|
+
const { readApiKeyFromEnv } = await import("./env-api-key-DEl3LJBv.js").then((n) => n.t);
|
|
4047
4140
|
const apiKey = argv.apiKey || readApiKeyFromEnv();
|
|
4048
4141
|
const debug = argv.debug;
|
|
4049
4142
|
const localMcp = argv.local;
|
|
4050
4143
|
try {
|
|
4051
|
-
const { startTUI } = await import("./start-tui-
|
|
4052
|
-
const { buildSession } = await import("./wizard-session-
|
|
4144
|
+
const { startTUI } = await import("./start-tui-BjCKghQq.js");
|
|
4145
|
+
const { buildSession } = await import("./wizard-session-C8W_6X0j.js");
|
|
4053
4146
|
const tui = startTUI(VERSION, Program.McpAdd);
|
|
4054
4147
|
tui.store.session = buildSession({
|
|
4055
4148
|
debug,
|
|
@@ -4061,7 +4154,7 @@ function runMcpAdd(argv) {
|
|
|
4061
4154
|
} catch (error) {
|
|
4062
4155
|
if (!isTUIUnavailable(error)) throw error;
|
|
4063
4156
|
setUI(new LoggingUI());
|
|
4064
|
-
const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-
|
|
4157
|
+
const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-CHSzsAG8.js").then((n) => n.r);
|
|
4065
4158
|
await addMCPServerToClientsStep({
|
|
4066
4159
|
local: localMcp,
|
|
4067
4160
|
features,
|
|
@@ -4100,8 +4193,8 @@ function runMcpRemove(argv) {
|
|
|
4100
4193
|
const debug = argv.debug;
|
|
4101
4194
|
const localMcp = argv.local;
|
|
4102
4195
|
try {
|
|
4103
|
-
const { startTUI } = await import("./start-tui-
|
|
4104
|
-
const { buildSession } = await import("./wizard-session-
|
|
4196
|
+
const { startTUI } = await import("./start-tui-BjCKghQq.js");
|
|
4197
|
+
const { buildSession } = await import("./wizard-session-C8W_6X0j.js");
|
|
4105
4198
|
const tui = startTUI(VERSION, Program.McpRemove);
|
|
4106
4199
|
tui.store.session = buildSession({
|
|
4107
4200
|
debug,
|
|
@@ -4110,7 +4203,7 @@ function runMcpRemove(argv) {
|
|
|
4110
4203
|
});
|
|
4111
4204
|
} catch {
|
|
4112
4205
|
setUI(new LoggingUI());
|
|
4113
|
-
const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-
|
|
4206
|
+
const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-CHSzsAG8.js").then((n) => n.r);
|
|
4114
4207
|
await removeMCPServerFromClientsStep({ local: localMcp });
|
|
4115
4208
|
}
|
|
4116
4209
|
})();
|
|
@@ -4132,8 +4225,8 @@ function runMcpTutorial(argv) {
|
|
|
4132
4225
|
const debug = argv.debug;
|
|
4133
4226
|
const localMcp = argv.local;
|
|
4134
4227
|
try {
|
|
4135
|
-
const { startTUI } = await import("./start-tui-
|
|
4136
|
-
const { buildSession } = await import("./wizard-session-
|
|
4228
|
+
const { startTUI } = await import("./start-tui-BjCKghQq.js");
|
|
4229
|
+
const { buildSession } = await import("./wizard-session-C8W_6X0j.js");
|
|
4137
4230
|
const tui = startTUI(VERSION, Program.McpTutorial);
|
|
4138
4231
|
tui.store.session = buildSession({
|
|
4139
4232
|
debug,
|
|
@@ -4213,10 +4306,10 @@ function runWizard(config, options) {
|
|
|
4213
4306
|
(async () => {
|
|
4214
4307
|
try {
|
|
4215
4308
|
const installDir = options.installDir || process.cwd();
|
|
4216
|
-
const { startTUI } = await import("./start-tui-
|
|
4217
|
-
const { buildSession, RunPhase } = await import("./wizard-session-
|
|
4218
|
-
const { TaskStreamPush } = await import("./task-stream-
|
|
4219
|
-
const { PostHogDestination } = await import("./posthog-
|
|
4309
|
+
const { startTUI } = await import("./start-tui-BjCKghQq.js");
|
|
4310
|
+
const { buildSession, RunPhase } = await import("./wizard-session-C8W_6X0j.js");
|
|
4311
|
+
const { TaskStreamPush } = await import("./task-stream-rC4IEUHC.js");
|
|
4312
|
+
const { PostHogDestination } = await import("./posthog-BESrI6-T.js");
|
|
4220
4313
|
tui = startTUI(WIZARD_VERSION, config.id);
|
|
4221
4314
|
const activeTui = tui;
|
|
4222
4315
|
const session = buildSession({
|
|
@@ -4280,7 +4373,7 @@ function runWizard(config, options) {
|
|
|
4280
4373
|
if (shown(step)) await advanceStep(step, activeTui.store, config);
|
|
4281
4374
|
}
|
|
4282
4375
|
else if (skipAgent) {
|
|
4283
|
-
const { getOrAskForProjectData } = await import("./setup-utils-
|
|
4376
|
+
const { getOrAskForProjectData } = await import("./setup-utils-DLGNk8T6.js").then((n) => n.r);
|
|
4284
4377
|
const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
|
|
4285
4378
|
signup: session.signup,
|
|
4286
4379
|
ci: session.ci,
|
|
@@ -4382,11 +4475,11 @@ function runNonInteractive(config, options, mode) {
|
|
|
4382
4475
|
analytics.setTag("build", mode);
|
|
4383
4476
|
(async () => {
|
|
4384
4477
|
const path = await import("path");
|
|
4385
|
-
const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-
|
|
4386
|
-
const { readEnvironment } = await import("./environment-
|
|
4387
|
-
const { readApiKeyFromEnv } = await import("./env-api-key-
|
|
4388
|
-
const { configureLogFileFromEnvironment, logToFile } = await import("./debug-
|
|
4389
|
-
const { wizardAbort, WizardError } = await import("./wizard-abort-
|
|
4478
|
+
const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-C8W_6X0j.js");
|
|
4479
|
+
const { readEnvironment } = await import("./environment-BEsDCA2q.js").then((n) => n.t);
|
|
4480
|
+
const { readApiKeyFromEnv } = await import("./env-api-key-DEl3LJBv.js").then((n) => n.t);
|
|
4481
|
+
const { configureLogFileFromEnvironment, logToFile } = await import("./debug-Cne-cLxd.js");
|
|
4482
|
+
const { wizardAbort, WizardError } = await import("./wizard-abort-DHrEGqaC.js");
|
|
4390
4483
|
configureLogFileFromEnvironment();
|
|
4391
4484
|
const env = readEnvironment();
|
|
4392
4485
|
const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
|
|
@@ -4417,9 +4510,9 @@ function runNonInteractive(config, options, mode) {
|
|
|
4417
4510
|
let store = null;
|
|
4418
4511
|
let taskStream = null;
|
|
4419
4512
|
if (mode === "headless") {
|
|
4420
|
-
const { WizardStore } = await import("./store-
|
|
4421
|
-
const { HeadlessUI } = await import("./headless-ui-
|
|
4422
|
-
const { TaskStreamPush, PostHogDestination } = await import("./task-stream-
|
|
4513
|
+
const { WizardStore } = await import("./store-CDIxU0rr.js").then((n) => n.n);
|
|
4514
|
+
const { HeadlessUI } = await import("./headless-ui-DGunpK-s.js");
|
|
4515
|
+
const { TaskStreamPush, PostHogDestination } = await import("./task-stream-rC4IEUHC.js");
|
|
4423
4516
|
store = new WizardStore(config.id);
|
|
4424
4517
|
store.session = session;
|
|
4425
4518
|
setUI(new HeadlessUI(store));
|
|
@@ -4474,7 +4567,7 @@ function runNonInteractive(config, options, mode) {
|
|
|
4474
4567
|
});
|
|
4475
4568
|
}
|
|
4476
4569
|
}
|
|
4477
|
-
const { runAgent } = await import("./agent-runner-
|
|
4570
|
+
const { runAgent } = await import("./agent-runner-CMfTmM3B.js").then((n) => n.t);
|
|
4478
4571
|
await runAgent(config, session);
|
|
4479
4572
|
await settleStream(RunPhase.Completed);
|
|
4480
4573
|
} catch (error) {
|
|
@@ -5026,7 +5119,8 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
|
|
|
5026
5119
|
children: colOpts.map((opt, rowIdx) => {
|
|
5027
5120
|
const flatIdx = colIdx * rows + rowIdx;
|
|
5028
5121
|
const isFocused = flatIdx === focused;
|
|
5029
|
-
const
|
|
5122
|
+
const base = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
|
|
5123
|
+
const label = opt.indent ? ` ${base}` : base;
|
|
5030
5124
|
return /* @__PURE__ */ jsxs(Box, {
|
|
5031
5125
|
gap: 1,
|
|
5032
5126
|
marginBottom: optionMarginBottom,
|
|
@@ -5034,16 +5128,16 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
|
|
|
5034
5128
|
/* @__PURE__ */ jsx(Text, {
|
|
5035
5129
|
color: isFocused ? Colors.accent : void 0,
|
|
5036
5130
|
dimColor: !isFocused,
|
|
5037
|
-
children: isFocused ? Icons.triangleSmallRight : " "
|
|
5131
|
+
children: isFocused && !opt.header ? Icons.triangleSmallRight : " "
|
|
5038
5132
|
}),
|
|
5039
5133
|
opt.icon && /* @__PURE__ */ jsx(Text, {
|
|
5040
5134
|
color: opt.icon.color,
|
|
5041
5135
|
children: opt.icon.glyph
|
|
5042
5136
|
}),
|
|
5043
5137
|
/* @__PURE__ */ jsx(Text, {
|
|
5044
|
-
color: opt.disabled ? Colors.muted : isFocused ? Colors.accent : void 0,
|
|
5045
|
-
bold: isFocused && !opt.disabled,
|
|
5046
|
-
dimColor: !isFocused || opt.disabled,
|
|
5138
|
+
color: opt.header ? void 0 : opt.disabled ? Colors.muted : isFocused ? Colors.accent : void 0,
|
|
5139
|
+
bold: opt.header || isFocused && !opt.disabled,
|
|
5140
|
+
dimColor: !opt.header && (!isFocused || opt.disabled),
|
|
5047
5141
|
children: label
|
|
5048
5142
|
})
|
|
5049
5143
|
]
|
|
@@ -5401,7 +5495,7 @@ async function runDoctorCI(options) {
|
|
|
5401
5495
|
getUI().intro("Welcome to the PostHog setup wizard");
|
|
5402
5496
|
getUI().log.info("Running posthog-doctor in CI mode");
|
|
5403
5497
|
try {
|
|
5404
|
-
const { getOrAskForProjectData } = await import("./setup-utils-
|
|
5498
|
+
const { getOrAskForProjectData } = await import("./setup-utils-DLGNk8T6.js").then((n) => n.r);
|
|
5405
5499
|
const { host, accessToken, projectId } = await getOrAskForProjectData({
|
|
5406
5500
|
signup: false,
|
|
5407
5501
|
ci: true,
|
|
@@ -5419,7 +5513,7 @@ async function runDoctorCI(options) {
|
|
|
5419
5513
|
for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
|
|
5420
5514
|
process.exit(1);
|
|
5421
5515
|
} catch (error) {
|
|
5422
|
-
const { ApiError } = await import("./api-
|
|
5516
|
+
const { ApiError } = await import("./api-T_WHs3qZ.js").then((n) => n.n);
|
|
5423
5517
|
const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
|
|
5424
5518
|
getUI().log.error(`Doctor failed: ${message}`);
|
|
5425
5519
|
process.exit(1);
|
|
@@ -5501,8 +5595,8 @@ function runSlackConnect(argv) {
|
|
|
5501
5595
|
(async () => {
|
|
5502
5596
|
const debug = argv.debug;
|
|
5503
5597
|
try {
|
|
5504
|
-
const { startTUI } = await import("./start-tui-
|
|
5505
|
-
const { buildSession } = await import("./wizard-session-
|
|
5598
|
+
const { startTUI } = await import("./start-tui-BjCKghQq.js");
|
|
5599
|
+
const { buildSession } = await import("./wizard-session-C8W_6X0j.js");
|
|
5506
5600
|
const tui = startTUI(VERSION, Program.SlackConnect);
|
|
5507
5601
|
tui.store.session = buildSession({
|
|
5508
5602
|
debug,
|