@alumbwe/anvil 1.0.18 → 1.0.20
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 +28 -30
- package/bin/anvil +2 -0
- package/package.json +71 -75
- package/src/agents/bundled-agents.generated.d.ts +14 -0
- package/src/agents/bundled-agents.generated.ts +3760 -0
- package/src/app.tsx +399 -0
- package/src/chat.tsx +1698 -0
- package/src/cli-args.ts +134 -0
- package/src/commands/__tests__/anvil-command-aliases.test.ts +56 -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__/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__/anvil-landing-heading-row.test.tsx +91 -0
- package/src/components/__tests__/anvil-model-selector.test.tsx +519 -0
- package/src/components/__tests__/anvil-offer-invariants.test.ts +54 -0
- package/src/components/__tests__/anvil-takeover-prompt.test.tsx +212 -0
- package/src/components/__tests__/chat-input-bar.test.tsx +130 -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-active-session-summary.tsx +63 -0
- package/src/components/anvil-landing-screen.tsx +865 -0
- package/src/components/anvil-mark.tsx +52 -0
- package/src/components/anvil-model-selector.tsx +1087 -0
- package/src/components/anvil-referral-banner.tsx +604 -0
- package/src/components/anvil-superseded-screen.tsx +62 -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/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-anvil-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-anvil-ctrl-c-exit.ts +23 -0
- package/src/hooks/use-anvil-session-progress.ts +34 -0
- package/src/hooks/use-anvil-session.ts +806 -0
- package/src/hooks/use-anvil-streak-query.ts +61 -0
- package/src/hooks/use-ask-user-bridge.ts +92 -0
- package/src/hooks/use-auth-query.ts +262 -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-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 +473 -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/setup/key-setup.ts +295 -0
- package/src/setup/load-env.ts +106 -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/anvil-model-store.ts +42 -0
- package/src/state/anvil-session-store.ts +53 -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/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/types/anvil-session.ts +17 -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/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__/anvil-instance-owner.test.ts +69 -0
- package/src/utils/__tests__/anvil-model-navigation.test.ts +102 -0
- package/src/utils/__tests__/anvil-premium-reset.test.ts +79 -0
- package/src/utils/__tests__/anvil-referral-cache.test.ts +116 -0
- package/src/utils/__tests__/anvil-session-api.test.ts +215 -0
- package/src/utils/__tests__/anvil-session-display.test.ts +23 -0
- package/src/utils/__tests__/anvil-streak-line.test.ts +217 -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__/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-agent-selection.ts +33 -0
- package/src/utils/anvil-api.ts +612 -0
- package/src/utils/anvil-client.ts +217 -0
- package/src/utils/anvil-instance-owner.ts +66 -0
- package/src/utils/anvil-model-navigation.ts +50 -0
- package/src/utils/anvil-premium-reset.ts +55 -0
- package/src/utils/anvil-referral-cache.ts +51 -0
- package/src/utils/anvil-session-api.ts +243 -0
- package/src/utils/anvil-session-display.ts +21 -0
- package/src/utils/anvil-streak-line.ts +93 -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/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
- package/bin/anvil.cjs +0 -40
- package/dist/bundled.js +0 -123450
- package/dist/bundled.js.map +0 -827
- package/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/dist/vendor/ripgrep/arm64-linux/rg +0 -0
- package/dist/vendor/ripgrep/x64-darwin/rg +0 -0
- package/dist/vendor/ripgrep/x64-linux/rg +0 -0
- package/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// The label/dots/perk-note logic is shared with Anvil Desktop and lives in
|
|
2
|
+
// common; this module re-exports it and adds the terminal rendering and layout
|
|
3
|
+
// gating only the CLI needs.
|
|
4
|
+
export {
|
|
5
|
+
ANVIL_STREAK_WEEK,
|
|
6
|
+
getAnvilStreakBonusNote,
|
|
7
|
+
} from '@anvil/common/util/anvil-streak-line'
|
|
8
|
+
export type { AnvilStreakLine } from '@anvil/common/util/anvil-streak-line'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ANVIL_STREAK_WEEK,
|
|
12
|
+
getAnvilStreakBonusNote,
|
|
13
|
+
getAnvilStreakLine as getSharedAnvilStreakLine,
|
|
14
|
+
} from '@anvil/common/util/anvil-streak-line'
|
|
15
|
+
|
|
16
|
+
import type { AnvilStreakLine } from '@anvil/common/util/anvil-streak-line'
|
|
17
|
+
|
|
18
|
+
const ANVIL_STREAK_BONUS_MIN_HEIGHT = 30
|
|
19
|
+
|
|
20
|
+
/** Columns between the count label and its progress dots. */
|
|
21
|
+
export const ANVIL_STREAK_LABEL_GAP = 2
|
|
22
|
+
|
|
23
|
+
/** Columns kept clear between the heading and the streak when they share a
|
|
24
|
+
* row. The heading row is laid out space-between inside a shrink-to-fit
|
|
25
|
+
* column, so when the row is the widest child there is no free space to
|
|
26
|
+
* distribute and the two would otherwise render flush against each other
|
|
27
|
+
* ("Start coding for free18 day streak"). This is the floor, and the same
|
|
28
|
+
* number decides whether they may share a row at all. */
|
|
29
|
+
export const ANVIL_STREAK_INLINE_GAP = 3
|
|
30
|
+
|
|
31
|
+
/** Progress glyphs for a terminal — the same ●/○ pair the shared module and
|
|
32
|
+
* the desktop app use. Bullet and middle dot were tried here because U+25CF
|
|
33
|
+
* is missing from a few terminal fonts and lands as a tofu box, but • and ·
|
|
34
|
+
* differ only in size: at a glance a partial week and a full one look alike,
|
|
35
|
+
* which is the whole point of the row. Filled-vs-hollow reads instantly, and
|
|
36
|
+
* the CLI already bets on ●/○ for its agent status indicators, so a font that
|
|
37
|
+
* can't draw them is already visibly broken elsewhere.
|
|
38
|
+
*
|
|
39
|
+
* If a font ever fails these, █/░ (Block Elements) is the fallback pair: the
|
|
40
|
+
* ASCII logo and the progress bar are built from them, so anything that
|
|
41
|
+
* renders the CLI at all renders those. */
|
|
42
|
+
const TERMINAL_DOT_CHARS = { filled: '●', empty: '○' }
|
|
43
|
+
|
|
44
|
+
/** The streak line as the CLI draws it. */
|
|
45
|
+
export function getAnvilStreakLine(
|
|
46
|
+
streak: number,
|
|
47
|
+
): AnvilStreakLine | null {
|
|
48
|
+
return getSharedAnvilStreakLine(streak, TERMINAL_DOT_CHARS)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Rendered width of the streak, e.g. "18 day streak ●●●●●●●+". */
|
|
52
|
+
export function getAnvilStreakInlineWidth(line: AnvilStreakLine): number {
|
|
53
|
+
return line.label.length + ANVIL_STREAK_LABEL_GAP + line.dots.length
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** What a user with no streak yet is about to earn. The empty slot is measured
|
|
57
|
+
* against it so the row doesn't move on day one. */
|
|
58
|
+
const DAY_ONE_LINE = getAnvilStreakLine(1)!
|
|
59
|
+
|
|
60
|
+
/** Whether the heading and the streak can share a row with the inline gap left
|
|
61
|
+
* clear between them. A streak long enough to widen its own label (or a
|
|
62
|
+
* narrow terminal) pushes the streak onto its own line instead of letting the
|
|
63
|
+
* two collide. */
|
|
64
|
+
export function fitsAnvilStreakOnHeadingRow(params: {
|
|
65
|
+
/** null when the user has no streak yet — measured as day one. */
|
|
66
|
+
line: AnvilStreakLine | null
|
|
67
|
+
headingWidth: number
|
|
68
|
+
availableWidth: number
|
|
69
|
+
}): boolean {
|
|
70
|
+
return (
|
|
71
|
+
params.headingWidth +
|
|
72
|
+
ANVIL_STREAK_INLINE_GAP +
|
|
73
|
+
getAnvilStreakInlineWidth(params.line ?? DAY_ONE_LINE) <=
|
|
74
|
+
params.availableWidth
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Returns the earned perk note only when the landing layout can show it
|
|
79
|
+
* without crowding the picker or wrapping onto additional rows. */
|
|
80
|
+
export function getAnvilStreakBonusNoteForLayout(params: {
|
|
81
|
+
streak: number
|
|
82
|
+
accessTier: 'full' | 'limited'
|
|
83
|
+
terminalHeight: number
|
|
84
|
+
availableWidth: number
|
|
85
|
+
}): string | null {
|
|
86
|
+
if (params.streak < ANVIL_STREAK_WEEK) return null
|
|
87
|
+
if (params.terminalHeight < ANVIL_STREAK_BONUS_MIN_HEIGHT) return null
|
|
88
|
+
|
|
89
|
+
const note = getAnvilStreakBonusNote(params)
|
|
90
|
+
if (!note || note.length > params.availableWidth) return null
|
|
91
|
+
|
|
92
|
+
return note
|
|
93
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** Python-style range function */
|
|
2
|
+
export function* range(start: number, stop?: number, step: number = 1) {
|
|
3
|
+
if (stop === undefined) {
|
|
4
|
+
stop = start
|
|
5
|
+
start = 0
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (step === 0) {
|
|
9
|
+
throw new Error('Step cannot be zero')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (step > 0) {
|
|
13
|
+
for (let i = start; i < stop; i += step) {
|
|
14
|
+
yield i
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
for (let i = start; i > stop; i += step) {
|
|
18
|
+
yield i
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function repeated<T>(value: T, count: number): T[] {
|
|
24
|
+
return Array.from({ length: count }, () => value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class Queue<T> {
|
|
28
|
+
private _items: T[]
|
|
29
|
+
private _head: number
|
|
30
|
+
private _length: number
|
|
31
|
+
private _defaultCapacity: number
|
|
32
|
+
|
|
33
|
+
constructor(items: T[] | undefined = undefined, capacity: number = 100) {
|
|
34
|
+
this._defaultCapacity = capacity
|
|
35
|
+
if (!items) {
|
|
36
|
+
items = []
|
|
37
|
+
}
|
|
38
|
+
this._items = items.length
|
|
39
|
+
? [
|
|
40
|
+
...items,
|
|
41
|
+
...repeated(undefined as T, Math.max(0, capacity - items.length)),
|
|
42
|
+
]
|
|
43
|
+
: [undefined as T]
|
|
44
|
+
this._head = 0
|
|
45
|
+
this._length = items.length
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static from<T>(iterable: Iterable<T>): Queue<T> {
|
|
49
|
+
return new Queue<T>([...iterable])
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
enqueue(...items: T[]) {
|
|
53
|
+
if (this._items.length < this._length + items.length) {
|
|
54
|
+
const newItems = [
|
|
55
|
+
...repeated(undefined as T, this._length),
|
|
56
|
+
...items,
|
|
57
|
+
...repeated(undefined as T, this._length),
|
|
58
|
+
]
|
|
59
|
+
for (let i = 0; i < this._length; i++) {
|
|
60
|
+
newItems[i] = this.at(i)!
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._items = newItems
|
|
64
|
+
this._head = 0
|
|
65
|
+
this._length += items.length
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let index = (this._head + this._length) % this._items.length
|
|
70
|
+
for (const item of items) {
|
|
71
|
+
this._items[index] = item
|
|
72
|
+
index = (index + 1) % this._items.length
|
|
73
|
+
}
|
|
74
|
+
this._length += items.length
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dequeue(): T | undefined {
|
|
78
|
+
if (this._length === 0) {
|
|
79
|
+
return undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const item = this._items[this._head]
|
|
83
|
+
this._items[this._head] = undefined as T
|
|
84
|
+
this._head = (this._head + 1) % this._items.length
|
|
85
|
+
this._length--
|
|
86
|
+
return item
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
peek(): T | undefined {
|
|
90
|
+
if (this._length === 0) {
|
|
91
|
+
return undefined
|
|
92
|
+
}
|
|
93
|
+
return this._items[this._head]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
at(index: number): T | undefined {
|
|
97
|
+
if (index >= this._length || index < -this.length) {
|
|
98
|
+
return undefined
|
|
99
|
+
}
|
|
100
|
+
if (index < 0) {
|
|
101
|
+
return this._items[
|
|
102
|
+
(this._head + this._length + index) % this._items.length
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
return this._items[(this._head + index) % this._items.length]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clear(): void {
|
|
109
|
+
this._items = repeated(undefined as T, this._defaultCapacity)
|
|
110
|
+
this._head = 0
|
|
111
|
+
this._length = 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get length() {
|
|
115
|
+
return this._length
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
import { getCiEnv } from '@anvil/common/env-ci'
|
|
5
|
+
import { getDirectDeepSeekApiKey } from '@anvil/common/constants/deepseek-direct'
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import { getApiClient, setApiClientAuthToken } from './anvil-api'
|
|
10
|
+
import { getConfigDir as getConfigDirBase } from './config-dir'
|
|
11
|
+
import { logger } from './logger'
|
|
12
|
+
|
|
13
|
+
import type { CiEnv } from '@anvil/common/types/contracts/env'
|
|
14
|
+
|
|
15
|
+
// User schema
|
|
16
|
+
const userSchema = z.object({
|
|
17
|
+
id: z.string().optional(),
|
|
18
|
+
name: z.string(),
|
|
19
|
+
email: z.string(),
|
|
20
|
+
authToken: z.string(),
|
|
21
|
+
fingerprintId: z.string().optional(),
|
|
22
|
+
fingerprintHash: z.string().optional(),
|
|
23
|
+
credits: z.number().optional(),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export type User = z.infer<typeof userSchema>
|
|
27
|
+
|
|
28
|
+
const credentialsSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
default: userSchema.optional(),
|
|
31
|
+
})
|
|
32
|
+
.catchall(z.unknown())
|
|
33
|
+
|
|
34
|
+
// Get the config directory path.
|
|
35
|
+
// Re-exported as a wrapper (rather than a bare `export ... from`) so existing
|
|
36
|
+
// tests can still `spyOn(authModule, 'getConfigDir')`.
|
|
37
|
+
export const getConfigDir = (): string => getConfigDirBase()
|
|
38
|
+
|
|
39
|
+
// Get the credentials file path
|
|
40
|
+
export const getCredentialsPath = (): string => {
|
|
41
|
+
return path.join(getConfigDir(), 'credentials.json')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse user from JSON string
|
|
46
|
+
*/
|
|
47
|
+
const userFromJson = (
|
|
48
|
+
json: string,
|
|
49
|
+
profileName: string = 'default',
|
|
50
|
+
): User | undefined => {
|
|
51
|
+
try {
|
|
52
|
+
const allCredentials = credentialsSchema.parse(JSON.parse(json))
|
|
53
|
+
const profile = allCredentials[profileName]
|
|
54
|
+
// Validate that the profile matches the user schema
|
|
55
|
+
const parsed = userSchema.safeParse(profile)
|
|
56
|
+
return parsed.success ? parsed.data : undefined
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error(
|
|
59
|
+
{
|
|
60
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
61
|
+
errorStack: error instanceof Error ? error.stack : undefined,
|
|
62
|
+
profileName,
|
|
63
|
+
},
|
|
64
|
+
'Error parsing user JSON',
|
|
65
|
+
)
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get user credentials from file system
|
|
72
|
+
* @returns User object or null if not found/authenticated
|
|
73
|
+
*/
|
|
74
|
+
export const getUserCredentials = (): User | null => {
|
|
75
|
+
const credentialsPath = getCredentialsPath()
|
|
76
|
+
|
|
77
|
+
// Read user credentials directly from file
|
|
78
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const credentialsFile = fs.readFileSync(credentialsPath, 'utf8')
|
|
84
|
+
const user = userFromJson(credentialsFile)
|
|
85
|
+
return user || null
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.error(
|
|
88
|
+
{
|
|
89
|
+
error: error instanceof Error ? error.message : String(error),
|
|
90
|
+
},
|
|
91
|
+
'Error reading credentials',
|
|
92
|
+
)
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type AuthTokenSource = 'credentials' | 'environment' | null
|
|
98
|
+
|
|
99
|
+
export interface AuthTokenDetails {
|
|
100
|
+
token?: string
|
|
101
|
+
source: AuthTokenSource
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the auth token and track where it came from.
|
|
106
|
+
*/
|
|
107
|
+
export const getAuthTokenDetails = (
|
|
108
|
+
ciEnv: CiEnv = getCiEnv(),
|
|
109
|
+
): AuthTokenDetails => {
|
|
110
|
+
const userCredentials = getUserCredentials()
|
|
111
|
+
if (userCredentials?.authToken) {
|
|
112
|
+
return { token: userCredentials.authToken, source: 'credentials' }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const envToken = ciEnv.ANVIL_API_KEY
|
|
116
|
+
if (envToken) {
|
|
117
|
+
return { token: envToken, source: 'environment' }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Direct-to-DeepSeek mode: the DeepSeek key doubles as the auth token so
|
|
121
|
+
// the CLI treats the user as authenticated without a Anvil backend.
|
|
122
|
+
const deepseekToken = getDirectDeepSeekApiKey()
|
|
123
|
+
if (deepseekToken) {
|
|
124
|
+
return { token: deepseekToken, source: 'environment' }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { source: null }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the auth token from user credentials or environment variable
|
|
132
|
+
*/
|
|
133
|
+
export const getAuthToken = (): string | undefined => {
|
|
134
|
+
return getAuthTokenDetails().token
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check if the user has authentication credentials (but doesn't validate them)
|
|
139
|
+
*/
|
|
140
|
+
export const hasAuthCredentials = (): boolean => {
|
|
141
|
+
return !!getAuthTokenDetails().token
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface AuthValidationResult {
|
|
145
|
+
authenticated: boolean
|
|
146
|
+
hasInvalidCredentials: boolean
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Read existing credentials file, returns empty object if missing/invalid.
|
|
151
|
+
*
|
|
152
|
+
* Drops `chatgptOAuth`, which the removed ChatGPT integration wrote. Both
|
|
153
|
+
* callers spread this result straight back over the file, so the dead key —
|
|
154
|
+
* an OAuth access and refresh token for the user's ChatGPT account — is
|
|
155
|
+
* cleaned up the next time we write for any reason. Doing it here rather than
|
|
156
|
+
* in a startup pass means no extra write and no new race with login/logout;
|
|
157
|
+
* nothing reads the key anymore, and with /connect gone the user has no way
|
|
158
|
+
* to clear it themselves.
|
|
159
|
+
*/
|
|
160
|
+
const readCredentialsFile = (): Record<string, unknown> => {
|
|
161
|
+
const credentialsPath = getCredentialsPath()
|
|
162
|
+
if (!fs.existsSync(credentialsPath)) return {}
|
|
163
|
+
try {
|
|
164
|
+
const { chatgptOAuth: _removedIntegration, ...rest } = JSON.parse(
|
|
165
|
+
fs.readFileSync(credentialsPath, 'utf8'),
|
|
166
|
+
)
|
|
167
|
+
return rest
|
|
168
|
+
} catch {
|
|
169
|
+
return {}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Save user credentials to file system.
|
|
175
|
+
*/
|
|
176
|
+
export const saveUserCredentials = (user: User): void => {
|
|
177
|
+
const configDir = getConfigDir()
|
|
178
|
+
const credentialsPath = getCredentialsPath()
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
if (!fs.existsSync(configDir)) {
|
|
182
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const updatedData = { ...readCredentialsFile(), default: user }
|
|
186
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(updatedData, null, 2))
|
|
187
|
+
} catch (error) {
|
|
188
|
+
logger.error(
|
|
189
|
+
{
|
|
190
|
+
error: error instanceof Error ? error.message : String(error),
|
|
191
|
+
},
|
|
192
|
+
'Error saving credentials',
|
|
193
|
+
)
|
|
194
|
+
throw error
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Clear user credentials from file system.
|
|
200
|
+
* Only removes the 'default' field, preserving other credentials.
|
|
201
|
+
*/
|
|
202
|
+
export const clearUserCredentials = (): void => {
|
|
203
|
+
const credentialsPath = getCredentialsPath()
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
if (!fs.existsSync(credentialsPath)) return
|
|
207
|
+
|
|
208
|
+
const { default: _, ...rest } = readCredentialsFile()
|
|
209
|
+
|
|
210
|
+
if (Object.keys(rest).length === 0) {
|
|
211
|
+
fs.unlinkSync(credentialsPath)
|
|
212
|
+
} else {
|
|
213
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(rest, null, 2))
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.error(
|
|
217
|
+
{
|
|
218
|
+
error: error instanceof Error ? error.message : String(error),
|
|
219
|
+
},
|
|
220
|
+
'Error clearing credentials',
|
|
221
|
+
)
|
|
222
|
+
throw error
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function logoutUser(): Promise<boolean> {
|
|
227
|
+
try {
|
|
228
|
+
const user = getUserCredentials()
|
|
229
|
+
if (user?.authToken) {
|
|
230
|
+
setApiClientAuthToken(user.authToken)
|
|
231
|
+
const apiClient = getApiClient()
|
|
232
|
+
try {
|
|
233
|
+
const response = await apiClient.logout({
|
|
234
|
+
userId: user.id,
|
|
235
|
+
fingerprintId: user.fingerprintId,
|
|
236
|
+
fingerprintHash: user.fingerprintHash,
|
|
237
|
+
})
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
logger.error(
|
|
240
|
+
{ status: response.status, error: response.error },
|
|
241
|
+
'Logout request failed',
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
logger.error(err, 'Logout request error')
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
logger.error(error, 'Unexpected error preparing logout')
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
clearUserCredentials()
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.debug({ error }, 'Failed to clear credentials during logout')
|
|
256
|
+
}
|
|
257
|
+
return true
|
|
258
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildBashHistoryMessages,
|
|
3
|
+
createRunTerminalToolResult,
|
|
4
|
+
formatBashContextForPrompt,
|
|
5
|
+
} from './bash-messages'
|
|
6
|
+
|
|
7
|
+
import type { PendingBashMessage } from '../types/store'
|
|
8
|
+
import type { ChatMessage } from '../types/chat'
|
|
9
|
+
|
|
10
|
+
// Turns pending bash executions into chat history messages and prompt context.
|
|
11
|
+
export const processBashContext = (
|
|
12
|
+
pendingBashMessages: PendingBashMessage[],
|
|
13
|
+
): {
|
|
14
|
+
bashMessages: ChatMessage[]
|
|
15
|
+
bashContextForPrompt: string
|
|
16
|
+
} => {
|
|
17
|
+
const bashContextForPrompt = formatBashContextForPrompt(pendingBashMessages)
|
|
18
|
+
const bashMessages: ChatMessage[] = []
|
|
19
|
+
|
|
20
|
+
for (const bash of pendingBashMessages) {
|
|
21
|
+
if (bash.addedToHistory) {
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const toolCallId = crypto.randomUUID()
|
|
26
|
+
const cwd = bash.cwd || process.cwd()
|
|
27
|
+
const toolResultOutput = createRunTerminalToolResult({
|
|
28
|
+
command: bash.command,
|
|
29
|
+
cwd,
|
|
30
|
+
stdout: bash.stdout || null,
|
|
31
|
+
stderr: bash.stderr || null,
|
|
32
|
+
exitCode: bash.exitCode,
|
|
33
|
+
})
|
|
34
|
+
const outputJson = JSON.stringify(toolResultOutput)
|
|
35
|
+
const { assistantMessage } = buildBashHistoryMessages({
|
|
36
|
+
command: bash.command,
|
|
37
|
+
cwd,
|
|
38
|
+
toolCallId,
|
|
39
|
+
output: outputJson,
|
|
40
|
+
isComplete: true,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
bashMessages.push(assistantMessage)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { bashMessages, bashContextForPrompt }
|
|
47
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { formatTimestamp } from './helpers'
|
|
2
|
+
|
|
3
|
+
import type { PendingBashMessage } from '../types/store'
|
|
4
|
+
import type { ChatMessage, ContentBlock } from '../types/chat'
|
|
5
|
+
import type { ToolResultOutput } from '@anvil/common/types/messages/content-part'
|
|
6
|
+
|
|
7
|
+
export function createRunTerminalToolResult(params: {
|
|
8
|
+
command: string
|
|
9
|
+
cwd: string
|
|
10
|
+
stdout: string | null
|
|
11
|
+
stderr: string | null
|
|
12
|
+
exitCode: number
|
|
13
|
+
errorMessage?: string
|
|
14
|
+
}): ToolResultOutput[] {
|
|
15
|
+
const { command, cwd, stdout, stderr, exitCode, errorMessage } = params
|
|
16
|
+
if (errorMessage) {
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
type: 'json' as const,
|
|
20
|
+
value: { command, startingCwd: cwd, errorMessage },
|
|
21
|
+
},
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
type: 'json' as const,
|
|
27
|
+
value: {
|
|
28
|
+
command,
|
|
29
|
+
startingCwd: cwd,
|
|
30
|
+
stdout: stdout || null,
|
|
31
|
+
stderr: stderr || null,
|
|
32
|
+
exitCode,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildBashHistoryMessages(params: {
|
|
39
|
+
command: string
|
|
40
|
+
cwd: string
|
|
41
|
+
toolCallId?: string
|
|
42
|
+
output?: string
|
|
43
|
+
isComplete?: boolean
|
|
44
|
+
}): {
|
|
45
|
+
assistantMessage: ChatMessage
|
|
46
|
+
toolCallId: string
|
|
47
|
+
} {
|
|
48
|
+
const { command, cwd, output = '...', isComplete = false } = params
|
|
49
|
+
const toolCallId = params.toolCallId ?? crypto.randomUUID()
|
|
50
|
+
|
|
51
|
+
const toolBlock: ContentBlock = {
|
|
52
|
+
type: 'tool',
|
|
53
|
+
toolName: 'run_terminal_command',
|
|
54
|
+
toolCallId,
|
|
55
|
+
input: { command },
|
|
56
|
+
output,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const assistantMessage: ChatMessage = {
|
|
60
|
+
id: `bash-result-${toolCallId}`,
|
|
61
|
+
variant: 'ai',
|
|
62
|
+
content: '',
|
|
63
|
+
blocks: [toolBlock],
|
|
64
|
+
timestamp: formatTimestamp(),
|
|
65
|
+
isComplete,
|
|
66
|
+
metadata: { bashCwd: cwd },
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { assistantMessage, toolCallId }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format pending bash messages as context to prepend to the user's prompt.
|
|
74
|
+
* This provides the LLM with information about commands the user ran manually.
|
|
75
|
+
* Only includes completed (non-running) commands.
|
|
76
|
+
*/
|
|
77
|
+
export function formatBashContextForPrompt(
|
|
78
|
+
pendingBashMessages: PendingBashMessage[],
|
|
79
|
+
): string {
|
|
80
|
+
// Only include completed commands
|
|
81
|
+
const completedCommands = pendingBashMessages.filter((msg) => !msg.isRunning)
|
|
82
|
+
|
|
83
|
+
if (completedCommands.length === 0) {
|
|
84
|
+
return ''
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const commandsContext = completedCommands
|
|
88
|
+
.map((bash) => {
|
|
89
|
+
const cwd = bash.cwd || process.cwd()
|
|
90
|
+
let result = `Command: ${bash.command}\nDirectory: ${cwd}\nExit code: ${bash.exitCode}`
|
|
91
|
+
if (bash.stdout) {
|
|
92
|
+
result += `\nStdout:\n${bash.stdout}`
|
|
93
|
+
}
|
|
94
|
+
if (bash.stderr) {
|
|
95
|
+
result += `\nStderr:\n${bash.stderr}`
|
|
96
|
+
}
|
|
97
|
+
return result
|
|
98
|
+
})
|
|
99
|
+
.join('\n\n---\n\n')
|
|
100
|
+
|
|
101
|
+
return `<user_terminal_commands>
|
|
102
|
+
The user ran the following terminal command(s) before this message:
|
|
103
|
+
|
|
104
|
+
${commandsContext}
|
|
105
|
+
</user_terminal_commands>
|
|
106
|
+
|
|
107
|
+
`
|
|
108
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ContentBlock, TextContentBlock } from '../types/chat'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Margin calculation result for a content block.
|
|
5
|
+
*/
|
|
6
|
+
export interface BlockMargins {
|
|
7
|
+
marginTop: number
|
|
8
|
+
marginBottom: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Extracts margins for a text block, suppressing top margin after tool/agent blocks. */
|
|
12
|
+
export function extractTextBlockMargins(
|
|
13
|
+
block: TextContentBlock,
|
|
14
|
+
prevBlock: ContentBlock | null,
|
|
15
|
+
): BlockMargins {
|
|
16
|
+
const prevBlockSuppressesMargin =
|
|
17
|
+
prevBlock !== null &&
|
|
18
|
+
(prevBlock.type === 'tool' || prevBlock.type === 'agent')
|
|
19
|
+
|
|
20
|
+
const marginTop = prevBlockSuppressesMargin ? 0 : (block.marginTop ?? 0)
|
|
21
|
+
const marginBottom = block.marginBottom ?? 0
|
|
22
|
+
|
|
23
|
+
return { marginTop, marginBottom }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Extracts margins for an HTML block using explicit values without context adjustments. */
|
|
27
|
+
export function extractHtmlBlockMargins(block: {
|
|
28
|
+
marginTop?: number
|
|
29
|
+
marginBottom?: number
|
|
30
|
+
}): BlockMargins {
|
|
31
|
+
return {
|
|
32
|
+
marginTop: block.marginTop ?? 0,
|
|
33
|
+
marginBottom: block.marginBottom ?? 0,
|
|
34
|
+
}
|
|
35
|
+
}
|