@posthog/wizard 2.64.1 → 2.66.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 +7 -2
- package/dist/{add-mcp-server-to-clients-BUqb93_I.js → add-mcp-server-to-clients-DWDEuDUR.js} +210 -46
- package/dist/add-mcp-server-to-clients-DWDEuDUR.js.map +1 -0
- package/dist/{agent-interface-NurnwA01.js → agent-interface-D3MNaDp3.js} +405 -47
- package/dist/agent-interface-D3MNaDp3.js.map +1 -0
- package/dist/{agent-prompt-loader-CbowPuWI.js → agent-prompt-loader-D8Bajo-L.js} +10 -5
- package/dist/agent-prompt-loader-D8Bajo-L.js.map +1 -0
- package/dist/{agent-runner-DisJWxJa.js → agent-runner-DEeMqJxy.js} +111 -53
- package/dist/agent-runner-DEeMqJxy.js.map +1 -0
- package/dist/{analytics-D3vKvr3l.js → analytics-D8JNuPAT.js} +2 -2
- package/dist/{analytics-D3vKvr3l.js.map → analytics-D8JNuPAT.js.map} +1 -1
- package/dist/analytics-DN3Kzf7H.js +2 -0
- package/dist/{api-Bhe8jpUR.js → api-CemGgyBv.js} +3 -3
- package/dist/{api-Bhe8jpUR.js.map → api-CemGgyBv.js.map} +1 -1
- package/dist/bin.js +245 -93
- package/dist/bin.js.map +1 -1
- package/dist/{bounded-fs-BeWc675I.js → bounded-fs-C6OjqZst.js} +3 -3
- package/dist/{bounded-fs-BeWc675I.js.map → bounded-fs-C6OjqZst.js.map} +1 -1
- package/dist/{ci-install-BpNzfghY.js → ci-install-DIB2n4Ju.js} +4 -4
- package/dist/{ci-install-BpNzfghY.js.map → ci-install-DIB2n4Ju.js.map} +1 -1
- package/dist/{debug-hy6aWAyk.js → debug-BKqv-yfT.js} +14 -9
- package/dist/debug-BKqv-yfT.js.map +1 -0
- package/dist/{debug-DGfRPr2A.js → debug-H-GTKvvU.js} +1 -1
- package/dist/{defaults-mrAre0xM.js → defaults-BHhXrX9F.js} +1 -1
- package/dist/{defaults-mrAre0xM.js.map → defaults-BHhXrX9F.js.map} +1 -1
- package/dist/{env-api-key-CMPe-WJp.js → env-api-key-CzHDwfV2.js} +2 -2
- package/dist/{env-api-key-CMPe-WJp.js.map → env-api-key-CzHDwfV2.js.map} +1 -1
- package/dist/{environment-Ktu0Ktjl.js → environment-Cl-fIQku.js} +4 -4
- package/dist/{environment-Ktu0Ktjl.js.map → environment-Cl-fIQku.js.map} +1 -1
- package/dist/{file-watcher-CqJ1dgjq.js → file-watcher-Cr-4tA-e.js} +2 -2
- package/dist/{file-watcher-CqJ1dgjq.js.map → file-watcher-Cr-4tA-e.js.map} +1 -1
- package/dist/{headless-ui-zZLgs4QL.js → headless-ui-DT3Db1bk.js} +2 -2
- package/dist/{headless-ui-zZLgs4QL.js.map → headless-ui-DT3Db1bk.js.map} +1 -1
- package/dist/{interactive-BUw1SEOz.js → interactive-D3LdFxZG.js} +2 -2
- package/dist/{interactive-BUw1SEOz.js.map → interactive-D3LdFxZG.js.map} +1 -1
- package/dist/local-dev-BN6NKQ0B.js +135 -0
- package/dist/local-dev-BN6NKQ0B.js.map +1 -0
- package/dist/{mcp-C7NMhhWa.js → mcp-BjckYQmP.js} +2 -2
- package/dist/{mcp-C7NMhhWa.js.map → mcp-BjckYQmP.js.map} +1 -1
- package/dist/mcp-install-smoke-test.ts +477 -0
- package/dist/{mcp-prompt-streaming-CKyqqB10.js → mcp-prompt-streaming--sRErHKv.js} +20 -4
- package/dist/mcp-prompt-streaming--sRErHKv.js.map +1 -0
- package/dist/{non-interactive-73xGtug6.js → non-interactive-CNoYoEZp.js} +2 -2
- package/dist/{non-interactive-73xGtug6.js.map → non-interactive-CNoYoEZp.js.map} +1 -1
- package/dist/{orchestrator-tools-DcMJzfDH.js → orchestrator-tools-8bGyDDhn.js} +3 -3
- package/dist/{orchestrator-tools-DcMJzfDH.js.map → orchestrator-tools-8bGyDDhn.js.map} +1 -1
- package/dist/{package-json-CDs1ZVKu.js → package-json-D0DCWpe5.js} +1 -1
- package/dist/{package-json-CDs1ZVKu.js.map → package-json-D0DCWpe5.js.map} +1 -1
- package/dist/{package-manager-KweKX3u_.js → package-manager-BQDHm7ER.js} +58 -3
- package/dist/package-manager-BQDHm7ER.js.map +1 -0
- package/dist/{pi-B4p4gpr0.js → pi-W-4RUPFY.js} +11 -11
- package/dist/{pi-B4p4gpr0.js.map → pi-W-4RUPFY.js.map} +1 -1
- package/dist/{playground-AEkWcB1E.js → playground-uHHgZChe.js} +7 -7
- package/dist/{playground-AEkWcB1E.js.map → playground-uHHgZChe.js.map} +1 -1
- package/dist/{posthog-4PljLcIz.js → posthog-ChzdlmCs.js} +2 -2
- package/dist/{posthog-4PljLcIz.js.map → posthog-ChzdlmCs.js.map} +1 -1
- package/dist/{posthog-integration-DKlEg8-Q.js → posthog-integration-LxEBGkoe.js} +680 -49
- package/dist/posthog-integration-LxEBGkoe.js.map +1 -0
- package/dist/{provisioning-QsG7HDCm.js → provisioning-BHTl91OZ.js} +3 -3
- package/dist/{provisioning-QsG7HDCm.js.map → provisioning-BHTl91OZ.js.map} +1 -1
- package/dist/{queue-tools-Jlz9dV_B.js → queue-tools-DABh1Ssc.js} +33 -7
- package/dist/queue-tools-DABh1Ssc.js.map +1 -0
- package/dist/{registry-PG_nV5Bf.js → registry-CxNpCNAj.js} +309 -6
- package/dist/registry-CxNpCNAj.js.map +1 -0
- package/dist/{security-BKUJk-Gg.js → security-B4jU_-7q.js} +5 -5
- package/dist/{security-BKUJk-Gg.js.map → security-B4jU_-7q.js.map} +1 -1
- package/dist/{setup-utils-DNpldqge.js → setup-utils-aWZEdfwh.js} +18 -13
- package/dist/setup-utils-aWZEdfwh.js.map +1 -0
- package/dist/smoke-test-ci.sh +12 -1
- package/dist/{start-tui-D4B0-2nr.js → start-tui-BQH4mQIZ.js} +138 -27
- package/dist/start-tui-BQH4mQIZ.js.map +1 -0
- package/dist/{steps-Dy0AXdGo.js → steps-5HAqjUvn.js} +7 -7
- package/dist/{steps-Dy0AXdGo.js.map → steps-5HAqjUvn.js.map} +1 -1
- package/dist/{store-DouLLelV.js → store-P_7zUfbj.js} +22 -7
- package/dist/store-P_7zUfbj.js.map +1 -0
- package/dist/{subagent-B4yVs5A2.js → subagent-uGPDXPfS.js} +2 -2
- package/dist/{subagent-B4yVs5A2.js.map → subagent-uGPDXPfS.js.map} +1 -1
- package/dist/{task-DRKYEbKM.js → task-oh8OAnvR.js} +29 -18
- package/dist/task-oh8OAnvR.js.map +1 -0
- package/dist/{task-stream-BGUP7yA-.js → task-stream-jUARpyB5.js} +5 -5
- package/dist/{task-stream-BGUP7yA-.js.map → task-stream-jUARpyB5.js.map} +1 -1
- package/dist/{tasks-B2GzXsZ0.js → tasks-2mu3lAvm.js} +2 -2
- package/dist/{tasks-B2GzXsZ0.js.map → tasks-2mu3lAvm.js.map} +1 -1
- package/dist/{telemetry-6zaFfCcy.js → telemetry-BllPiBKw.js} +3 -3
- package/dist/{telemetry-6zaFfCcy.js.map → telemetry-BllPiBKw.js.map} +1 -1
- package/dist/{terminal-Bu3eHFn0.js → terminal-aWQrDy8s.js} +99 -98
- package/dist/terminal-aWQrDy8s.js.map +1 -0
- package/dist/{tools-DQO5tKJg.js → tools-u8TaJFqa.js} +54 -47
- package/dist/tools-u8TaJFqa.js.map +1 -0
- package/dist/{wizard-abort-D6jJh8u5.js → wizard-abort-D9jiMkZA.js} +4 -4
- package/dist/{wizard-abort-D6jJh8u5.js.map → wizard-abort-D9jiMkZA.js.map} +1 -1
- package/dist/{wizard-abort-PgTdRS1l.js → wizard-abort-pFFH7dcx.js} +1 -1
- package/dist/{wizard-session-DZUDtS5d.js → wizard-session-Cw7-_OcZ.js} +1 -1
- package/dist/{wizard-session-BUhlJ1b8.js → wizard-session-DTyLMPsX.js} +17 -3
- package/dist/wizard-session-DTyLMPsX.js.map +1 -0
- package/dist/{yara-hooks-DjReXmTQ.js → yara-hooks-BXDBIOBq.js} +4 -4
- package/dist/{yara-hooks-DjReXmTQ.js.map → yara-hooks-BXDBIOBq.js.map} +1 -1
- package/dist/yara-hooks-i2jOabhI.js +2 -0
- package/package.json +2 -1
- package/dist/add-mcp-server-to-clients-BUqb93_I.js.map +0 -1
- package/dist/agent-interface-NurnwA01.js.map +0 -1
- package/dist/agent-prompt-loader-CbowPuWI.js.map +0 -1
- package/dist/agent-runner-DisJWxJa.js.map +0 -1
- package/dist/analytics-hNca7GLq.js +0 -2
- package/dist/debug-hy6aWAyk.js.map +0 -1
- package/dist/mcp-prompt-streaming-CKyqqB10.js.map +0 -1
- package/dist/package-manager-KweKX3u_.js.map +0 -1
- package/dist/posthog-integration-DKlEg8-Q.js.map +0 -1
- package/dist/queue-tools-Jlz9dV_B.js.map +0 -1
- package/dist/registry-PG_nV5Bf.js.map +0 -1
- package/dist/setup-utils-DNpldqge.js.map +0 -1
- package/dist/start-tui-D4B0-2nr.js.map +0 -1
- package/dist/store-DouLLelV.js.map +0 -1
- package/dist/task-DRKYEbKM.js.map +0 -1
- package/dist/terminal-Bu3eHFn0.js.map +0 -1
- package/dist/tools-DQO5tKJg.js.map +0 -1
- package/dist/wizard-session-BUhlJ1b8.js.map +0 -1
- package/dist/yara-hooks-CN_uFGCu.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-stream-BGUP7yA-.js","names":[],"sources":["../src/lib/task-stream/event-plan-watcher.ts","../src/lib/task-stream/task-stream-push.ts"],"sourcesContent":["import type { PlannedEvent, WizardStore } from '@ui/tui/store';\nimport {\n startFileWatcher,\n type FileWatcherHandle,\n type FileWatcherOptions,\n} from '@lib/file-watcher';\n\nconst MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;\nconst MAX_EVENT_COUNT = 50;\nconst MAX_EVENT_NAME_LENGTH = 400;\nconst MAX_EVENT_DESCRIPTION_LENGTH = 4000;\n\nfunction firstString(...values: unknown[]): string | null {\n const value = values.find((candidate) => typeof candidate === 'string');\n return typeof value === 'string' ? value : null;\n}\n\nexport function normalizeEventPlan(parsed: unknown): PlannedEvent[] | null {\n if (!Array.isArray(parsed)) return null;\n\n const events: PlannedEvent[] = [];\n for (const value of parsed) {\n if (events.length >= MAX_EVENT_COUNT) break;\n\n const entry =\n value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : {};\n const name = firstString(entry.event_name, entry.name, entry.event);\n if (!name || !name.trim() || name.length > MAX_EVENT_NAME_LENGTH) continue;\n\n const description =\n firstString(entry.event_description, entry.description) ?? '';\n events.push({\n name,\n description: description.slice(0, MAX_EVENT_DESCRIPTION_LENGTH),\n });\n }\n\n return events;\n}\n\nexport class EventPlanWatcher {\n private handle: FileWatcherHandle | null = null;\n private captured = false;\n\n constructor(\n private readonly store: WizardStore,\n private readonly path: string,\n private readonly options: FileWatcherOptions = {},\n ) {}\n\n start(): void {\n if (this.handle || this.captured) return;\n\n this.handle = startFileWatcher(\n this.path,\n (parsed) => {\n const events = normalizeEventPlan(parsed);\n if (!events || events.length === 0) return;\n\n this.captured = true;\n this.store.setEventPlan(events);\n this.stop();\n },\n {\n ignoreInitialFile: true,\n maxFileSizeBytes: MAX_EVENT_PLAN_FILE_BYTES,\n ...this.options,\n },\n );\n }\n\n refresh(): void {\n if (this.captured) return;\n this.handle?.refresh();\n }\n\n stop(): void {\n this.handle?.stop();\n this.handle = null;\n }\n}\n","/**\n * Task-stream push — subscribes to WizardStore, builds payloads,\n * and fans out async to all registered destinations.\n *\n * Behaviour:\n * - `attach(store)` subscribe to store changes\n * - task updates debounced 250ms (trailing edge)\n * - phase transitions flush immediately, bypass debounce\n * - RunPhase.Idle skipped (no push)\n * - enabled === false destination delivery is disabled\n * - shutdown(timeoutMs) cancel pending, flush terminal phase\n * with timeout, never throw\n *\n * Concurrency: only one fan-out at a time. Emits during an in-flight\n * push are coalesced — at most one follow-up push fires with the\n * latest state once the current one settles.\n */\n\nimport type { WizardStore, TaskItem } from '@ui/tui/store';\nimport { TaskStatus } from '@ui/wizard-ui';\nimport {\n RunPhase,\n OutroKind,\n type OutroData,\n type PendingQuestion,\n} from '@lib/wizard-session';\nimport {\n type TaskStreamDestination,\n type TaskStreamUpdate,\n type StreamTask,\n type TaskStreamError,\n type StreamPendingInput,\n StreamTaskStatus,\n StreamEvent,\n} from './types';\nimport { EventPlanWatcher } from './event-plan-watcher';\nimport { logToFile } from '@utils/debug';\n\n/** Trailing-edge debounce window for non-phase-change emits. */\nconst DEBOUNCE_MS = 250;\n/** Default shutdown timeout for the final terminal flush. */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2000;\n\nconst STATUS_MAP: Record<TaskStatus, StreamTaskStatus> = {\n [TaskStatus.Pending]: StreamTaskStatus.Pending,\n [TaskStatus.InProgress]: StreamTaskStatus.InProgress,\n [TaskStatus.Completed]: StreamTaskStatus.Completed,\n // The stream has no skipped state; skipped is terminal, so report it resolved.\n [TaskStatus.Skipped]: StreamTaskStatus.Completed,\n};\n\nfunction buildTasks(items: TaskItem[]): StreamTask[] {\n return items.map((item, i) => ({\n id: String(i),\n title: item.label,\n status: STATUS_MAP[item.status] ?? StreamTaskStatus.Pending,\n }));\n}\n\n/** Drop \".SSSZ\" → \"Z\" so session_id segments stay routing-safe. */\nfunction secondPrecisionIso(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * `workflow_id` and `skill_id` end up unescaped in Redis pub/sub\n * channel names, so the backend rejects anything outside\n * `^[A-Za-z0-9_.-]{1,255}$` with a 400. All current values already\n * comply; this is defence in depth in case a future caller passes\n * something with `:`, spaces, or other separators.\n */\nfunction sanitizeChannelId(value: string): string {\n return value.replace(/[^A-Za-z0-9_.-]/g, '-').slice(0, 255);\n}\n\nfunction buildError(\n phase: RunPhase,\n outroData: OutroData | null,\n): TaskStreamError | undefined {\n if (phase !== RunPhase.Error) return undefined;\n if (outroData?.kind === OutroKind.Error) {\n const message = outroData.message ?? outroData.body ?? 'Wizard run failed';\n return { type: 'wizard_error', message };\n }\n return { type: 'wizard_error', message: 'Wizard run failed' };\n}\n\n/**\n * Sensitive asks (secrets, API keys) publish only the fact that input is\n * required — never the prompt text. The session row is team-visible and\n * outlives the prompt.\n */\nfunction buildPendingInput(\n question: PendingQuestion | null | undefined,\n): StreamPendingInput | undefined {\n if (!question) return undefined;\n const sensitive = question.questions.some((q) => q.sensitive === true);\n return {\n id: question.id,\n asked_at: question.askedAt ?? new Date().toISOString(),\n question_count: question.questions.length,\n sensitive,\n prompts: sensitive ? undefined : question.questions.map((q) => q.prompt),\n };\n}\n\nexport interface TaskStreamPushOptions {\n store: WizardStore;\n programId: string;\n destinations: TaskStreamDestination[];\n /** Optional absolute event-plan path to load into the store once. */\n eventPlanPath?: string;\n /** When false, destination subscription/delivery remains disabled. */\n enabled?: boolean;\n}\n\nexport class TaskStreamPush {\n private readonly store: WizardStore;\n private readonly destinations: TaskStreamDestination[];\n private readonly startedAt: string;\n private readonly programId: string;\n private readonly sessionId: string;\n private readonly eventPlanWatcher: EventPlanWatcher | null;\n\n private enabled: boolean;\n private created = false;\n private lastPushedPhase: RunPhase | null = null;\n private lastPushedQuestionId: string | null = null;\n\n private unsubscribe: (() => void) | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private inFlight: Promise<void> | null = null;\n private needsAnotherPush = false;\n private shuttingDown = false;\n\n constructor(opts: TaskStreamPushOptions) {\n this.store = opts.store;\n this.programId = sanitizeChannelId(opts.programId);\n this.destinations = opts.destinations;\n this.enabled = opts.enabled ?? true;\n const startedAt = new Date();\n this.eventPlanWatcher = opts.eventPlanPath\n ? new EventPlanWatcher(this.store, opts.eventPlanPath)\n : null;\n this.startedAt = secondPrecisionIso(startedAt);\n // skillId may not be set yet — fall back to programId so the\n // session_id is stable for the whole run regardless of when the\n // program metadata is populated.\n const skillId = sanitizeChannelId(\n this.store.session.skillId ?? this.programId,\n );\n this.sessionId = `${this.programId}-${skillId}-${this.startedAt}`;\n }\n\n /**\n * Load the event plan and subscribe to store changes. Destination delivery\n * remains disabled when `enabled === false`, but the plan still populates the\n * store for local and headless consumers.\n */\n attach(store?: WizardStore): void {\n this.eventPlanWatcher?.start();\n if (!this.enabled) return;\n if (this.unsubscribe) return;\n const target = store ?? this.store;\n this.unsubscribe = target.subscribe(() => this.onStoreChange());\n }\n\n /** Stop subscribing. Does not flush. */\n detach(): void {\n this.eventPlanWatcher?.stop();\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n /**\n * Cancel pending debounce, flush one final push if the current\n * phase is terminal, and resolve. Never throws. Bounded by\n * `timeoutMs` — if a destination hangs, this returns anyway.\n */\n async shutdown(\n timeoutMs: number = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n ): Promise<void> {\n this.shuttingDown = true;\n this.eventPlanWatcher?.refresh();\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.detach();\n if (!this.enabled) return;\n\n const phase = this.store.session.runPhase;\n const isTerminal = phase === RunPhase.Completed || phase === RunPhase.Error;\n if (!isTerminal) return;\n\n const flush = this.flush();\n if (timeoutMs <= 0) return;\n await Promise.race([\n flush,\n new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),\n ]);\n }\n\n /**\n * Imperative push — fires immediately regardless of phase. Kept as\n * the building block for both subscription-driven and direct calls.\n */\n async push(): Promise<void> {\n await this.flush();\n }\n\n // ── Internal ────────────────────────────────────────────────────\n\n private onStoreChange(): void {\n if (!this.enabled || this.shuttingDown) return;\n const phase = this.store.session.runPhase;\n if (phase === RunPhase.Idle) return;\n\n // A push is already in flight — coalesce. The in-flight push's\n // settle handler will trigger one follow-up with the latest state.\n if (this.inFlight) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.needsAnotherPush = true;\n return;\n }\n\n const phaseChanged = phase !== this.lastPushedPhase;\n const questionId = this.store.session.pendingQuestion?.id ?? null;\n const questionChanged = questionId !== this.lastPushedQuestionId;\n if (phaseChanged || questionChanged) {\n // Phase transitions bypass the debounce: the web app needs to\n // see Running → Completed as soon as it lands. A wizard_ask\n // opening or closing is the same shape — the whole point of\n // publishing it is that the user is looking at the web app, so\n // a 250ms-stale \"needs your input\" defeats the purpose.\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n void this.flush();\n return;\n }\n\n // Task updates can arrive faster than we want to push. Debounce\n // them — the last update in a burst wins.\n if (this.debounceTimer) return;\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n void this.flush();\n }, DEBOUNCE_MS);\n }\n\n /**\n * Fan out the current state to every destination. Serialized — if\n * a flush is already running, mark \"needs another\" and let the\n * in-flight one schedule the follow-up when it settles.\n */\n private flush(): Promise<void> {\n if (this.inFlight) {\n this.needsAnotherPush = true;\n return this.inFlight;\n }\n\n const run = async (): Promise<void> => {\n try {\n await this.sendOnce();\n } finally {\n this.inFlight = null;\n if (this.needsAnotherPush) {\n this.needsAnotherPush = false;\n // Re-enter to push the latest snapshot.\n await this.flush();\n }\n }\n };\n\n this.inFlight = run();\n return this.inFlight;\n }\n\n private async sendOnce(): Promise<void> {\n const { session, tasks, eventPlan, handoffText } = this.store;\n const skillId = sanitizeChannelId(session.skillId ?? this.programId);\n const phase = session.runPhase;\n\n const payload: TaskStreamUpdate = {\n session_id: this.sessionId,\n workflow_id: this.programId,\n skill_id: skillId,\n started_at: this.startedAt,\n run_phase: phase,\n tasks: buildTasks(tasks),\n event_plan: eventPlan.length > 0 ? { events: eventPlan } : undefined,\n error: buildError(phase, session.outroData),\n pending_input: buildPendingInput(session.pendingQuestion),\n // Included on every push once captured; the backend keeps it sticky, so\n // pushes that raced the capture cannot un-set it.\n handoff_text: handoffText ?? undefined,\n timestamp: new Date().toISOString(),\n };\n logToFile(\n `[task-stream-push] push phase=${phase} handoff_text=${\n handoffText === null ? 'absent' : `${handoffText.length} chars`\n }`,\n );\n\n let event: StreamEvent;\n if (!this.created) {\n this.created = true;\n event = StreamEvent.Create;\n } else if (phase === RunPhase.Completed) {\n event = StreamEvent.Complete;\n } else if (phase === RunPhase.Error) {\n event = StreamEvent.Error;\n } else {\n event = StreamEvent.Update;\n }\n\n this.lastPushedPhase = phase;\n this.lastPushedQuestionId = payload.pending_input?.id ?? null;\n\n await Promise.all(\n this.destinations.map((d) =>\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n d.send(event, payload).catch(() => {}),\n ),\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"task-stream-jUARpyB5.js","names":[],"sources":["../src/lib/task-stream/event-plan-watcher.ts","../src/lib/task-stream/task-stream-push.ts"],"sourcesContent":["import type { PlannedEvent, WizardStore } from '@ui/tui/store';\nimport {\n startFileWatcher,\n type FileWatcherHandle,\n type FileWatcherOptions,\n} from '@lib/file-watcher';\n\nconst MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;\nconst MAX_EVENT_COUNT = 50;\nconst MAX_EVENT_NAME_LENGTH = 400;\nconst MAX_EVENT_DESCRIPTION_LENGTH = 4000;\n\nfunction firstString(...values: unknown[]): string | null {\n const value = values.find((candidate) => typeof candidate === 'string');\n return typeof value === 'string' ? value : null;\n}\n\nexport function normalizeEventPlan(parsed: unknown): PlannedEvent[] | null {\n if (!Array.isArray(parsed)) return null;\n\n const events: PlannedEvent[] = [];\n for (const value of parsed) {\n if (events.length >= MAX_EVENT_COUNT) break;\n\n const entry =\n value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : {};\n const name = firstString(entry.event_name, entry.name, entry.event);\n if (!name || !name.trim() || name.length > MAX_EVENT_NAME_LENGTH) continue;\n\n const description =\n firstString(entry.event_description, entry.description) ?? '';\n events.push({\n name,\n description: description.slice(0, MAX_EVENT_DESCRIPTION_LENGTH),\n });\n }\n\n return events;\n}\n\nexport class EventPlanWatcher {\n private handle: FileWatcherHandle | null = null;\n private captured = false;\n\n constructor(\n private readonly store: WizardStore,\n private readonly path: string,\n private readonly options: FileWatcherOptions = {},\n ) {}\n\n start(): void {\n if (this.handle || this.captured) return;\n\n this.handle = startFileWatcher(\n this.path,\n (parsed) => {\n const events = normalizeEventPlan(parsed);\n if (!events || events.length === 0) return;\n\n this.captured = true;\n this.store.setEventPlan(events);\n this.stop();\n },\n {\n ignoreInitialFile: true,\n maxFileSizeBytes: MAX_EVENT_PLAN_FILE_BYTES,\n ...this.options,\n },\n );\n }\n\n refresh(): void {\n if (this.captured) return;\n this.handle?.refresh();\n }\n\n stop(): void {\n this.handle?.stop();\n this.handle = null;\n }\n}\n","/**\n * Task-stream push — subscribes to WizardStore, builds payloads,\n * and fans out async to all registered destinations.\n *\n * Behaviour:\n * - `attach(store)` subscribe to store changes\n * - task updates debounced 250ms (trailing edge)\n * - phase transitions flush immediately, bypass debounce\n * - RunPhase.Idle skipped (no push)\n * - enabled === false destination delivery is disabled\n * - shutdown(timeoutMs) cancel pending, flush terminal phase\n * with timeout, never throw\n *\n * Concurrency: only one fan-out at a time. Emits during an in-flight\n * push are coalesced — at most one follow-up push fires with the\n * latest state once the current one settles.\n */\n\nimport type { WizardStore, TaskItem } from '@ui/tui/store';\nimport { TaskStatus } from '@ui/wizard-ui';\nimport {\n RunPhase,\n OutroKind,\n type OutroData,\n type PendingQuestion,\n} from '@lib/wizard-session';\nimport {\n type TaskStreamDestination,\n type TaskStreamUpdate,\n type StreamTask,\n type TaskStreamError,\n type StreamPendingInput,\n StreamTaskStatus,\n StreamEvent,\n} from './types';\nimport { EventPlanWatcher } from './event-plan-watcher';\nimport { logToFile } from '@utils/debug';\n\n/** Trailing-edge debounce window for non-phase-change emits. */\nconst DEBOUNCE_MS = 250;\n/** Default shutdown timeout for the final terminal flush. */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2000;\n\nconst STATUS_MAP: Record<TaskStatus, StreamTaskStatus> = {\n [TaskStatus.Pending]: StreamTaskStatus.Pending,\n [TaskStatus.InProgress]: StreamTaskStatus.InProgress,\n [TaskStatus.Completed]: StreamTaskStatus.Completed,\n // The stream has no skipped state; skipped is terminal, so report it resolved.\n [TaskStatus.Skipped]: StreamTaskStatus.Completed,\n};\n\nfunction buildTasks(items: TaskItem[]): StreamTask[] {\n return items.map((item, i) => ({\n id: String(i),\n title: item.label,\n status: STATUS_MAP[item.status] ?? StreamTaskStatus.Pending,\n }));\n}\n\n/** Drop \".SSSZ\" → \"Z\" so session_id segments stay routing-safe. */\nfunction secondPrecisionIso(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * `workflow_id` and `skill_id` end up unescaped in Redis pub/sub\n * channel names, so the backend rejects anything outside\n * `^[A-Za-z0-9_.-]{1,255}$` with a 400. All current values already\n * comply; this is defence in depth in case a future caller passes\n * something with `:`, spaces, or other separators.\n */\nfunction sanitizeChannelId(value: string): string {\n return value.replace(/[^A-Za-z0-9_.-]/g, '-').slice(0, 255);\n}\n\nfunction buildError(\n phase: RunPhase,\n outroData: OutroData | null,\n): TaskStreamError | undefined {\n if (phase !== RunPhase.Error) return undefined;\n if (outroData?.kind === OutroKind.Error) {\n const message = outroData.message ?? outroData.body ?? 'Wizard run failed';\n return { type: 'wizard_error', message };\n }\n return { type: 'wizard_error', message: 'Wizard run failed' };\n}\n\n/**\n * Sensitive asks (secrets, API keys) publish only the fact that input is\n * required — never the prompt text. The session row is team-visible and\n * outlives the prompt.\n */\nfunction buildPendingInput(\n question: PendingQuestion | null | undefined,\n): StreamPendingInput | undefined {\n if (!question) return undefined;\n const sensitive = question.questions.some((q) => q.sensitive === true);\n return {\n id: question.id,\n asked_at: question.askedAt ?? new Date().toISOString(),\n question_count: question.questions.length,\n sensitive,\n prompts: sensitive ? undefined : question.questions.map((q) => q.prompt),\n };\n}\n\nexport interface TaskStreamPushOptions {\n store: WizardStore;\n programId: string;\n destinations: TaskStreamDestination[];\n /** Optional absolute event-plan path to load into the store once. */\n eventPlanPath?: string;\n /** When false, destination subscription/delivery remains disabled. */\n enabled?: boolean;\n}\n\nexport class TaskStreamPush {\n private readonly store: WizardStore;\n private readonly destinations: TaskStreamDestination[];\n private readonly startedAt: string;\n private readonly programId: string;\n private readonly sessionId: string;\n private readonly eventPlanWatcher: EventPlanWatcher | null;\n\n private enabled: boolean;\n private created = false;\n private lastPushedPhase: RunPhase | null = null;\n private lastPushedQuestionId: string | null = null;\n\n private unsubscribe: (() => void) | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private inFlight: Promise<void> | null = null;\n private needsAnotherPush = false;\n private shuttingDown = false;\n\n constructor(opts: TaskStreamPushOptions) {\n this.store = opts.store;\n this.programId = sanitizeChannelId(opts.programId);\n this.destinations = opts.destinations;\n this.enabled = opts.enabled ?? true;\n const startedAt = new Date();\n this.eventPlanWatcher = opts.eventPlanPath\n ? new EventPlanWatcher(this.store, opts.eventPlanPath)\n : null;\n this.startedAt = secondPrecisionIso(startedAt);\n // skillId may not be set yet — fall back to programId so the\n // session_id is stable for the whole run regardless of when the\n // program metadata is populated.\n const skillId = sanitizeChannelId(\n this.store.session.skillId ?? this.programId,\n );\n this.sessionId = `${this.programId}-${skillId}-${this.startedAt}`;\n }\n\n /**\n * Load the event plan and subscribe to store changes. Destination delivery\n * remains disabled when `enabled === false`, but the plan still populates the\n * store for local and headless consumers.\n */\n attach(store?: WizardStore): void {\n this.eventPlanWatcher?.start();\n if (!this.enabled) return;\n if (this.unsubscribe) return;\n const target = store ?? this.store;\n this.unsubscribe = target.subscribe(() => this.onStoreChange());\n }\n\n /** Stop subscribing. Does not flush. */\n detach(): void {\n this.eventPlanWatcher?.stop();\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n /**\n * Cancel pending debounce, flush one final push if the current\n * phase is terminal, and resolve. Never throws. Bounded by\n * `timeoutMs` — if a destination hangs, this returns anyway.\n */\n async shutdown(\n timeoutMs: number = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n ): Promise<void> {\n this.shuttingDown = true;\n this.eventPlanWatcher?.refresh();\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.detach();\n if (!this.enabled) return;\n\n const phase = this.store.session.runPhase;\n const isTerminal = phase === RunPhase.Completed || phase === RunPhase.Error;\n if (!isTerminal) return;\n\n const flush = this.flush();\n if (timeoutMs <= 0) return;\n await Promise.race([\n flush,\n new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),\n ]);\n }\n\n /**\n * Imperative push — fires immediately regardless of phase. Kept as\n * the building block for both subscription-driven and direct calls.\n */\n async push(): Promise<void> {\n await this.flush();\n }\n\n // ── Internal ────────────────────────────────────────────────────\n\n private onStoreChange(): void {\n if (!this.enabled || this.shuttingDown) return;\n const phase = this.store.session.runPhase;\n if (phase === RunPhase.Idle) return;\n\n // A push is already in flight — coalesce. The in-flight push's\n // settle handler will trigger one follow-up with the latest state.\n if (this.inFlight) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.needsAnotherPush = true;\n return;\n }\n\n const phaseChanged = phase !== this.lastPushedPhase;\n const questionId = this.store.session.pendingQuestion?.id ?? null;\n const questionChanged = questionId !== this.lastPushedQuestionId;\n if (phaseChanged || questionChanged) {\n // Phase transitions bypass the debounce: the web app needs to\n // see Running → Completed as soon as it lands. A wizard_ask\n // opening or closing is the same shape — the whole point of\n // publishing it is that the user is looking at the web app, so\n // a 250ms-stale \"needs your input\" defeats the purpose.\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n void this.flush();\n return;\n }\n\n // Task updates can arrive faster than we want to push. Debounce\n // them — the last update in a burst wins.\n if (this.debounceTimer) return;\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n void this.flush();\n }, DEBOUNCE_MS);\n }\n\n /**\n * Fan out the current state to every destination. Serialized — if\n * a flush is already running, mark \"needs another\" and let the\n * in-flight one schedule the follow-up when it settles.\n */\n private flush(): Promise<void> {\n if (this.inFlight) {\n this.needsAnotherPush = true;\n return this.inFlight;\n }\n\n const run = async (): Promise<void> => {\n try {\n await this.sendOnce();\n } finally {\n this.inFlight = null;\n if (this.needsAnotherPush) {\n this.needsAnotherPush = false;\n // Re-enter to push the latest snapshot.\n await this.flush();\n }\n }\n };\n\n this.inFlight = run();\n return this.inFlight;\n }\n\n private async sendOnce(): Promise<void> {\n const { session, tasks, eventPlan, handoffText } = this.store;\n const skillId = sanitizeChannelId(session.skillId ?? this.programId);\n const phase = session.runPhase;\n\n const payload: TaskStreamUpdate = {\n session_id: this.sessionId,\n workflow_id: this.programId,\n skill_id: skillId,\n started_at: this.startedAt,\n run_phase: phase,\n tasks: buildTasks(tasks),\n event_plan: eventPlan.length > 0 ? { events: eventPlan } : undefined,\n error: buildError(phase, session.outroData),\n pending_input: buildPendingInput(session.pendingQuestion),\n // Included on every push once captured; the backend keeps it sticky, so\n // pushes that raced the capture cannot un-set it.\n handoff_text: handoffText ?? undefined,\n timestamp: new Date().toISOString(),\n };\n logToFile(\n `[task-stream-push] push phase=${phase} handoff_text=${\n handoffText === null ? 'absent' : `${handoffText.length} chars`\n }`,\n );\n\n let event: StreamEvent;\n if (!this.created) {\n this.created = true;\n event = StreamEvent.Create;\n } else if (phase === RunPhase.Completed) {\n event = StreamEvent.Complete;\n } else if (phase === RunPhase.Error) {\n event = StreamEvent.Error;\n } else {\n event = StreamEvent.Update;\n }\n\n this.lastPushedPhase = phase;\n this.lastPushedQuestionId = payload.pending_input?.id ?? null;\n\n await Promise.all(\n this.destinations.map((d) =>\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n d.send(event, payload).catch(() => {}),\n ),\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as getUI } from "./debug-
|
|
1
|
+
import { f as getUI } from "./debug-BKqv-yfT.js";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
//#region src/lib/agent/runner/harness/pi/tasks.ts
|
|
@@ -109,4 +109,4 @@ function createWizardPiTaskTools() {
|
|
|
109
109
|
//#endregion
|
|
110
110
|
export { createWizardPiTaskTools };
|
|
111
111
|
|
|
112
|
-
//# sourceMappingURL=tasks-
|
|
112
|
+
//# sourceMappingURL=tasks-2mu3lAvm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-
|
|
1
|
+
{"version":3,"file":"tasks-2mu3lAvm.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/tasks.ts"],"sourcesContent":["/**\n * Task/todo parity for pi (#526). The same four Task tools the anthropic path\n * exposes (TaskCreate/Update/Get/List), as pi `defineTool` tools backed by a\n * shared in-memory store. Every mutation pushes the list to the TUI via\n * `getUI().syncTodos`, so the todo panel updates live under pi exactly like the\n * anthropic path — the thing that was missing before.\n */\n\nimport { Type } from 'typebox';\nimport { defineTool } from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport { getUI } from '@ui';\n\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed';\nexport interface TaskEntry {\n content: string;\n status: TaskStatus;\n activeForm?: string;\n}\nexport type TaskStore = Map<string, TaskEntry>;\n\nfunction text(s: string): {\n content: [{ type: 'text'; text: string }];\n details: unknown;\n} {\n return { content: [{ type: 'text', text: s }], details: {} };\n}\n\nfunction syncToTui(store: TaskStore): void {\n getUI().syncTodos(\n Array.from(store.values()).map((t) => ({\n content: t.content,\n status: t.status,\n activeForm: t.activeForm,\n })),\n );\n}\n\n/** Build the four Task tools over a fresh store. */\nexport function createWizardPiTaskTools(): {\n tools: ToolDefinition[];\n store: TaskStore;\n} {\n const store: TaskStore = new Map();\n\n const taskCreate = defineTool({\n name: 'TaskCreate',\n label: 'Create task',\n description:\n 'Create a task in the shared todo list. Returns its assigned id.',\n promptSnippet:\n 'TaskCreate(content) — add a todo (surfaces progress in the UI)',\n parameters: Type.Object({\n content: Type.String({ description: 'Imperative task description' }),\n activeForm: Type.Optional(\n Type.String({ description: 'Present-continuous form for the spinner' }),\n ),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const id = `task-${store.size + 1}`;\n store.set(id, {\n content: args.content,\n status: 'pending',\n activeForm: args.activeForm,\n });\n syncToTui(store);\n return text(`Created ${id}`);\n },\n });\n\n const taskUpdate = defineTool({\n name: 'TaskUpdate',\n label: 'Update task',\n description:\n 'Update an existing task by id (status, content, or activeForm).',\n promptSnippet:\n 'TaskUpdate(taskId, status) — mark a todo in_progress/completed',\n parameters: Type.Object({\n taskId: Type.String(),\n status: Type.Optional(\n Type.Union([\n Type.Literal('pending'),\n Type.Literal('in_progress'),\n Type.Literal('completed'),\n ]),\n ),\n content: Type.Optional(Type.String()),\n activeForm: Type.Optional(Type.String()),\n }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const existing = store.get(args.taskId);\n if (!existing) return text(`No such task: ${args.taskId}`);\n store.set(args.taskId, {\n content: args.content ?? existing.content,\n status: (args.status as TaskStatus) ?? existing.status,\n activeForm: args.activeForm ?? existing.activeForm,\n });\n syncToTui(store);\n return text(`Updated ${args.taskId}`);\n },\n });\n\n const taskGet = defineTool({\n name: 'TaskGet',\n label: 'Get task',\n description: 'Fetch a single task by id.',\n parameters: Type.Object({ taskId: Type.String() }),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute(_id, args) {\n const t = store.get(args.taskId);\n return text(\n t\n ? JSON.stringify({ id: args.taskId, ...t })\n : `No such task: ${args.taskId}`,\n );\n },\n });\n\n const taskList = defineTool({\n name: 'TaskList',\n label: 'List tasks',\n description: 'List all tasks in the shared todo list.',\n parameters: Type.Object({}),\n // eslint-disable-next-line @typescript-eslint/require-await -- pi tool contract returns a Promise\n async execute() {\n return text(\n JSON.stringify(\n Array.from(store.entries()).map(([id, t]) => ({ id, ...t })),\n ),\n );\n },\n });\n\n return { tools: [taskCreate, taskUpdate, taskGet, taskList], store };\n}\n"],"mappings":";;;;;;;;;;;AAqBA,SAAS,KAAK,GAGZ;AACA,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAG,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG9D,SAAS,UAAU,OAAwB;AACzC,QAAO,CAAC,UACN,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK,OAAO;EACrC,SAAS,EAAE;EACX,QAAQ,EAAE;EACV,YAAY,EAAE;EACf,EAAE,CACJ;;;AAIH,SAAgB,0BAGd;CACA,MAAM,wBAAmB,IAAI,KAAK;AA4FlC,QAAO;EAAE,OAAO;GA1FG,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,SAAS,KAAK,OAAO,EAAE,aAAa,+BAA+B,CAAC;KACpE,YAAY,KAAK,SACf,KAAK,OAAO,EAAE,aAAa,2CAA2C,CAAC,CACxE;KACF,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,KAAK,QAAQ,MAAM,OAAO;AAChC,WAAM,IAAI,IAAI;MACZ,SAAS,KAAK;MACd,QAAQ;MACR,YAAY,KAAK;MAClB,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK;;IAE/B,CAAC;GAEiB,WAAW;IAC5B,MAAM;IACN,OAAO;IACP,aACE;IACF,eACE;IACF,YAAY,KAAK,OAAO;KACtB,QAAQ,KAAK,QAAQ;KACrB,QAAQ,KAAK,SACX,KAAK,MAAM;MACT,KAAK,QAAQ,UAAU;MACvB,KAAK,QAAQ,cAAc;MAC3B,KAAK,QAAQ,YAAY;MAC1B,CAAC,CACH;KACD,SAAS,KAAK,SAAS,KAAK,QAAQ,CAAC;KACrC,YAAY,KAAK,SAAS,KAAK,QAAQ,CAAC;KACzC,CAAC;IAEF,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,WAAW,MAAM,IAAI,KAAK,OAAO;AACvC,SAAI,CAAC,SAAU,QAAO,KAAK,iBAAiB,KAAK,SAAS;AAC1D,WAAM,IAAI,KAAK,QAAQ;MACrB,SAAS,KAAK,WAAW,SAAS;MAClC,QAAS,KAAK,UAAyB,SAAS;MAChD,YAAY,KAAK,cAAc,SAAS;MACzC,CAAC;AACF,eAAU,MAAM;AAChB,YAAO,KAAK,WAAW,KAAK,SAAS;;IAExC,CAAC;GAEc,WAAW;IACzB,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,QAAQ,KAAK,QAAQ,EAAE,CAAC;IAElD,MAAM,QAAQ,KAAK,MAAM;KACvB,MAAM,IAAI,MAAM,IAAI,KAAK,OAAO;AAChC,YAAO,KACL,IACI,KAAK,UAAU;MAAE,IAAI,KAAK;MAAQ,GAAG;MAAG,CAAC,GACzC,iBAAiB,KAAK,SAC3B;;IAEJ,CAAC;GAEe,WAAW;IAC1B,MAAM;IACN,OAAO;IACP,aAAa;IACb,YAAY,KAAK,OAAO,EAAE,CAAC;IAE3B,MAAM,UAAU;AACd,YAAO,KACL,KAAK,UACH,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ;MAAE;MAAI,GAAG;MAAG,EAAE,CAC7D,CACF;;IAEJ,CAAC;GAEyD;EAAE;EAAO"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./debug-
|
|
2
|
-
import { n as analytics } from "./analytics-
|
|
1
|
+
import "./debug-BKqv-yfT.js";
|
|
2
|
+
import { n as analytics } from "./analytics-D8JNuPAT.js";
|
|
3
3
|
import opn from "opn";
|
|
4
4
|
//#region src/utils/links.ts
|
|
5
5
|
/**
|
|
@@ -65,4 +65,4 @@ function updateProgress(step) {
|
|
|
65
65
|
//#endregion
|
|
66
66
|
export { withUtm as i, openTrackedLink as n, setEntryCommand as r, withProgress as t };
|
|
67
67
|
|
|
68
|
-
//# sourceMappingURL=telemetry-
|
|
68
|
+
//# sourceMappingURL=telemetry-BllPiBKw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry-
|
|
1
|
+
{"version":3,"file":"telemetry-BllPiBKw.js","names":[],"sources":["../src/utils/links.ts","../src/telemetry.ts"],"sourcesContent":["/**\n * Outbound links: UTM tagging and tracked opens.\n *\n * Every URL the wizard sends a user to — auto-opened or printed in the TUI —\n * carries `utm_source=wizard`, `utm_medium=cli`, `utm_content=<which link>`.\n * The command dimension rides on every wizard event as the `command` tag.\n * Opening a link also captures a wizard event.\n */\nimport opn from 'opn';\nimport { NODE_ENV } from '@env';\nimport { analytics } from './analytics';\n\n/**\n * Record the CLI command this run was started with (e.g. `integrate`,\n * `slack`, `mcp-add`). Set once at dispatch; tagged onto every wizard\n * event so wizard-side events segment by command.\n */\nexport function setEntryCommand(command: string): void {\n analytics.setTag('command', command);\n}\n\n/**\n * Tag a URL with the wizard's UTM params. `content` names the specific link\n * (e.g. `oauth-signup`, `slack-connect-setup`). URLs that already carry a\n * utm_source — or don't parse — are returned untouched.\n */\nexport function withUtm(url: string, content: string): string {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return url;\n }\n if (parsed.searchParams.has('utm_source')) return url;\n parsed.searchParams.set('utm_source', 'wizard');\n parsed.searchParams.set('utm_medium', 'cli');\n parsed.searchParams.set('utm_content', content);\n return parsed.toString();\n}\n\n/**\n * Open a URL in the user's browser, UTM-tagged (pass `skipUtm` for\n * destinations that aren't PostHog properties or are already final), and\n * capture the interaction. `auto` marks opens the wizard initiated itself,\n * as opposed to a user picking a link on screen. opn throws in headless\n * environments — the URL is always also printed on screen, so the failure\n * is swallowed.\n */\nexport function openTrackedLink(\n url: string,\n content: string,\n opts?: { auto?: boolean; skipUtm?: boolean },\n): void {\n const finalUrl = opts?.skipUtm ? url : withUtm(url, content);\n analytics.wizardCapture('link opened', {\n content,\n url: finalUrl,\n auto: opts?.auto ?? false,\n });\n if (NODE_ENV !== 'test') {\n opn(finalUrl, { wait: false }).catch(() => {\n // No browser available — the printed URL is the fallback.\n });\n }\n}\n","import { analytics } from '@utils/analytics';\n\nexport function withProgress<T>(step: string, callback: () => T): T {\n updateProgress(step);\n return callback();\n}\n\nexport function updateProgress(step: string) {\n analytics.setTag('progress', step);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,gBAAgB,SAAuB;AACrD,WAAU,OAAO,WAAW,QAAQ;;;;;;;AAQtC,SAAgB,QAAQ,KAAa,SAAyB;CAC5D,IAAI;AACJ,KAAI;AACF,WAAS,IAAI,IAAI,IAAI;SACf;AACN,SAAO;;AAET,KAAI,OAAO,aAAa,IAAI,aAAa,CAAE,QAAO;AAClD,QAAO,aAAa,IAAI,cAAc,SAAS;AAC/C,QAAO,aAAa,IAAI,cAAc,MAAM;AAC5C,QAAO,aAAa,IAAI,eAAe,QAAQ;AAC/C,QAAO,OAAO,UAAU;;;;;;;;;;AAW1B,SAAgB,gBACd,KACA,SACA,MACM;CACN,MAAM,WAAW,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ;AAC5D,WAAU,cAAc,eAAe;EACrC;EACA,KAAK;EACL,MAAM,MAAM,QAAQ;EACrB,CAAC;AAEA,KAAI,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,YAAY,GAEzC;;;;AC5DN,SAAgB,aAAgB,MAAc,UAAsB;AAClE,gBAAe,KAAK;AACpB,QAAO,UAAU;;AAGnB,SAAgB,eAAe,MAAc;AAC3C,WAAU,OAAO,YAAY,KAAK"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { V as POSTHOG_APP_URL, h as SERVICE_LABELS, mt as getSkillsBaseUrl, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-
|
|
2
|
-
import { n as analytics } from "./analytics-
|
|
3
|
-
import { i as withUtm, n as openTrackedLink } from "./telemetry-
|
|
4
|
-
import { t as getOrAskForProjectData } from "./setup-utils-
|
|
5
|
-
import { a as fetchUserData, i as fetchSlackConnected } from "./api-
|
|
6
|
-
import "./wizard-session-
|
|
7
|
-
import { a as formatCostUsd, o as formatTokenCount } from "./agent-runner-
|
|
8
|
-
import {
|
|
9
|
-
import { y as AUDIT_SEVERITY_STYLE } from "./yara-hooks-
|
|
10
|
-
import { c as TextBlock, d as isClearBlock, f as isLinesBlock, h as Icons, l as computeVisibleRange, m as Colors, p as isObjectBlock } from "./posthog-integration-
|
|
1
|
+
import { V as POSTHOG_APP_URL, h as SERVICE_LABELS, mt as getSkillsBaseUrl, pt as WIZARD_USER_AGENT, s as logToFile } from "./debug-BKqv-yfT.js";
|
|
2
|
+
import { n as analytics } from "./analytics-D8JNuPAT.js";
|
|
3
|
+
import { i as withUtm, n as openTrackedLink } from "./telemetry-BllPiBKw.js";
|
|
4
|
+
import { t as getOrAskForProjectData } from "./setup-utils-aWZEdfwh.js";
|
|
5
|
+
import { a as fetchUserData, i as fetchSlackConnected } from "./api-CemGgyBv.js";
|
|
6
|
+
import "./wizard-session-DTyLMPsX.js";
|
|
7
|
+
import { a as formatCostUsd, o as formatTokenCount } from "./agent-runner-DEeMqJxy.js";
|
|
8
|
+
import { j as fetchSkillMenu } from "./agent-interface-D3MNaDp3.js";
|
|
9
|
+
import { y as AUDIT_SEVERITY_STYLE } from "./yara-hooks-BXDBIOBq.js";
|
|
10
|
+
import { c as TextBlock, d as isClearBlock, f as isLinesBlock, h as Icons, l as computeVisibleRange, m as Colors, p as isObjectBlock } from "./posthog-integration-LxEBGkoe.js";
|
|
11
11
|
import { E as getKindMeta, a as useStdoutDimensions, f as Program, i as useKeyboardHintsContext, n as useKeyBindings, o as ConfirmButton, r as KeyboardHintsProvider, s as PromptLabel, t as PickerMenu } from "./bin.js";
|
|
12
|
-
import { r as totalTokenCount } from "./store-
|
|
13
|
-
import { c as namesWithStatus, n as AVAILABLE_FEATURES, o as isAllFeaturesSelected, s as isOk, t as ALL_FEATURE_VALUES, u as summarizeFailure } from "./defaults-
|
|
12
|
+
import { r as totalTokenCount } from "./store-P_7zUfbj.js";
|
|
13
|
+
import { c as namesWithStatus, n as AVAILABLE_FEATURES, o as isAllFeaturesSelected, s as isOk, t as ALL_FEATURE_VALUES, u as summarizeFailure } from "./defaults-BHhXrX9F.js";
|
|
14
14
|
import opn from "opn";
|
|
15
15
|
import * as fs$1 from "fs";
|
|
16
16
|
import axios from "axios";
|
|
@@ -2700,8 +2700,25 @@ const IssueRow = ({ issue, docsWidth }) => {
|
|
|
2700
2700
|
*
|
|
2701
2701
|
* When done, calls store.setMcpComplete(). The router resolves to outro.
|
|
2702
2702
|
*/
|
|
2703
|
-
const markDone = (store, outcome, clients = [], featuresSelected) => {
|
|
2704
|
-
store.setMcpComplete(outcome, clients, featuresSelected);
|
|
2703
|
+
const markDone = (store, outcome, clients = [], featuresSelected, loginCommands = []) => {
|
|
2704
|
+
store.setMcpComplete(outcome, clients, featuresSelected, loginCommands);
|
|
2705
|
+
};
|
|
2706
|
+
/**
|
|
2707
|
+
* The editor-owned login commands still to run after this install: fresh
|
|
2708
|
+
* config entries use the client's own server name, plugin-provided servers
|
|
2709
|
+
* their `plugin:` name. One entry per client.
|
|
2710
|
+
*/
|
|
2711
|
+
const pendingLoginCommands = (clients, mcpResult, pluginResult) => {
|
|
2712
|
+
const commands = /* @__PURE__ */ new Map();
|
|
2713
|
+
for (const r of pluginResult) {
|
|
2714
|
+
const command = clients.find((c) => c.name === r.name)?.pluginLoginCommand;
|
|
2715
|
+
if (isOk(r) && command) commands.set(r.name, command);
|
|
2716
|
+
}
|
|
2717
|
+
for (const r of mcpResult) {
|
|
2718
|
+
const command = clients.find((c) => c.name === r.name)?.loginCommand;
|
|
2719
|
+
if (r.status === "changed" && command) commands.set(r.name, command);
|
|
2720
|
+
}
|
|
2721
|
+
return [...commands.values()];
|
|
2705
2722
|
};
|
|
2706
2723
|
const reportFeatures = (features) => isAllFeaturesSelected(features) ? "all" : features;
|
|
2707
2724
|
const errorText = (err) => summarizeFailure(err instanceof Error ? err.message : String(err)) ?? "unknown error";
|
|
@@ -2777,7 +2794,6 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
|
|
|
2777
2794
|
const [selectedClientNames, setSelectedClientNames] = useState([]);
|
|
2778
2795
|
const [mcpResults, setMcpResults] = useState([]);
|
|
2779
2796
|
const [pluginResults, setPluginResults] = useState([]);
|
|
2780
|
-
const [installMode, setInstallMode] = useState("custom");
|
|
2781
2797
|
const [detectError, setDetectError] = useState(null);
|
|
2782
2798
|
const [flowError, setFlowError] = useState(null);
|
|
2783
2799
|
const finishFlow = useRef(null);
|
|
@@ -2799,65 +2815,46 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
|
|
|
2799
2815
|
}
|
|
2800
2816
|
})();
|
|
2801
2817
|
}, [installer]);
|
|
2802
|
-
const
|
|
2818
|
+
const oauthFlow = !store.session.apiKey;
|
|
2819
|
+
const proceedAfterClientPick = (clientNames) => {
|
|
2803
2820
|
setSelectedClientNames(clientNames);
|
|
2804
|
-
if (
|
|
2805
|
-
|
|
2821
|
+
if (clientNames.some((name) => clients.find((c) => c.name === name)?.finish)) {
|
|
2822
|
+
setPhase("connector");
|
|
2806
2823
|
return;
|
|
2807
2824
|
}
|
|
2808
|
-
if (
|
|
2809
|
-
doInstall(clientNames
|
|
2825
|
+
if (oauthFlow) {
|
|
2826
|
+
doInstall(clientNames);
|
|
2810
2827
|
return;
|
|
2811
2828
|
}
|
|
2812
|
-
if (
|
|
2813
|
-
|
|
2829
|
+
if (store.session.mcpFeatures) {
|
|
2830
|
+
doInstall(clientNames, store.session.mcpFeatures);
|
|
2814
2831
|
return;
|
|
2815
2832
|
}
|
|
2816
2833
|
setPhase("feature-select");
|
|
2817
2834
|
};
|
|
2818
2835
|
const handleConfirm = () => {
|
|
2819
2836
|
if (isRemove) doRemove();
|
|
2820
|
-
else if (clients.length === 1) proceedAfterClientPick([clients[0].name]
|
|
2821
|
-
else setPhase("pick");
|
|
2822
|
-
};
|
|
2823
|
-
const handleTriStateChoice = (choice) => {
|
|
2824
|
-
if (choice === "skip") {
|
|
2825
|
-
handleSkip();
|
|
2826
|
-
return;
|
|
2827
|
-
}
|
|
2828
|
-
setInstallMode(choice);
|
|
2829
|
-
if (clients.length === 1) proceedAfterClientPick([clients[0].name], choice);
|
|
2837
|
+
else if (clients.length === 1) proceedAfterClientPick([clients[0].name]);
|
|
2830
2838
|
else setPhase("pick");
|
|
2831
2839
|
};
|
|
2832
2840
|
const handleSkip = () => {
|
|
2833
2841
|
markDone(store, "skipped");
|
|
2834
2842
|
};
|
|
2835
|
-
|
|
2836
|
-
* `chosenMode` is passed in rather than read from `installMode`: the tri-state
|
|
2837
|
-
* picker sets that state and installs in the same tick when a single client
|
|
2838
|
-
* is detected, so the closure would still hold the previous value and a
|
|
2839
|
-
* one-editor machine would silently get the MCP-only path.
|
|
2840
|
-
*/
|
|
2841
|
-
const doInstall = async (names, features, chosenMode = installMode) => {
|
|
2843
|
+
const doInstall = async (names, features) => {
|
|
2842
2844
|
setPhase("working");
|
|
2843
2845
|
let mcpResult = [];
|
|
2844
2846
|
let pluginResult = [];
|
|
2845
2847
|
const pluginCapableSet = new Set(clients.filter((c) => c.supportsPlugin).map((c) => c.name));
|
|
2848
|
+
const bundledSet = new Set(clients.filter((c) => c.pluginBundlesMcp).map((c) => c.name));
|
|
2846
2849
|
const pluginCapableNames = names.filter((n) => pluginCapableSet.has(n));
|
|
2847
|
-
const directNames = names.filter((n) => !
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
pluginResult = await installer.installPlugins(pluginCapableNames);
|
|
2856
|
-
} catch (err) {
|
|
2857
|
-
setFlowError(errorText(err));
|
|
2858
|
-
}
|
|
2859
|
-
} else try {
|
|
2860
|
-
mcpResult = await installer.install(names, features, store.session.apiKey);
|
|
2850
|
+
const directNames = names.filter((n) => !bundledSet.has(n));
|
|
2851
|
+
try {
|
|
2852
|
+
mcpResult = await installer.install(oauthFlow ? directNames : names, features, store.session.apiKey);
|
|
2853
|
+
} catch (err) {
|
|
2854
|
+
setFlowError(errorText(err));
|
|
2855
|
+
}
|
|
2856
|
+
if (oauthFlow) try {
|
|
2857
|
+
pluginResult = await installer.installPlugins(pluginCapableNames);
|
|
2861
2858
|
} catch (err) {
|
|
2862
2859
|
setFlowError(errorText(err));
|
|
2863
2860
|
}
|
|
@@ -2866,7 +2863,8 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
|
|
|
2866
2863
|
const ready = [...mcpResult, ...pluginResult].filter(isOk);
|
|
2867
2864
|
const outcome = ready.length > 0 ? "installed" : "failed";
|
|
2868
2865
|
const featuresReport = reportFeatures(features ?? [...ALL_FEATURE_VALUES]);
|
|
2869
|
-
|
|
2866
|
+
const logins = oauthFlow ? pendingLoginCommands(clients, mcpResult, pluginResult) : [];
|
|
2867
|
+
finishFlow.current = () => markDone(store, outcome, ready.map((r) => r.name), featuresReport, logins);
|
|
2870
2868
|
setPhase("done");
|
|
2871
2869
|
};
|
|
2872
2870
|
const doRemove = async () => {
|
|
@@ -2961,26 +2959,7 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
|
|
|
2961
2959
|
}),
|
|
2962
2960
|
/* @__PURE__ */ jsx(Box, {
|
|
2963
2961
|
marginTop: 1,
|
|
2964
|
-
children:
|
|
2965
|
-
message: `Install the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
|
|
2966
|
-
options: [
|
|
2967
|
-
{
|
|
2968
|
-
label: "Install with all features",
|
|
2969
|
-
value: "all",
|
|
2970
|
-
hint: "recommended"
|
|
2971
|
-
},
|
|
2972
|
-
{
|
|
2973
|
-
label: "Customize features",
|
|
2974
|
-
value: "custom"
|
|
2975
|
-
},
|
|
2976
|
-
{
|
|
2977
|
-
label: "No thanks",
|
|
2978
|
-
value: "skip"
|
|
2979
|
-
}
|
|
2980
|
-
],
|
|
2981
|
-
mode: "single",
|
|
2982
|
-
onSelect: (choice) => handleTriStateChoice(choice)
|
|
2983
|
-
}) : /* @__PURE__ */ jsx(ConfirmationInput, {
|
|
2962
|
+
children: /* @__PURE__ */ jsx(ConfirmationInput, {
|
|
2984
2963
|
message: `${isRemove ? "Remove" : "Install"} the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
|
|
2985
2964
|
confirmLabel: isRemove ? "Remove" : "Install",
|
|
2986
2965
|
cancelLabel: "No thanks",
|
|
@@ -2990,16 +2969,16 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
|
|
|
2990
2969
|
})
|
|
2991
2970
|
] }),
|
|
2992
2971
|
phase === "pick" && /* @__PURE__ */ jsx(PickerMenu, {
|
|
2993
|
-
message:
|
|
2972
|
+
message: "Select editor to install",
|
|
2994
2973
|
options: clients.map((c) => ({
|
|
2995
2974
|
label: c.name,
|
|
2996
2975
|
value: c.name,
|
|
2997
2976
|
exclusive: Boolean(c.finish),
|
|
2998
|
-
hint:
|
|
2977
|
+
hint: c.finish ? "connector" : c.supportsPlugin ? "plugin" : "MCP"
|
|
2999
2978
|
})),
|
|
3000
2979
|
mode: "multi",
|
|
3001
2980
|
onSelect: (selected) => {
|
|
3002
|
-
proceedAfterClientPick(Array.isArray(selected) ? selected : [selected]
|
|
2981
|
+
proceedAfterClientPick(Array.isArray(selected) ? selected : [selected]);
|
|
3003
2982
|
}
|
|
3004
2983
|
}),
|
|
3005
2984
|
phase === "feature-select" && /* @__PURE__ */ jsx(GroupedPickerMenu, {
|
|
@@ -4625,7 +4604,14 @@ const FOLLOW_UP_DELAY_MS = 3e3;
|
|
|
4625
4604
|
const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
4626
4605
|
useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
|
|
4627
4606
|
const session = store.session;
|
|
4628
|
-
const
|
|
4607
|
+
const programId = useMemo(() => store.analyticsProgramId, [store]);
|
|
4608
|
+
const capture = useMemo(() => (event, properties) => {
|
|
4609
|
+
analytics.wizardCapture(event, {
|
|
4610
|
+
program_id: programId,
|
|
4611
|
+
...properties
|
|
4612
|
+
});
|
|
4613
|
+
}, [programId]);
|
|
4614
|
+
const [phase, setPhase] = useState(store.router.activeProgram === Program.McpTutorial ? "choose" : "goodbye");
|
|
4629
4615
|
const [profile, setProfile] = useState(null);
|
|
4630
4616
|
const greeting = useMemo(() => getRoleGreeting(session.roleAtOrganization), [session.roleAtOrganization]);
|
|
4631
4617
|
const pickerCandidates = useMemo(() => getTutorialPicker(session.roleAtOrganization, session.integration, profile), [
|
|
@@ -4692,7 +4678,7 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4692
4678
|
if (cancelled) return;
|
|
4693
4679
|
setProfile(probed);
|
|
4694
4680
|
const offerSeed = probed.tier === "empty" && isKnownCloudHost(credentials.host.apiHost);
|
|
4695
|
-
|
|
4681
|
+
capture("mcp suggested prompts scouted", {
|
|
4696
4682
|
tier: probed.tier,
|
|
4697
4683
|
totalEvents: probed.totalEvents,
|
|
4698
4684
|
distinctEvents: probed.distinctEventCount,
|
|
@@ -4727,12 +4713,12 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4727
4713
|
});
|
|
4728
4714
|
if (cancelled) return;
|
|
4729
4715
|
setProfile(seeded);
|
|
4730
|
-
|
|
4716
|
+
capture("mcp suggested prompts seeded", { totalEvents: seeded.totalEvents });
|
|
4731
4717
|
} catch (err) {
|
|
4732
4718
|
if (cancelled) return;
|
|
4733
4719
|
const message = err instanceof Error ? err.message : String(err);
|
|
4734
4720
|
logToFile(`[McpSuggestedPromptsScreen] seeding failed: ${message}`);
|
|
4735
|
-
|
|
4721
|
+
capture("mcp suggested prompts seed failed", { error: message });
|
|
4736
4722
|
}
|
|
4737
4723
|
if (cancelled) return;
|
|
4738
4724
|
setPhase("greeting");
|
|
@@ -4762,11 +4748,11 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4762
4748
|
const finishStream = (kind, durationMs, errorText) => {
|
|
4763
4749
|
if (controller.signal.aborted) return;
|
|
4764
4750
|
setRunDurationSecs(Math.round(durationMs / 1e3));
|
|
4765
|
-
if (kind === "done")
|
|
4751
|
+
if (kind === "done") capture("mcp suggested prompts run", {
|
|
4766
4752
|
prompt: runningPrompt,
|
|
4767
4753
|
durationMs
|
|
4768
4754
|
});
|
|
4769
|
-
else
|
|
4755
|
+
else capture("mcp suggested prompts run failed", {
|
|
4770
4756
|
prompt: runningPrompt,
|
|
4771
4757
|
error: errorText
|
|
4772
4758
|
});
|
|
@@ -4835,20 +4821,20 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4835
4821
|
const choice = Array.isArray(value) ? value[0] : value;
|
|
4836
4822
|
setLoginError(null);
|
|
4837
4823
|
if (choice === "login") {
|
|
4838
|
-
|
|
4824
|
+
capture("mcp suggested prompts choose", { choice: "login" });
|
|
4839
4825
|
startedTutorialRef.current = true;
|
|
4840
4826
|
setPhase(session.credentials ? "scouting" : "authenticating");
|
|
4841
4827
|
} else {
|
|
4842
|
-
|
|
4828
|
+
capture("mcp suggested prompts choose", { choice: "exit" });
|
|
4843
4829
|
enterGoodbye();
|
|
4844
4830
|
}
|
|
4845
4831
|
};
|
|
4846
4832
|
const handleSeedChoice = (value) => {
|
|
4847
4833
|
if ((Array.isArray(value) ? value[0] : value) === "seed") {
|
|
4848
|
-
|
|
4834
|
+
capture("mcp suggested prompts seed offer", { choice: "seed" });
|
|
4849
4835
|
setPhase("seeding");
|
|
4850
4836
|
} else {
|
|
4851
|
-
|
|
4837
|
+
capture("mcp suggested prompts seed offer", { choice: "skip" });
|
|
4852
4838
|
setPhase("greeting");
|
|
4853
4839
|
}
|
|
4854
4840
|
};
|
|
@@ -4864,14 +4850,14 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4864
4850
|
const handleFollowUpPick = (value) => {
|
|
4865
4851
|
const picked = Array.isArray(value) ? value[0] : value;
|
|
4866
4852
|
if (picked === "__follow_up_exit__") {
|
|
4867
|
-
|
|
4853
|
+
capture("mcp suggested prompts follow-up", {
|
|
4868
4854
|
choice: "exit",
|
|
4869
4855
|
depth: branchHistory.length
|
|
4870
4856
|
});
|
|
4871
4857
|
enterGoodbye();
|
|
4872
4858
|
return;
|
|
4873
4859
|
}
|
|
4874
|
-
|
|
4860
|
+
capture("mcp suggested prompts follow-up", {
|
|
4875
4861
|
choice: "continue",
|
|
4876
4862
|
depth: branchHistory.length,
|
|
4877
4863
|
lastToolName
|
|
@@ -4979,6 +4965,7 @@ const McpSuggestedPromptsScreen = ({ store, services }) => {
|
|
|
4979
4965
|
integration: session.integration,
|
|
4980
4966
|
profile,
|
|
4981
4967
|
engaged: branchHistory.length > 0,
|
|
4968
|
+
loginCommands: session.mcpLoginCommands,
|
|
4982
4969
|
onClose: closeWizard
|
|
4983
4970
|
})
|
|
4984
4971
|
]
|
|
@@ -5408,7 +5395,7 @@ const FollowUpPhase = ({ lastToolName, lastToolCommand, lastPrompt, chunks, role
|
|
|
5408
5395
|
})]
|
|
5409
5396
|
});
|
|
5410
5397
|
};
|
|
5411
|
-
const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, onClose }) => {
|
|
5398
|
+
const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, loginCommands, onClose }) => {
|
|
5412
5399
|
const generated = getGeneratedQuests(profile);
|
|
5413
5400
|
const samples = (generated.length > 0 ? generated : getRolePrompts(role, integration)).slice(0, 3);
|
|
5414
5401
|
const headline = engaged ? "Nice work. You can keep talking to PostHog anytime." : "You're all set — PostHog MCP is here when you're ready.";
|
|
@@ -5437,6 +5424,19 @@ const GoodbyePhase = ({ installedClients, role, integration, profile, engaged, o
|
|
|
5437
5424
|
marginBottom: 1,
|
|
5438
5425
|
children: introLine
|
|
5439
5426
|
}),
|
|
5427
|
+
loginCommands.length > 0 && /* @__PURE__ */ jsxs(Box, {
|
|
5428
|
+
flexDirection: "column",
|
|
5429
|
+
marginBottom: 1,
|
|
5430
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
5431
|
+
color: "green",
|
|
5432
|
+
bold: true,
|
|
5433
|
+
children: ["✔", " Authenticate to finish (opens your browser):"]
|
|
5434
|
+
}), loginCommands.map((command) => /* @__PURE__ */ jsxs(Text, { children: [" ", /* @__PURE__ */ jsx(Text, {
|
|
5435
|
+
bold: true,
|
|
5436
|
+
color: "green",
|
|
5437
|
+
children: command
|
|
5438
|
+
})] }, command))]
|
|
5439
|
+
}),
|
|
5440
5440
|
/* @__PURE__ */ jsx(Box, {
|
|
5441
5441
|
marginBottom: 1,
|
|
5442
5442
|
flexDirection: "column",
|
|
@@ -6896,9 +6896,10 @@ const OutroScreen = ({ store }) => {
|
|
|
6896
6896
|
* Shared "Skill: <id> / URL: <downloadUrl>" block for intro screens.
|
|
6897
6897
|
*
|
|
6898
6898
|
* `useSkillEntry` fetches the entry from the skill menu and re-runs when
|
|
6899
|
-
* `skillId`
|
|
6900
|
-
*
|
|
6901
|
-
*
|
|
6899
|
+
* `skillId` changes — detection sets it after mount. The previous fetch is
|
|
6900
|
+
* cancelled so a late result can't overwrite a newer one. Which registry to
|
|
6901
|
+
* fetch from is not a screen's business: `getSkillsBaseUrl()` reads the
|
|
6902
|
+
* process-wide target.
|
|
6902
6903
|
*
|
|
6903
6904
|
* `<SkillSourceInfo>` renders the block, taking the entry as a prop so the
|
|
6904
6905
|
* caller can reuse the same hook result for additional UI (e.g. showing
|
|
@@ -6924,7 +6925,7 @@ function resolveSkillEntry(entries, skillId) {
|
|
|
6924
6925
|
const variants = entries.filter((s) => s.id.startsWith(`integration-${skillId}-`));
|
|
6925
6926
|
return variants.length === 1 ? variants[0] : null;
|
|
6926
6927
|
}
|
|
6927
|
-
function useSkillEntry(skillId
|
|
6928
|
+
function useSkillEntry(skillId) {
|
|
6928
6929
|
const [skillEntry, setSkillEntry] = useState(null);
|
|
6929
6930
|
const [fetchFailed, setFetchFailed] = useState(false);
|
|
6930
6931
|
useEffect(() => {
|
|
@@ -6935,7 +6936,7 @@ function useSkillEntry(skillId, local) {
|
|
|
6935
6936
|
let cancelled = false;
|
|
6936
6937
|
setSkillEntry(null);
|
|
6937
6938
|
setFetchFailed(false);
|
|
6938
|
-
fetchSkillMenu(getSkillsBaseUrl(
|
|
6939
|
+
fetchSkillMenu(getSkillsBaseUrl()).then((menu) => {
|
|
6939
6940
|
if (cancelled) return;
|
|
6940
6941
|
if (!menu) {
|
|
6941
6942
|
setFetchFailed(true);
|
|
@@ -6948,7 +6949,7 @@ function useSkillEntry(skillId, local) {
|
|
|
6948
6949
|
return () => {
|
|
6949
6950
|
cancelled = true;
|
|
6950
6951
|
};
|
|
6951
|
-
}, [skillId
|
|
6952
|
+
}, [skillId]);
|
|
6952
6953
|
return {
|
|
6953
6954
|
skillEntry,
|
|
6954
6955
|
fetchFailed
|
|
@@ -7002,7 +7003,7 @@ const AiOptInRequiredScreen = ({ store }) => {
|
|
|
7002
7003
|
const [showSkill, setShowSkill] = useState(false);
|
|
7003
7004
|
const [retrying, setRetrying] = useState(false);
|
|
7004
7005
|
const [retryError, setRetryError] = useState(null);
|
|
7005
|
-
const { skillEntry } = useSkillEntry(session.skillId
|
|
7006
|
+
const { skillEntry } = useSkillEntry(session.skillId);
|
|
7006
7007
|
useEffect(() => {
|
|
7007
7008
|
analytics.wizardCapture("ai opt-in shown", { variant });
|
|
7008
7009
|
}, [variant]);
|
|
@@ -7218,4 +7219,4 @@ function releaseTerminal() {
|
|
|
7218
7219
|
//#endregion
|
|
7219
7220
|
export { EventPlanViewer as A, CardLayout as B, VisualizerTab as C, HNViewer as D, ContentSequencer as E, ConfirmationInput as F, GroupedPickerMenu as I, ProgressList as L, LinkText as M, extractUrls as N, TabContainer as O, ModalOverlay as P, LoadingBox as R, ServiceHealthList as S, LearnCard as T, TAILORED_ROLES as _, useSkillEntry as a, SEVERITY_LABEL as b, SlackConnectScreen as c, AuditChecksViewer as d, seedDemoEvents as f, probeProjectData as g, assembleProfile as h, SkillSourceInfo as i, LogViewer as j, ScreenContainer as k, AUDIT_AREA_SLIDES as l, McpSuggestedPromptsScreen as m, releaseTerminal as n, OutroScreen as o, seededProfile as p, AiOptInRequiredScreen as r, useDismissOnAnyKey as s, enterDarkTerminal as t, VisualBox as u, McpScreen as v, TipsCard as w, SEVERITY_ORDER as x, IssueTable as y, SplitView as z };
|
|
7220
7221
|
|
|
7221
|
-
//# sourceMappingURL=terminal-
|
|
7222
|
+
//# sourceMappingURL=terminal-aWQrDy8s.js.map
|