@alumbwe/anvil 1.0.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 +84 -0
- package/bin/anvil +2 -0
- package/bin/tree-sitter.wasm +0 -0
- package/package.json +67 -0
- package/src/__tests__/README.md +256 -0
- package/src/__tests__/bash-mode.test.ts +442 -0
- package/src/__tests__/cli-args.test.ts +181 -0
- package/src/__tests__/cli-harness.test.ts +52 -0
- package/src/__tests__/e2e/first-time-login.test.ts +173 -0
- package/src/__tests__/e2e/logout-relogin-flow.test.ts +110 -0
- package/src/__tests__/e2e/returning-user-auth.test.ts +113 -0
- package/src/__tests__/e2e-cli.test.ts +183 -0
- package/src/__tests__/helpers/launcher-disconnect-fixture.cjs +176 -0
- package/src/__tests__/helpers/mock-api-client.ts +64 -0
- package/src/__tests__/helpers/renderer-cleanup-fixture.tsx +89 -0
- package/src/__tests__/helpers/terminal-watchdog-fixture.ts +75 -0
- package/src/__tests__/home-directory-detection.test.ts +213 -0
- package/src/__tests__/integration/api-integration.test.ts +288 -0
- package/src/__tests__/integration/credentials-storage.test.ts +486 -0
- package/src/__tests__/integration/local-agents.test.ts +1187 -0
- package/src/__tests__/integration/login-flow-instrumentation.test.ts +134 -0
- package/src/__tests__/integration/login-polling-working.test.ts +285 -0
- package/src/__tests__/integration/queue-editing.test.tsx +261 -0
- package/src/__tests__/integration/usage-refresh-on-completion.test.ts +168 -0
- package/src/__tests__/integration-tmux.test.ts +188 -0
- package/src/__tests__/launcher-avx2-fallback.test.ts +630 -0
- package/src/__tests__/launcher-disconnect.test.ts +104 -0
- package/src/__tests__/mocks/hover-toggle-controller.ts +71 -0
- package/src/__tests__/open-tui-native-bundle.test.ts +378 -0
- package/src/__tests__/path-completion.test.ts +243 -0
- package/src/__tests__/project-files-chat-id.test.ts +34 -0
- package/src/__tests__/release/proxy-http-get.test.ts +667 -0
- package/src/__tests__/release/wrapper-safety.test.ts +412 -0
- package/src/__tests__/renderer-cleanup.test.ts +174 -0
- package/src/__tests__/rerender-perf.integration.test.ts +283 -0
- package/src/__tests__/terminal-reset-sequences.test.ts +36 -0
- package/src/__tests__/terminal-watchdog.test.ts +388 -0
- package/src/__tests__/test-utils.ts +271 -0
- package/src/__tests__/tmux-poc.ts +150 -0
- package/src/__tests__/unit/agent-mode-toggle.test.ts +137 -0
- package/src/__tests__/unit/copy-button.test.ts +157 -0
- package/src/__tests__/unit/create-run-config.test.ts +66 -0
- package/src/__tests__/unit/publish-confirmation.test.ts +70 -0
- package/src/__tests__/unit/segmented-control.test.ts +152 -0
- package/src/__tests__/utils/env.test.ts +169 -0
- package/src/__tests__/utils/project-picker.test.ts +39 -0
- package/src/agents/bundled-agents.generated.d.ts +14 -0
- package/src/agents/bundled-agents.generated.ts +3692 -0
- package/src/app.tsx +399 -0
- package/src/chat.tsx +1698 -0
- package/src/cli-args.ts +134 -0
- package/src/commands/__tests__/bash-command.test.ts +445 -0
- package/src/commands/__tests__/command-args.test.ts +349 -0
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +108 -0
- package/src/commands/__tests__/copy-conversation.test.ts +226 -0
- package/src/commands/__tests__/diagnostics-command.test.ts +35 -0
- package/src/commands/__tests__/freebuff-command-aliases.test.ts +56 -0
- package/src/commands/__tests__/image.test.ts +99 -0
- package/src/commands/__tests__/init.test.ts +456 -0
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +91 -0
- package/src/commands/__tests__/process-diagnostics.test.ts +54 -0
- package/src/commands/__tests__/prompt-builders.test.ts +28 -0
- package/src/commands/__tests__/router-input.test.ts +298 -0
- package/src/commands/ads.ts +44 -0
- package/src/commands/command-registry.ts +726 -0
- package/src/commands/copy-conversation.ts +392 -0
- package/src/commands/help.ts +13 -0
- package/src/commands/image.ts +20 -0
- package/src/commands/init.ts +121 -0
- package/src/commands/process-diagnostics.ts +120 -0
- package/src/commands/prompt-builders.ts +100 -0
- package/src/commands/publish.ts +203 -0
- package/src/commands/router-utils.ts +80 -0
- package/src/commands/router.ts +487 -0
- package/src/commands/usage.ts +26 -0
- package/src/components/__tests__/ad-banner.test.tsx +83 -0
- package/src/components/__tests__/chat-input-bar.test.tsx +130 -0
- package/src/components/__tests__/freebuff-landing-heading-row.test.tsx +91 -0
- package/src/components/__tests__/freebuff-model-selector.test.tsx +519 -0
- package/src/components/__tests__/freebuff-offer-invariants.test.ts +54 -0
- package/src/components/__tests__/freebuff-takeover-prompt.test.tsx +212 -0
- package/src/components/__tests__/grid-layout.integration.test.tsx +304 -0
- package/src/components/__tests__/grid-layout.test.tsx +1051 -0
- package/src/components/__tests__/message-block.completion.test.tsx +104 -0
- package/src/components/__tests__/message-block.streaming.test.tsx +78 -0
- package/src/components/__tests__/message-with-agents.test.tsx +579 -0
- package/src/components/__tests__/multiline-input.test.tsx +1132 -0
- package/src/components/__tests__/queue-panel.test.tsx +398 -0
- package/src/components/__tests__/selectable-list.test.ts +232 -0
- package/src/components/__tests__/status-bar.test.tsx +44 -0
- package/src/components/__tests__/status-indicator.test.tsx +204 -0
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +111 -0
- package/src/components/__tests__/user-error-banner.test.tsx +102 -0
- package/src/components/ad-banner.tsx +323 -0
- package/src/components/agent-checklist.tsx +350 -0
- package/src/components/agent-mode-toggle.tsx +241 -0
- package/src/components/anvil-mark.tsx +52 -0
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
- package/src/components/ask-user/__tests__/validation.test.ts +213 -0
- package/src/components/ask-user/components/accordion-question.tsx +153 -0
- package/src/components/ask-user/components/custom-answer-input.tsx +66 -0
- package/src/components/ask-user/components/options-list.tsx +133 -0
- package/src/components/ask-user/components/question-header.tsx +80 -0
- package/src/components/ask-user/components/question-option.tsx +89 -0
- package/src/components/ask-user/constants.ts +38 -0
- package/src/components/ask-user/index.tsx +631 -0
- package/src/components/ask-user/utils/validation.ts +85 -0
- package/src/components/attachment-card.tsx +65 -0
- package/src/components/blocks/agent-block-grid.tsx +58 -0
- package/src/components/blocks/agent-branch-item.tsx +311 -0
- package/src/components/blocks/agent-branch-wrapper.tsx +493 -0
- package/src/components/blocks/agent-list-branch.tsx +77 -0
- package/src/components/blocks/ask-user-branch.tsx +81 -0
- package/src/components/blocks/block-helpers.ts +11 -0
- package/src/components/blocks/blocks-renderer.tsx +289 -0
- package/src/components/blocks/content-with-markdown.tsx +33 -0
- package/src/components/blocks/image-block.tsx +128 -0
- package/src/components/blocks/implementor-row.tsx +429 -0
- package/src/components/blocks/single-block.tsx +201 -0
- package/src/components/blocks/thinking-block.tsx +67 -0
- package/src/components/blocks/tool-block-group.tsx +57 -0
- package/src/components/blocks/tool-branch.tsx +206 -0
- package/src/components/blocks/user-content-copy.tsx +157 -0
- package/src/components/bottom-banner.tsx +136 -0
- package/src/components/build-mode-buttons.tsx +110 -0
- package/src/components/button.tsx +66 -0
- package/src/components/chat-header.tsx +77 -0
- package/src/components/chat-history-screen.tsx +413 -0
- package/src/components/chat-input-bar.tsx +523 -0
- package/src/components/clickable-title-box.tsx +66 -0
- package/src/components/clickable.tsx +119 -0
- package/src/components/collapse-button.tsx +34 -0
- package/src/components/copy-button.tsx +203 -0
- package/src/components/elapsed-timer.tsx +55 -0
- package/src/components/error-boundary.tsx +55 -0
- package/src/components/feedback-container.tsx +181 -0
- package/src/components/feedback-icon-button.tsx +89 -0
- package/src/components/feedback-input-mode.tsx +344 -0
- package/src/components/file-attachment-card.tsx +98 -0
- package/src/components/freebuff-active-session-summary.tsx +63 -0
- package/src/components/freebuff-landing-screen.tsx +865 -0
- package/src/components/freebuff-model-selector.tsx +1087 -0
- package/src/components/freebuff-referral-banner.tsx +604 -0
- package/src/components/freebuff-superseded-screen.tsx +62 -0
- package/src/components/grid-layout.tsx +80 -0
- package/src/components/help-banner.tsx +127 -0
- package/src/components/highlighted-text.tsx +52 -0
- package/src/components/image-card.tsx +167 -0
- package/src/components/image-thumbnail.tsx +114 -0
- package/src/components/input-cursor.tsx +77 -0
- package/src/components/input-mode-banner.tsx +57 -0
- package/src/components/load-previous-button.tsx +48 -0
- package/src/components/login-modal.tsx +475 -0
- package/src/components/message-block.tsx +346 -0
- package/src/components/message-footer.tsx +258 -0
- package/src/components/message-with-agents.tsx +506 -0
- package/src/components/mode-divider.tsx +43 -0
- package/src/components/multiline-input.tsx +1241 -0
- package/src/components/out-of-credits-banner.tsx +160 -0
- package/src/components/pending-attachments-banner.tsx +116 -0
- package/src/components/pending-bash-message.tsx +59 -0
- package/src/components/progress-bar.tsx +81 -0
- package/src/components/project-picker-screen.tsx +485 -0
- package/src/components/publish-confirmation.tsx +493 -0
- package/src/components/publish-container.tsx +687 -0
- package/src/components/queue-panel.tsx +310 -0
- package/src/components/raised-pill.tsx +95 -0
- package/src/components/renderers/plan-box.tsx +59 -0
- package/src/components/review-screen.tsx +112 -0
- package/src/components/scroll-to-bottom-button.tsx +34 -0
- package/src/components/segmented-control.tsx +187 -0
- package/src/components/selectable-list.tsx +248 -0
- package/src/components/selected-chips.tsx +80 -0
- package/src/components/separator.tsx +40 -0
- package/src/components/session-ended-banner.tsx +226 -0
- package/src/components/shimmer-text.tsx +248 -0
- package/src/components/status-bar.tsx +313 -0
- package/src/components/subscription-limit-banner.tsx +206 -0
- package/src/components/suggested-prompts.tsx +184 -0
- package/src/components/suggestion-menu.tsx +215 -0
- package/src/components/terminal-command-display.tsx +151 -0
- package/src/components/terminal-link.tsx +97 -0
- package/src/components/terminal-thinking-orb.tsx +95 -0
- package/src/components/text-attachment-card.tsx +77 -0
- package/src/components/thinking.tsx +109 -0
- package/src/components/tools/__tests__/apply-patch.test.tsx +84 -0
- package/src/components/tools/__tests__/code-search.test.tsx +45 -0
- package/src/components/tools/__tests__/gravity-index.test.ts +76 -0
- package/src/components/tools/__tests__/render-ui.test.tsx +92 -0
- package/src/components/tools/__tests__/run-terminal-command.test.ts +233 -0
- package/src/components/tools/apply-patch.tsx +95 -0
- package/src/components/tools/code-search.tsx +45 -0
- package/src/components/tools/composio.tsx +152 -0
- package/src/components/tools/diff-viewer.tsx +76 -0
- package/src/components/tools/glob.tsx +61 -0
- package/src/components/tools/gravity-index.tsx +77 -0
- package/src/components/tools/list-directory.tsx +61 -0
- package/src/components/tools/read-docs.tsx +37 -0
- package/src/components/tools/read-files.tsx +91 -0
- package/src/components/tools/read-subtree.tsx +44 -0
- package/src/components/tools/read-url.tsx +33 -0
- package/src/components/tools/registry.ts +126 -0
- package/src/components/tools/render-ui.tsx +122 -0
- package/src/components/tools/run-terminal-command.tsx +79 -0
- package/src/components/tools/skill.tsx +29 -0
- package/src/components/tools/str-replace.tsx +77 -0
- package/src/components/tools/suggest-followups.tsx +369 -0
- package/src/components/tools/task-completed.tsx +16 -0
- package/src/components/tools/tool-call-item.tsx +273 -0
- package/src/components/tools/types.ts +55 -0
- package/src/components/tools/web-search.tsx +33 -0
- package/src/components/tools/write-file.tsx +16 -0
- package/src/components/tools/write-todos.tsx +95 -0
- package/src/components/top-banner.tsx +178 -0
- package/src/components/usage-banner.tsx +244 -0
- package/src/components/user-error-banner.tsx +56 -0
- package/src/components/validation-error-popover.tsx +192 -0
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +149 -0
- package/src/contexts/chat-runtime-context.tsx +240 -0
- package/src/data/slash-commands.test.ts +24 -0
- package/src/data/slash-commands.ts +249 -0
- package/src/entry.ts +12 -0
- package/src/hooks/__tests__/holds-live-freebuff-slot.test.ts +49 -0
- package/src/hooks/__tests__/session-fetch-signal.test.ts +63 -0
- package/src/hooks/__tests__/use-activity-query.test.ts +1000 -0
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +176 -0
- package/src/hooks/__tests__/use-auth-query.test.ts +21 -0
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +66 -0
- package/src/hooks/__tests__/use-clipboard.test.tsx +73 -0
- package/src/hooks/__tests__/use-connection-status.test.ts +112 -0
- package/src/hooks/__tests__/use-directory-browser.test.ts +203 -0
- package/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
- package/src/hooks/__tests__/use-grid-layout.test.ts +349 -0
- package/src/hooks/__tests__/use-input-history.test.ts +699 -0
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +209 -0
- package/src/hooks/__tests__/use-message-queue.test.tsx +71 -0
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +315 -0
- package/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
- package/src/hooks/__tests__/use-queue-ui.test.tsx +83 -0
- package/src/hooks/__tests__/use-searchable-list.test.ts +359 -0
- package/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +361 -0
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
- package/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
- package/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
- package/src/hooks/__tests__/use-timeout.test.ts +330 -0
- package/src/hooks/__tests__/use-usage-query.test.ts +502 -0
- package/src/hooks/__tests__/use-user-details-query.test.ts +195 -0
- package/src/hooks/helpers/__tests__/send-message.test.ts +1918 -0
- package/src/hooks/helpers/send-message.ts +654 -0
- package/src/hooks/stream-state.ts +83 -0
- package/src/hooks/use-activity-query.ts +668 -0
- package/src/hooks/use-agent-validation.ts +74 -0
- package/src/hooks/use-ask-user-bridge.ts +92 -0
- package/src/hooks/use-auth-query.ts +253 -0
- package/src/hooks/use-auth-state.ts +152 -0
- package/src/hooks/use-chat-input.ts +105 -0
- package/src/hooks/use-chat-keyboard.ts +321 -0
- package/src/hooks/use-chat-messages.ts +254 -0
- package/src/hooks/use-chat-state.ts +165 -0
- package/src/hooks/use-chat-streaming.ts +180 -0
- package/src/hooks/use-chat-ui.ts +131 -0
- package/src/hooks/use-clipboard.ts +151 -0
- package/src/hooks/use-connection-status.ts +142 -0
- package/src/hooks/use-directory-browser.ts +88 -0
- package/src/hooks/use-elapsed-time.ts +116 -0
- package/src/hooks/use-event.ts +34 -0
- package/src/hooks/use-exit-handler.ts +66 -0
- package/src/hooks/use-fetch-login-url.ts +65 -0
- package/src/hooks/use-fingerprint.ts +61 -0
- package/src/hooks/use-freebuff-ctrl-c-exit.ts +23 -0
- package/src/hooks/use-freebuff-session-progress.ts +34 -0
- package/src/hooks/use-freebuff-session.ts +806 -0
- package/src/hooks/use-freebuff-streak-query.ts +61 -0
- package/src/hooks/use-gravity-ad.ts +656 -0
- package/src/hooks/use-grid-layout.ts +73 -0
- package/src/hooks/use-input-history.ts +173 -0
- package/src/hooks/use-login-keyboard-handlers.ts +75 -0
- package/src/hooks/use-login-polling.ts +114 -0
- package/src/hooks/use-logo.tsx +161 -0
- package/src/hooks/use-message-queue.ts +387 -0
- package/src/hooks/use-now.ts +20 -0
- package/src/hooks/use-path-tab-completion.ts +88 -0
- package/src/hooks/use-publish-mutation.ts +60 -0
- package/src/hooks/use-queue-controls.ts +68 -0
- package/src/hooks/use-queue-ui.ts +74 -0
- package/src/hooks/use-scroll-management.ts +184 -0
- package/src/hooks/use-searchable-list.ts +98 -0
- package/src/hooks/use-send-message.ts +773 -0
- package/src/hooks/use-sheen-animation.tsx +88 -0
- package/src/hooks/use-subscription-query.ts +71 -0
- package/src/hooks/use-suggestion-engine.ts +783 -0
- package/src/hooks/use-terminal-breakpoints.ts +77 -0
- package/src/hooks/use-terminal-dimensions.ts +46 -0
- package/src/hooks/use-terminal-focus.ts +29 -0
- package/src/hooks/use-terminal-layout.ts +189 -0
- package/src/hooks/use-theme.tsx +140 -0
- package/src/hooks/use-timeout.ts +85 -0
- package/src/hooks/use-update-preference.ts +66 -0
- package/src/hooks/use-usage-monitor.ts +55 -0
- package/src/hooks/use-usage-query.ts +124 -0
- package/src/hooks/use-user-details-query.ts +93 -0
- package/src/hooks/use-why-did-you-update.ts +177 -0
- package/src/index.tsx +441 -0
- package/src/init/__tests__/init-direnv.test.ts +526 -0
- package/src/init/init-app.ts +34 -0
- package/src/init/init-direnv.ts +133 -0
- package/src/login/constants.ts +60 -0
- package/src/login/login-flow.ts +204 -0
- package/src/login/plain-login.ts +108 -0
- package/src/login/utils.ts +183 -0
- package/src/native/ripgrep.ts +80 -0
- package/src/polyfills/bun-strip-ansi.ts +12 -0
- package/src/pre-init/tree-sitter-wasm.ts +96 -0
- package/src/project-files.ts +102 -0
- package/src/smoke/terminal-command-broker.ts +439 -0
- package/src/state/__tests__/chat-store-focus.test.ts +22 -0
- package/src/state/__tests__/feedback-store.test.ts +257 -0
- package/src/state/chat-history-store.ts +30 -0
- package/src/state/chat-store.ts +544 -0
- package/src/state/feedback-store.ts +160 -0
- package/src/state/freebuff-model-store.ts +42 -0
- package/src/state/freebuff-session-store.ts +53 -0
- package/src/state/login-store.ts +150 -0
- package/src/state/message-block-store.ts +149 -0
- package/src/state/publish-store.ts +146 -0
- package/src/state/queue-panel-store.ts +26 -0
- package/src/state/review-store.ts +24 -0
- package/src/testing/env.ts +46 -0
- package/src/types/chat-state.ts +15 -0
- package/src/types/chat.ts +241 -0
- package/src/types/contracts/send-message.ts +12 -0
- package/src/types/env.ts +98 -0
- package/src/types/freebuff-session.ts +17 -0
- package/src/types/function-params.ts +28 -0
- package/src/types/react19-compat.d.ts +19 -0
- package/src/types/store.ts +111 -0
- package/src/types/theme-system.ts +144 -0
- package/src/types/utils.ts +3 -0
- package/src/utils/__tests__/active-run.test.ts +140 -0
- package/src/utils/__tests__/activity-tracker.test.ts +440 -0
- package/src/utils/__tests__/agent-display.test.ts +139 -0
- package/src/utils/__tests__/analytics-client.test.ts +262 -0
- package/src/utils/__tests__/anonymous-id.test.ts +74 -0
- package/src/utils/__tests__/anvil-api.test.ts +578 -0
- package/src/utils/__tests__/arrays.test.ts +264 -0
- package/src/utils/__tests__/bash-context-processor.test.ts +38 -0
- package/src/utils/__tests__/block-processor.test.ts +810 -0
- package/src/utils/__tests__/chat-history.test.ts +203 -0
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
- package/src/utils/__tests__/chat-meta.test.ts +130 -0
- package/src/utils/__tests__/clipboard-linux.test.ts +359 -0
- package/src/utils/__tests__/clipboard.test.ts +910 -0
- package/src/utils/__tests__/code-search-summary.test.ts +84 -0
- package/src/utils/__tests__/collapse-helpers.test.ts +1109 -0
- package/src/utils/__tests__/error-handling.test.ts +658 -0
- package/src/utils/__tests__/exit-cleanly.test.ts +110 -0
- package/src/utils/__tests__/feedback-helpers.test.ts +444 -0
- package/src/utils/__tests__/feedback-submission.test.ts +26 -0
- package/src/utils/__tests__/fetch-usage.test.ts +355 -0
- package/src/utils/__tests__/fingerprint.test.ts +144 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +7 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +3 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +33 -0
- package/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
- package/src/utils/__tests__/format-timeout.test.ts +87 -0
- package/src/utils/__tests__/freebuff-instance-owner.test.ts +69 -0
- package/src/utils/__tests__/freebuff-model-navigation.test.ts +102 -0
- package/src/utils/__tests__/freebuff-premium-reset.test.ts +79 -0
- package/src/utils/__tests__/freebuff-referral-cache.test.ts +116 -0
- package/src/utils/__tests__/freebuff-session-api.test.ts +215 -0
- package/src/utils/__tests__/freebuff-session-display.test.ts +23 -0
- package/src/utils/__tests__/freebuff-streak-line.test.ts +217 -0
- package/src/utils/__tests__/image-dimensions.test.ts +249 -0
- package/src/utils/__tests__/image-processor.test.ts +209 -0
- package/src/utils/__tests__/implementor-helpers.test.ts +1458 -0
- package/src/utils/__tests__/keyboard-actions.test.ts +660 -0
- package/src/utils/__tests__/layout-helpers.test.ts +36 -0
- package/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
- package/src/utils/__tests__/log-shipper.test.ts +53 -0
- package/src/utils/__tests__/markdown-renderer.test.tsx +421 -0
- package/src/utils/__tests__/message-block-helpers.test.ts +1263 -0
- package/src/utils/__tests__/message-updater.test.ts +645 -0
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
- package/src/utils/__tests__/pending-attachments.test.ts +277 -0
- package/src/utils/__tests__/polling-backoff.test.ts +51 -0
- package/src/utils/__tests__/queue-panel-actions.test.ts +148 -0
- package/src/utils/__tests__/run-state-storage.test.ts +919 -0
- package/src/utils/__tests__/safe-json.test.ts +77 -0
- package/src/utils/__tests__/sdk-event-handlers.test.ts +685 -0
- package/src/utils/__tests__/send-message-helpers.test.ts +1898 -0
- package/src/utils/__tests__/send-message-timer.test.ts +64 -0
- package/src/utils/__tests__/settings.test.ts +99 -0
- package/src/utils/__tests__/strings.test.ts +192 -0
- package/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
- package/src/utils/__tests__/terminal-command-broker.test.ts +528 -0
- package/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
- package/src/utils/__tests__/terminal-io.test.ts +137 -0
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +119 -0
- package/src/utils/__tests__/text-layout.test.ts +82 -0
- package/src/utils/__tests__/theme-platform-detection.test.ts +125 -0
- package/src/utils/__tests__/think-tag-parser.test.ts +159 -0
- package/src/utils/__tests__/trace-writer.test.ts +223 -0
- package/src/utils/__tests__/trim-chat-logs.test.ts +78 -0
- package/src/utils/__tests__/usage-banner-state.test.ts +298 -0
- package/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
- package/src/utils/__tests__/windows-terminal-health.test.ts +101 -0
- package/src/utils/__tests__/write-file-atomic.test.ts +127 -0
- package/src/utils/active-run.ts +131 -0
- package/src/utils/activity-tracker.ts +66 -0
- package/src/utils/agent-display.ts +87 -0
- package/src/utils/agent-helpers.ts +27 -0
- package/src/utils/agent-id-utils.ts +14 -0
- package/src/utils/ai-message-id.ts +12 -0
- package/src/utils/analytics.ts +361 -0
- package/src/utils/anonymous-id.ts +87 -0
- package/src/utils/anvil-api.ts +612 -0
- package/src/utils/anvil-client.ts +217 -0
- package/src/utils/arrays.ts +117 -0
- package/src/utils/auth.ts +258 -0
- package/src/utils/bash-context-processor.ts +47 -0
- package/src/utils/bash-messages.ts +108 -0
- package/src/utils/block-margins.ts +35 -0
- package/src/utils/block-operations.ts +523 -0
- package/src/utils/block-processor.ts +213 -0
- package/src/utils/chat-history.ts +253 -0
- package/src/utils/chat-input-key-intercept.ts +52 -0
- package/src/utils/chat-meta.ts +90 -0
- package/src/utils/chat-scroll-accel.ts +151 -0
- package/src/utils/clipboard-image.ts +588 -0
- package/src/utils/clipboard.ts +397 -0
- package/src/utils/code-search-summary.ts +70 -0
- package/src/utils/collapse-helpers.ts +256 -0
- package/src/utils/config-dir.ts +24 -0
- package/src/utils/constants.ts +190 -0
- package/src/utils/create-event-handler-state.ts +73 -0
- package/src/utils/create-run-config.ts +121 -0
- package/src/utils/detect-shell.ts +116 -0
- package/src/utils/directory-browser.ts +72 -0
- package/src/utils/engagement.ts +55 -0
- package/src/utils/env.ts +92 -0
- package/src/utils/error-handling.ts +255 -0
- package/src/utils/error-messages.ts +62 -0
- package/src/utils/exit-cleanly.ts +86 -0
- package/src/utils/feedback-helpers.ts +103 -0
- package/src/utils/feedback-submission.ts +22 -0
- package/src/utils/fetch-usage.ts +76 -0
- package/src/utils/fingerprint.ts +245 -0
- package/src/utils/format-elapsed-time.ts +33 -0
- package/src/utils/format-session-units.ts +6 -0
- package/src/utils/format-timeout.ts +28 -0
- package/src/utils/format-validation-errors-for-message.ts +84 -0
- package/src/utils/freebuff-agent-selection.ts +33 -0
- package/src/utils/freebuff-instance-owner.ts +66 -0
- package/src/utils/freebuff-model-navigation.ts +50 -0
- package/src/utils/freebuff-premium-reset.ts +55 -0
- package/src/utils/freebuff-referral-cache.ts +51 -0
- package/src/utils/freebuff-session-api.ts +243 -0
- package/src/utils/freebuff-session-display.ts +21 -0
- package/src/utils/freebuff-streak-line.ts +93 -0
- package/src/utils/git.ts +17 -0
- package/src/utils/helpers.ts +47 -0
- package/src/utils/image-display.ts +67 -0
- package/src/utils/image-handler.ts +325 -0
- package/src/utils/image-processor.ts +133 -0
- package/src/utils/image-thumbnail.ts +78 -0
- package/src/utils/implementor-helpers.ts +787 -0
- package/src/utils/input-modes.ts +174 -0
- package/src/utils/keyboard-actions.ts +392 -0
- package/src/utils/keypad-keys.ts +47 -0
- package/src/utils/layout-helpers.ts +33 -0
- package/src/utils/lazy-response-ads.ts +1 -0
- package/src/utils/local-agent-registry.ts +474 -0
- package/src/utils/log-shipper.ts +126 -0
- package/src/utils/logger.ts +327 -0
- package/src/utils/markdown-renderer.tsx +1090 -0
- package/src/utils/math.ts +3 -0
- package/src/utils/message-block-helpers.ts +684 -0
- package/src/utils/message-history.ts +143 -0
- package/src/utils/message-tree-utils.ts +67 -0
- package/src/utils/message-updater.ts +279 -0
- package/src/utils/open-file.ts +146 -0
- package/src/utils/open-url.ts +63 -0
- package/src/utils/path-completion.ts +111 -0
- package/src/utils/path-helpers.ts +38 -0
- package/src/utils/pending-attachments.ts +360 -0
- package/src/utils/polling-backoff.ts +59 -0
- package/src/utils/project-picker.ts +12 -0
- package/src/utils/queue-panel-actions.ts +70 -0
- package/src/utils/recent-projects.ts +156 -0
- package/src/utils/renderer-cleanup.ts +233 -0
- package/src/utils/response-ad-positions.ts +1 -0
- package/src/utils/run-state-storage.ts +603 -0
- package/src/utils/safe-json.ts +153 -0
- package/src/utils/sdk-event-handlers.ts +571 -0
- package/src/utils/send-message-helpers.ts +210 -0
- package/src/utils/send-message-timer.ts +110 -0
- package/src/utils/settings.ts +226 -0
- package/src/utils/skill-registry.ts +94 -0
- package/src/utils/spawn-agent-matcher.ts +55 -0
- package/src/utils/status-indicator-state.ts +107 -0
- package/src/utils/stream-chunk-processor.ts +57 -0
- package/src/utils/strings.ts +271 -0
- package/src/utils/subscription.ts +31 -0
- package/src/utils/syntax-highlighter.tsx +19 -0
- package/src/utils/terminal-color-detection.ts +474 -0
- package/src/utils/terminal-command-broker.ts +467 -0
- package/src/utils/terminal-enter-detection.ts +63 -0
- package/src/utils/terminal-images.ts +229 -0
- package/src/utils/terminal-io.ts +85 -0
- package/src/utils/terminal-protocol-controller.ts +173 -0
- package/src/utils/terminal-reset-sequences.ts +33 -0
- package/src/utils/terminal-title.ts +84 -0
- package/src/utils/terminal-watchdog.ts +366 -0
- package/src/utils/text-layout.ts +149 -0
- package/src/utils/theme-config.ts +141 -0
- package/src/utils/theme-system.ts +1206 -0
- package/src/utils/think-tag-parser.ts +95 -0
- package/src/utils/time-format.ts +21 -0
- package/src/utils/trace-writer.ts +162 -0
- package/src/utils/ui-constants.ts +69 -0
- package/src/utils/update-checker.ts +110 -0
- package/src/utils/usage-banner-state.ts +146 -0
- package/src/utils/validation-error-formatting.ts +86 -0
- package/src/utils/validation-error-helpers.ts +19 -0
- package/src/utils/windows-terminal-health.ts +124 -0
- package/src/utils/word-wrap-utils.ts +53 -0
- package/src/utils/write-file-atomic.ts +54 -0
- package/src/utils/yield-to-event-loop.ts +9 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPostHogClient,
|
|
3
|
+
type AnalyticsClientWithIdentify,
|
|
4
|
+
type PostHogClientOptions,
|
|
5
|
+
} from '@anvil/common/analytics-core'
|
|
6
|
+
import {
|
|
7
|
+
env as defaultEnv,
|
|
8
|
+
IS_PROD as defaultIsProd,
|
|
9
|
+
DEBUG_ANALYTICS,
|
|
10
|
+
} from '@anvil/common/env'
|
|
11
|
+
import { shouldTrackAnalyticsEvent } from '@anvil/common/util/analytics-sampling'
|
|
12
|
+
import { shouldMirrorAnalyticsEvent } from '@anvil/common/util/log-mirror'
|
|
13
|
+
|
|
14
|
+
import { getOrCreatePersistentAnonymousId } from './anonymous-id'
|
|
15
|
+
import { enqueueClientLog as defaultEnqueueClientLog } from './log-shipper'
|
|
16
|
+
|
|
17
|
+
import { AnalyticsEvent } from '@anvil/common/constants/analytics-events'
|
|
18
|
+
|
|
19
|
+
import type { LogRecordInput } from '@anvil/common/schemas/logs'
|
|
20
|
+
|
|
21
|
+
// Re-export types from core for backwards compatibility
|
|
22
|
+
export type { AnalyticsClientWithIdentify as AnalyticsClient } from '@anvil/common/analytics-core'
|
|
23
|
+
|
|
24
|
+
export enum AnalyticsErrorStage {
|
|
25
|
+
Init = 'init',
|
|
26
|
+
Track = 'track',
|
|
27
|
+
Identify = 'identify',
|
|
28
|
+
Flush = 'flush',
|
|
29
|
+
CaptureException = 'captureException',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type AnalyticsErrorContext = {
|
|
33
|
+
stage: AnalyticsErrorStage
|
|
34
|
+
} & Record<string, unknown>
|
|
35
|
+
|
|
36
|
+
type AnalyticsErrorLogger = (
|
|
37
|
+
error: unknown,
|
|
38
|
+
context: AnalyticsErrorContext,
|
|
39
|
+
) => void
|
|
40
|
+
|
|
41
|
+
type ResolvedAnalyticsDeps = {
|
|
42
|
+
env: AnalyticsDeps['env']
|
|
43
|
+
isProd: boolean
|
|
44
|
+
createClient: AnalyticsDeps['createClient']
|
|
45
|
+
generateAnonymousId: NonNullable<AnalyticsDeps['generateAnonymousId']>
|
|
46
|
+
enqueueClientLog: NonNullable<AnalyticsDeps['enqueueClientLog']>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Dependencies that can be injected for testing */
|
|
50
|
+
export interface AnalyticsDeps {
|
|
51
|
+
env: {
|
|
52
|
+
NEXT_PUBLIC_POSTHOG_API_KEY?: string
|
|
53
|
+
NEXT_PUBLIC_POSTHOG_HOST_URL?: string
|
|
54
|
+
}
|
|
55
|
+
isProd: boolean
|
|
56
|
+
createClient: (
|
|
57
|
+
apiKey: string,
|
|
58
|
+
options: PostHogClientOptions,
|
|
59
|
+
) => AnalyticsClientWithIdentify
|
|
60
|
+
generateAnonymousId?: () => string
|
|
61
|
+
enqueueClientLog?: (record: LogRecordInput) => void
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Anonymous ID used before user identification (for PostHog alias)
|
|
65
|
+
let anonymousId: string | undefined
|
|
66
|
+
// Real user ID after identification
|
|
67
|
+
let currentUserId: string | undefined
|
|
68
|
+
let client: AnalyticsClientWithIdentify | undefined
|
|
69
|
+
let initializationState: 'not_started' | 'ready' | 'failed' = 'not_started'
|
|
70
|
+
|
|
71
|
+
// Store injected dependencies (for testing)
|
|
72
|
+
let injectedDeps: AnalyticsDeps | undefined
|
|
73
|
+
|
|
74
|
+
function resolveDeps(): ResolvedAnalyticsDeps {
|
|
75
|
+
return {
|
|
76
|
+
env: injectedDeps?.env ?? defaultEnv,
|
|
77
|
+
isProd: injectedDeps?.isProd ?? defaultIsProd,
|
|
78
|
+
createClient: injectedDeps?.createClient ?? createPostHogClient,
|
|
79
|
+
generateAnonymousId:
|
|
80
|
+
injectedDeps?.generateAnonymousId ?? getOrCreatePersistentAnonymousId,
|
|
81
|
+
enqueueClientLog: injectedDeps?.enqueueClientLog ?? defaultEnqueueClientLog,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let loggerModulePromise: Promise<{
|
|
86
|
+
logger: { debug: (data: any, msg?: string, ...args: any[]) => void }
|
|
87
|
+
}> | null = null
|
|
88
|
+
|
|
89
|
+
const loadLogger = () => {
|
|
90
|
+
if (!loggerModulePromise) {
|
|
91
|
+
loggerModulePromise = import('./logger')
|
|
92
|
+
}
|
|
93
|
+
return loggerModulePromise
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function logAnalyticsDebug(message: string, data: Record<string, unknown>) {
|
|
97
|
+
if (!DEBUG_ANALYTICS) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
loadLogger()
|
|
101
|
+
.then(({ logger }) => {
|
|
102
|
+
logger.debug(data, message)
|
|
103
|
+
})
|
|
104
|
+
.catch((error) => {
|
|
105
|
+
try {
|
|
106
|
+
console.debug(message, data)
|
|
107
|
+
} catch {
|
|
108
|
+
// Ignore console errors in restricted environments
|
|
109
|
+
}
|
|
110
|
+
// Log the error to help diagnose logger issues in debug mode
|
|
111
|
+
console.debug('Failed to load logger for analytics:', error)
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Get current distinct ID (real user ID if identified, otherwise anonymous ID) */
|
|
116
|
+
function getDistinctId(): string | undefined {
|
|
117
|
+
return currentUserId ?? anonymousId
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Reset analytics state - for testing only */
|
|
121
|
+
export function resetAnalyticsState(deps?: AnalyticsDeps) {
|
|
122
|
+
anonymousId = undefined
|
|
123
|
+
currentUserId = undefined
|
|
124
|
+
client = undefined
|
|
125
|
+
initializationState = 'not_started'
|
|
126
|
+
injectedDeps = deps
|
|
127
|
+
identified = false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export let identified: boolean = false
|
|
131
|
+
let analyticsErrorLogger: AnalyticsErrorLogger | undefined
|
|
132
|
+
|
|
133
|
+
export function setAnalyticsErrorLogger(loggerFn: AnalyticsErrorLogger) {
|
|
134
|
+
analyticsErrorLogger = loggerFn
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function logAnalyticsError(error: unknown, context: AnalyticsErrorContext) {
|
|
138
|
+
try {
|
|
139
|
+
analyticsErrorLogger?.(error, context)
|
|
140
|
+
} catch {
|
|
141
|
+
// Never throw from error reporting
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function initAnalytics() {
|
|
146
|
+
const { env, isProd, createClient, generateAnonymousId } = resolveDeps()
|
|
147
|
+
client = undefined
|
|
148
|
+
|
|
149
|
+
if (!env.NEXT_PUBLIC_POSTHOG_API_KEY || !env.NEXT_PUBLIC_POSTHOG_HOST_URL) {
|
|
150
|
+
initializationState = 'failed'
|
|
151
|
+
const error = new Error(
|
|
152
|
+
'NEXT_PUBLIC_POSTHOG_API_KEY or NEXT_PUBLIC_POSTHOG_HOST_URL is not set',
|
|
153
|
+
)
|
|
154
|
+
logAnalyticsError(error, {
|
|
155
|
+
stage: AnalyticsErrorStage.Init,
|
|
156
|
+
missingEnv: true,
|
|
157
|
+
})
|
|
158
|
+
throw error
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Generate anonymous ID for pre-login tracking
|
|
162
|
+
// PostHog will merge this with the real user ID via alias() when user logs in
|
|
163
|
+
anonymousId = generateAnonymousId()
|
|
164
|
+
identified = false
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
client = createClient(env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
|
168
|
+
host: env.NEXT_PUBLIC_POSTHOG_HOST_URL,
|
|
169
|
+
enableExceptionAutocapture: isProd,
|
|
170
|
+
})
|
|
171
|
+
initializationState = 'ready'
|
|
172
|
+
} catch (error) {
|
|
173
|
+
initializationState = 'failed'
|
|
174
|
+
logAnalyticsError(error, { stage: AnalyticsErrorStage.Init })
|
|
175
|
+
throw error
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function flushAnalytics() {
|
|
180
|
+
if (!client) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
await client.flush()
|
|
185
|
+
} catch (error) {
|
|
186
|
+
// Silently handle PostHog network errors - don't log to console or logger
|
|
187
|
+
// This prevents PostHog errors from cluttering the user's console
|
|
188
|
+
logAnalyticsError(error, { stage: AnalyticsErrorStage.Flush })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function trackEvent(
|
|
193
|
+
event: AnalyticsEvent,
|
|
194
|
+
properties?: Record<string, any>,
|
|
195
|
+
): boolean {
|
|
196
|
+
const { isProd, generateAnonymousId, enqueueClientLog } = resolveDeps()
|
|
197
|
+
let distinctId = getDistinctId()
|
|
198
|
+
|
|
199
|
+
if (!client) {
|
|
200
|
+
if (initializationState === 'failed') {
|
|
201
|
+
if (!distinctId) {
|
|
202
|
+
try {
|
|
203
|
+
anonymousId = generateAnonymousId()
|
|
204
|
+
distinctId = anonymousId
|
|
205
|
+
} catch {
|
|
206
|
+
return false
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} else if (isProd) {
|
|
210
|
+
const error = new Error('Analytics client not initialized')
|
|
211
|
+
logAnalyticsError(error, {
|
|
212
|
+
stage: AnalyticsErrorStage.Track,
|
|
213
|
+
event,
|
|
214
|
+
properties,
|
|
215
|
+
})
|
|
216
|
+
throw error
|
|
217
|
+
} else {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!distinctId) {
|
|
223
|
+
// This shouldn't happen if initAnalytics was called, but handle gracefully
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!isProd) {
|
|
228
|
+
if (DEBUG_ANALYTICS) {
|
|
229
|
+
logAnalyticsDebug(`[analytics] ${event}`, {
|
|
230
|
+
event,
|
|
231
|
+
properties,
|
|
232
|
+
distinctId,
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!shouldTrackAnalyticsEvent({ event, distinctId, properties })) {
|
|
239
|
+
return false
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (client) {
|
|
243
|
+
try {
|
|
244
|
+
client.capture({
|
|
245
|
+
distinctId,
|
|
246
|
+
event,
|
|
247
|
+
properties,
|
|
248
|
+
})
|
|
249
|
+
} catch (error) {
|
|
250
|
+
logAnalyticsError(error, {
|
|
251
|
+
stage: AnalyticsErrorStage.Track,
|
|
252
|
+
event,
|
|
253
|
+
properties,
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Mirror analytics events into the Axiom logs sink too (PostHog stays the
|
|
259
|
+
// product-analytics source of truth). The shipper batches and ships even
|
|
260
|
+
// before login (anonymously), so pre-auth events like app_launched reach
|
|
261
|
+
// Axiom — making install→login funnels queryable in APL. We correlate on the
|
|
262
|
+
// anonymous/run id so pre- and post-login events join. CLI_LOG is excluded
|
|
263
|
+
// because the logger already mirrors log rows to Axiom (avoids double-ship).
|
|
264
|
+
if (event !== AnalyticsEvent.CLI_LOG && shouldMirrorAnalyticsEvent(event)) {
|
|
265
|
+
try {
|
|
266
|
+
enqueueClientLog({
|
|
267
|
+
level: 'info',
|
|
268
|
+
event,
|
|
269
|
+
message: event,
|
|
270
|
+
client_session_id: anonymousId ?? currentUserId,
|
|
271
|
+
data: properties,
|
|
272
|
+
})
|
|
273
|
+
return true
|
|
274
|
+
} catch {
|
|
275
|
+
// Best-effort mirror; never let it affect analytics or the app.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function identifyUser(userId: string, properties?: Record<string, any>) {
|
|
282
|
+
if (!client) {
|
|
283
|
+
if (initializationState === 'failed') {
|
|
284
|
+
currentUserId = userId
|
|
285
|
+
identified = true
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
const error = new Error('Analytics client not initialized')
|
|
289
|
+
logAnalyticsError(error, {
|
|
290
|
+
stage: AnalyticsErrorStage.Identify,
|
|
291
|
+
properties,
|
|
292
|
+
})
|
|
293
|
+
throw error
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const { isProd } = resolveDeps()
|
|
297
|
+
const previousAnonymousId = anonymousId
|
|
298
|
+
|
|
299
|
+
// Store the real user ID for future events
|
|
300
|
+
currentUserId = userId
|
|
301
|
+
identified = true
|
|
302
|
+
|
|
303
|
+
if (!isProd) {
|
|
304
|
+
if (DEBUG_ANALYTICS) {
|
|
305
|
+
logAnalyticsDebug('[analytics] user identified', {
|
|
306
|
+
userId,
|
|
307
|
+
previousAnonymousId,
|
|
308
|
+
properties,
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
// If we had an anonymous ID, alias it FIRST to the real user ID
|
|
316
|
+
// This must be called BEFORE identify to properly merge the event histories
|
|
317
|
+
// See: https://posthog.com/docs/libraries/node
|
|
318
|
+
if (previousAnonymousId) {
|
|
319
|
+
client.alias({
|
|
320
|
+
distinctId: userId,
|
|
321
|
+
alias: previousAnonymousId,
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Then identify the user with their properties
|
|
326
|
+
client.identify({
|
|
327
|
+
distinctId: userId,
|
|
328
|
+
properties,
|
|
329
|
+
})
|
|
330
|
+
} catch (error) {
|
|
331
|
+
logAnalyticsError(error, {
|
|
332
|
+
stage: AnalyticsErrorStage.Identify,
|
|
333
|
+
properties,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function logError(
|
|
339
|
+
error: any,
|
|
340
|
+
userId?: string,
|
|
341
|
+
properties?: Record<string, any>,
|
|
342
|
+
) {
|
|
343
|
+
if (!client) {
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
client.captureException(
|
|
349
|
+
error,
|
|
350
|
+
userId ?? currentUserId ?? 'unknown',
|
|
351
|
+
properties,
|
|
352
|
+
)
|
|
353
|
+
} catch (postHogError) {
|
|
354
|
+
// Silently handle PostHog errors - don't log them to console
|
|
355
|
+
// This prevents PostHog connection issues from cluttering the user's console
|
|
356
|
+
logAnalyticsError(postHogError, {
|
|
357
|
+
stage: AnalyticsErrorStage.CaptureException,
|
|
358
|
+
properties,
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
import { generateAnonymousId } from '@anvil/common/analytics-core'
|
|
5
|
+
|
|
6
|
+
import { getConfigDir } from './config-dir'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Persistent anonymous analytics id.
|
|
10
|
+
*
|
|
11
|
+
* Pre-login events (e.g. `cli.app_launched`) are captured under this id and
|
|
12
|
+
* later aliased to the real user id on login. If the id were regenerated every
|
|
13
|
+
* process start, each pre-login launch would become a distinct "person" in
|
|
14
|
+
* PostHog — only the launch where login happens gets aliased, and every prior
|
|
15
|
+
* launch is orphaned at the top of the funnel, inflating the apparent drop-off.
|
|
16
|
+
*
|
|
17
|
+
* Persisting it to the config dir means all of a user's pre-login launches
|
|
18
|
+
* collapse to a single person that aliases cleanly into their account.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately a per-install random UUID, NOT the hardware fingerprint from
|
|
21
|
+
* `fingerprint.ts`: the fingerprint is intentionally deterministic across
|
|
22
|
+
* reinstalls (for anti-abuse), so reusing it here would alias a fresh install
|
|
23
|
+
* into a previous user's PostHog person — cross-account identity bleed. Keep
|
|
24
|
+
* the two identities separate.
|
|
25
|
+
*/
|
|
26
|
+
const ANONYMOUS_ID_FILE = 'analytics-id.json'
|
|
27
|
+
|
|
28
|
+
let cachedAnonymousId: string | undefined
|
|
29
|
+
|
|
30
|
+
const getAnonymousIdPath = (): string =>
|
|
31
|
+
path.join(getConfigDir(), ANONYMOUS_ID_FILE)
|
|
32
|
+
|
|
33
|
+
function readPersistedAnonymousId(): string | undefined {
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(getAnonymousIdPath(), 'utf8')
|
|
36
|
+
const parsed = JSON.parse(raw) as { anonymousId?: unknown }
|
|
37
|
+
if (typeof parsed.anonymousId === 'string' && parsed.anonymousId.trim()) {
|
|
38
|
+
return parsed.anonymousId
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// Missing/corrupt file — fall through to mint a new one.
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function persistAnonymousId(anonymousId: string): void {
|
|
47
|
+
try {
|
|
48
|
+
const configDir = getConfigDir()
|
|
49
|
+
if (!fs.existsSync(configDir)) {
|
|
50
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
51
|
+
}
|
|
52
|
+
fs.writeFileSync(
|
|
53
|
+
getAnonymousIdPath(),
|
|
54
|
+
JSON.stringify({ anonymousId }, null, 2),
|
|
55
|
+
)
|
|
56
|
+
} catch {
|
|
57
|
+
// Best-effort persistence. If the disk is unwritable we still return an id
|
|
58
|
+
// for this process; it just won't survive across launches.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Return a stable anonymous id, reading it from disk (or minting + persisting
|
|
64
|
+
* one on first run). Cached for the process lifetime. Never throws — on any
|
|
65
|
+
* filesystem error it falls back to an in-memory id.
|
|
66
|
+
*/
|
|
67
|
+
export function getOrCreatePersistentAnonymousId(): string {
|
|
68
|
+
if (cachedAnonymousId) {
|
|
69
|
+
return cachedAnonymousId
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const existing = readPersistedAnonymousId()
|
|
73
|
+
if (existing) {
|
|
74
|
+
cachedAnonymousId = existing
|
|
75
|
+
return existing
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const minted = generateAnonymousId()
|
|
79
|
+
persistAnonymousId(minted)
|
|
80
|
+
cachedAnonymousId = minted
|
|
81
|
+
return minted
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Reset the in-memory cache — for testing only. */
|
|
85
|
+
export function resetAnonymousIdCache(): void {
|
|
86
|
+
cachedAnonymousId = undefined
|
|
87
|
+
}
|