@alumbwe/anvil 1.0.20 → 1.0.21
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/package.json +6 -2
- package/src/agents/bundled-agents.generated.ts +4 -4
- package/src/chat.tsx +1703 -1698
- package/src/cli-args.ts +134 -134
- package/src/components/status-bar.tsx +331 -313
- package/src/hooks/helpers/send-message.ts +663 -654
- package/src/hooks/use-send-message.ts +806 -773
- package/src/index.tsx +473 -473
- package/src/state/chat-store.ts +553 -544
- package/src/utils/create-run-config.ts +121 -121
- package/src/utils/format-compact-number.ts +37 -0
- package/vendor/@anvil/common/package.json +27 -0
- package/vendor/@anvil/common/src/__tests__/agent-validation.test.ts +846 -0
- package/vendor/@anvil/common/src/__tests__/anvil-model-availability.test.ts +101 -0
- package/vendor/@anvil/common/src/__tests__/anvil-models.test.ts +1408 -0
- package/vendor/@anvil/common/src/__tests__/anvil-public-data-use-copy.test.ts +258 -0
- package/vendor/@anvil/common/src/__tests__/anvil-referral-tiers.test.ts +48 -0
- package/vendor/@anvil/common/src/__tests__/anvil-spend-ceilings.test.ts +282 -0
- package/vendor/@anvil/common/src/__tests__/anvil-trust.test.ts +526 -0
- package/vendor/@anvil/common/src/__tests__/deepseek-direct.test.ts +68 -0
- package/vendor/@anvil/common/src/__tests__/disposable-email.test.ts +132 -0
- package/vendor/@anvil/common/src/__tests__/dynamic-agent-template-schema.test.ts +422 -0
- package/vendor/@anvil/common/src/__tests__/env-ci.test.ts +167 -0
- package/vendor/@anvil/common/src/__tests__/env-process.test.ts +145 -0
- package/vendor/@anvil/common/src/__tests__/foreign-client-shipped-agents.test.ts +234 -0
- package/vendor/@anvil/common/src/__tests__/foreign-client-signals.test.ts +379 -0
- package/vendor/@anvil/common/src/__tests__/free-agents.test.ts +807 -0
- package/vendor/@anvil/common/src/__tests__/gravity-capi.test.ts +154 -0
- package/vendor/@anvil/common/src/__tests__/handlesteps-parsing.test.ts +246 -0
- package/vendor/@anvil/common/src/__tests__/kimi-k3-god-only.test.ts +77 -0
- package/vendor/@anvil/common/src/__tests__/model-config.test.ts +89 -0
- package/vendor/@anvil/common/src/__tests__/project-file-tree.test.ts +298 -0
- package/vendor/@anvil/common/src/__tests__/provisioned-model-tiers.test.ts +116 -0
- package/vendor/@anvil/common/src/__tests__/reasoning-effort.test.ts +167 -0
- package/vendor/@anvil/common/src/__tests__/reddit-capi.test.ts +132 -0
- package/vendor/@anvil/common/src/__tests__/response-ad-positions.test.ts +72 -0
- package/vendor/@anvil/common/src/__tests__/user-state.test.ts +30 -0
- package/vendor/@anvil/common/src/actions.ts +215 -0
- package/vendor/@anvil/common/src/analytics-core.ts +69 -0
- package/vendor/@anvil/common/src/analytics.ts +93 -0
- package/vendor/@anvil/common/src/api-keys/constants.ts +26 -0
- package/vendor/@anvil/common/src/browser-actions.ts +413 -0
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding-gate.test.ts +63 -0
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding.test.ts +246 -0
- package/vendor/@anvil/common/src/constants/__tests__/cf-worker-signals.test.ts +106 -0
- package/vendor/@anvil/common/src/constants/agents.ts +99 -0
- package/vendor/@anvil/common/src/constants/analytics-events.ts +435 -0
- package/vendor/@anvil/common/src/constants/anthropic.ts +73 -0
- package/vendor/@anvil/common/src/constants/anvil-data-use.ts +81 -0
- package/vendor/@anvil/common/src/constants/anvil-errors.ts +7 -0
- package/vendor/@anvil/common/src/constants/anvil-gemini-thinker.ts +21 -0
- package/vendor/@anvil/common/src/constants/anvil-model-ids.ts +11 -0
- package/vendor/@anvil/common/src/constants/anvil-models.ts +2130 -0
- package/vendor/@anvil/common/src/constants/anvil-onboarding-gate.ts +55 -0
- package/vendor/@anvil/common/src/constants/anvil-onboarding.ts +307 -0
- package/vendor/@anvil/common/src/constants/anvil-referral-tiers.ts +87 -0
- package/vendor/@anvil/common/src/constants/anvil-signup-block.ts +68 -0
- package/vendor/@anvil/common/src/constants/anvil-spend-ceilings.ts +450 -0
- package/vendor/@anvil/common/src/constants/anvil-trust.ts +976 -0
- package/vendor/@anvil/common/src/constants/auth.ts +18 -0
- package/vendor/@anvil/common/src/constants/byok.ts +2 -0
- package/vendor/@anvil/common/src/constants/cf-worker-signals.ts +132 -0
- package/vendor/@anvil/common/src/constants/composio.ts +34 -0
- package/vendor/@anvil/common/src/constants/deepseek-direct.ts +55 -0
- package/vendor/@anvil/common/src/constants/feedback.ts +13 -0
- package/vendor/@anvil/common/src/constants/foreign-client-signals.ts +272 -0
- package/vendor/@anvil/common/src/constants/free-agents.ts +777 -0
- package/vendor/@anvil/common/src/constants/gemini.ts +15 -0
- package/vendor/@anvil/common/src/constants/grant-priorities.ts +13 -0
- package/vendor/@anvil/common/src/constants/hosts.ts +6 -0
- package/vendor/@anvil/common/src/constants/images.ts +51 -0
- package/vendor/@anvil/common/src/constants/index.ts +7 -0
- package/vendor/@anvil/common/src/constants/knowledge.ts +35 -0
- package/vendor/@anvil/common/src/constants/limits.ts +23 -0
- package/vendor/@anvil/common/src/constants/model-config.ts +277 -0
- package/vendor/@anvil/common/src/constants/openrouter-attribution.ts +8 -0
- package/vendor/@anvil/common/src/constants/paths.ts +69 -0
- package/vendor/@anvil/common/src/constants/provider-routes.ts +317 -0
- package/vendor/@anvil/common/src/constants/reasoning-effort.ts +79 -0
- package/vendor/@anvil/common/src/constants/skills.ts +60 -0
- package/vendor/@anvil/common/src/constants/subscription-plans.ts +49 -0
- package/vendor/@anvil/common/src/constants/ui.ts +25 -0
- package/vendor/@anvil/common/src/env-ci.ts +36 -0
- package/vendor/@anvil/common/src/env-process.ts +95 -0
- package/vendor/@anvil/common/src/env-schema.ts +95 -0
- package/vendor/@anvil/common/src/env.ts +24 -0
- package/vendor/@anvil/common/src/gravity-capi.ts +207 -0
- package/vendor/@anvil/common/src/mcp/client.ts +233 -0
- package/vendor/@anvil/common/src/old-constants.ts +10 -0
- package/vendor/@anvil/common/src/project-file-tree.ts +355 -0
- package/vendor/@anvil/common/src/reddit-capi.ts +254 -0
- package/vendor/@anvil/common/src/schemas/feedback.ts +52 -0
- package/vendor/@anvil/common/src/schemas/logs.ts +66 -0
- package/vendor/@anvil/common/src/templates/agent-validation.ts +392 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/LICENSE +202 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/README.md +294 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/01-basic-diff-reviewer.ts +17 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/02-intermediate-git-committer.ts +78 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/03-advanced-file-explorer.ts +73 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/my-custom-agent.ts +40 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/package.json +6 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/README.md +65 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/example-skill/SKILL.md +29 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/agent-definition.ts +497 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/tools.ts +444 -0
- package/vendor/@anvil/common/src/templates/initial-agents-dir/types/util-types.ts +178 -0
- package/vendor/@anvil/common/src/testing/TESTING_PATTERNS.md +351 -0
- package/vendor/@anvil/common/src/testing/anvil-offer-invariants.ts +169 -0
- package/vendor/@anvil/common/src/testing/errors.ts +33 -0
- package/vendor/@anvil/common/src/testing/fixtures/agent-runtime.ts +334 -0
- package/vendor/@anvil/common/src/testing/impl/agent-runtime.ts +6 -0
- package/vendor/@anvil/common/src/testing/index.ts +84 -0
- package/vendor/@anvil/common/src/testing/mock-modules.ts +53 -0
- package/vendor/@anvil/common/src/testing/mock-types.ts +123 -0
- package/vendor/@anvil/common/src/testing/mocks/analytics.ts +261 -0
- package/vendor/@anvil/common/src/testing/mocks/child-process.ts +93 -0
- package/vendor/@anvil/common/src/testing/mocks/crypto.ts +218 -0
- package/vendor/@anvil/common/src/testing/mocks/database.ts +337 -0
- package/vendor/@anvil/common/src/testing/mocks/fetch.ts +219 -0
- package/vendor/@anvil/common/src/testing/mocks/filesystem.ts +166 -0
- package/vendor/@anvil/common/src/testing/mocks/index.ts +101 -0
- package/vendor/@anvil/common/src/testing/mocks/logger.ts +135 -0
- package/vendor/@anvil/common/src/testing/mocks/stream.ts +313 -0
- package/vendor/@anvil/common/src/testing/mocks/timers.ts +132 -0
- package/vendor/@anvil/common/src/testing/mocks/tree-sitter.ts +127 -0
- package/vendor/@anvil/common/src/testing/setup.ts +282 -0
- package/vendor/@anvil/common/src/testing-env-ci.ts +15 -0
- package/vendor/@anvil/common/src/testing-env-process.ts +78 -0
- package/vendor/@anvil/common/src/tools/__tests__/compile-tool-definitions.test.ts +34 -0
- package/vendor/@anvil/common/src/tools/compile-tool-definitions.ts +157 -0
- package/vendor/@anvil/common/src/tools/constants.ts +117 -0
- package/vendor/@anvil/common/src/tools/list.ts +190 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/coerce-to-array.test.ts +213 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/read-docs.test.ts +47 -0
- package/vendor/@anvil/common/src/tools/params/__tests__/render-ui.test.ts +65 -0
- package/vendor/@anvil/common/src/tools/params/tool/__tests__/run-terminal-command-timeout.test.ts +49 -0
- package/vendor/@anvil/common/src/tools/params/tool/add-message.ts +39 -0
- package/vendor/@anvil/common/src/tools/params/tool/add-subgoal.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/apply-patch.ts +110 -0
- package/vendor/@anvil/common/src/tools/params/tool/ask-user.ts +181 -0
- package/vendor/@anvil/common/src/tools/params/tool/browser-logs.ts +85 -0
- package/vendor/@anvil/common/src/tools/params/tool/cloud-plan-ready.ts +94 -0
- package/vendor/@anvil/common/src/tools/params/tool/code-search.ts +159 -0
- package/vendor/@anvil/common/src/tools/params/tool/composio.ts +131 -0
- package/vendor/@anvil/common/src/tools/params/tool/create-plan.ts +80 -0
- package/vendor/@anvil/common/src/tools/params/tool/end-turn.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/find-files.ts +60 -0
- package/vendor/@anvil/common/src/tools/params/tool/glob.ts +80 -0
- package/vendor/@anvil/common/src/tools/params/tool/gravity-index.ts +93 -0
- package/vendor/@anvil/common/src/tools/params/tool/list-directory.ts +58 -0
- package/vendor/@anvil/common/src/tools/params/tool/lookup-agent-info.ts +37 -0
- package/vendor/@anvil/common/src/tools/params/tool/propose-str-replace.ts +103 -0
- package/vendor/@anvil/common/src/tools/params/tool/propose-write-file.ts +71 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-docs.ts +90 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-files.ts +109 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-subtree.ts +79 -0
- package/vendor/@anvil/common/src/tools/params/tool/read-url.ts +81 -0
- package/vendor/@anvil/common/src/tools/params/tool/render-ui.ts +168 -0
- package/vendor/@anvil/common/src/tools/params/tool/run-file-change-hooks.ts +57 -0
- package/vendor/@anvil/common/src/tools/params/tool/run-terminal-command.ts +211 -0
- package/vendor/@anvil/common/src/tools/params/tool/screenshot.ts +97 -0
- package/vendor/@anvil/common/src/tools/params/tool/set-messages.ts +44 -0
- package/vendor/@anvil/common/src/tools/params/tool/set-output.ts +61 -0
- package/vendor/@anvil/common/src/tools/params/tool/skill.ts +59 -0
- package/vendor/@anvil/common/src/tools/params/tool/spawn-agent-inline.ts +56 -0
- package/vendor/@anvil/common/src/tools/params/tool/spawn-agents.ts +154 -0
- package/vendor/@anvil/common/src/tools/params/tool/str-replace.ts +107 -0
- package/vendor/@anvil/common/src/tools/params/tool/suggest-followups.ts +97 -0
- package/vendor/@anvil/common/src/tools/params/tool/task-completed.ts +61 -0
- package/vendor/@anvil/common/src/tools/params/tool/think-deeply.ts +56 -0
- package/vendor/@anvil/common/src/tools/params/tool/update-subgoal.ts +89 -0
- package/vendor/@anvil/common/src/tools/params/tool/vision-analyze.ts +82 -0
- package/vendor/@anvil/common/src/tools/params/tool/web-search.ts +73 -0
- package/vendor/@anvil/common/src/tools/params/tool/write-file.ts +71 -0
- package/vendor/@anvil/common/src/tools/params/tool/write-todos.ts +67 -0
- package/vendor/@anvil/common/src/tools/params/utils.ts +150 -0
- package/vendor/@anvil/common/src/tools/utils.ts +24 -0
- package/vendor/@anvil/common/src/types/__tests__/dynamic-agent-template.test.ts +20 -0
- package/vendor/@anvil/common/src/types/agent-template.ts +215 -0
- package/vendor/@anvil/common/src/types/anvil-session.ts +535 -0
- package/vendor/@anvil/common/src/types/anvil-streak.ts +6 -0
- package/vendor/@anvil/common/src/types/anvil-usage.ts +64 -0
- package/vendor/@anvil/common/src/types/api/agents/publish.ts +61 -0
- package/vendor/@anvil/common/src/types/bun-test.d.ts +5 -0
- package/vendor/@anvil/common/src/types/contracts/agent-runtime.ts +75 -0
- package/vendor/@anvil/common/src/types/contracts/analytics.ts +9 -0
- package/vendor/@anvil/common/src/types/contracts/bigquery.ts +55 -0
- package/vendor/@anvil/common/src/types/contracts/billing.ts +46 -0
- package/vendor/@anvil/common/src/types/contracts/client.ts +53 -0
- package/vendor/@anvil/common/src/types/contracts/database.ts +112 -0
- package/vendor/@anvil/common/src/types/contracts/env.ts +203 -0
- package/vendor/@anvil/common/src/types/contracts/llm.ts +191 -0
- package/vendor/@anvil/common/src/types/contracts/logger.ts +14 -0
- package/vendor/@anvil/common/src/types/contracts/logs.ts +34 -0
- package/vendor/@anvil/common/src/types/contracts/trace.ts +21 -0
- package/vendor/@anvil/common/src/types/dynamic-agent-template.ts +328 -0
- package/vendor/@anvil/common/src/types/filesystem.ts +10 -0
- package/vendor/@anvil/common/src/types/function-params.ts +33 -0
- package/vendor/@anvil/common/src/types/grant.ts +20 -0
- package/vendor/@anvil/common/src/types/gravity-index.ts +170 -0
- package/vendor/@anvil/common/src/types/json.ts +29 -0
- package/vendor/@anvil/common/src/types/mcp.ts +24 -0
- package/vendor/@anvil/common/src/types/messages/anvil-message.ts +58 -0
- package/vendor/@anvil/common/src/types/messages/content-part.ts +59 -0
- package/vendor/@anvil/common/src/types/messages/data-content.ts +14 -0
- package/vendor/@anvil/common/src/types/messages/provider-metadata.ts +13 -0
- package/vendor/@anvil/common/src/types/organization.ts +118 -0
- package/vendor/@anvil/common/src/types/print-mode.ts +121 -0
- package/vendor/@anvil/common/src/types/publisher.ts +67 -0
- package/vendor/@anvil/common/src/types/session-state.ts +149 -0
- package/vendor/@anvil/common/src/types/skill.ts +86 -0
- package/vendor/@anvil/common/src/types/source.ts +11 -0
- package/vendor/@anvil/common/src/types/spawn.ts +13 -0
- package/vendor/@anvil/common/src/types/subscription.ts +67 -0
- package/vendor/@anvil/common/src/types/usage.ts +16 -0
- package/vendor/@anvil/common/src/types/util.ts +3 -0
- package/vendor/@anvil/common/src/util/__tests__/ad-user-agent.test.ts +26 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-dispatcher.test.ts +122 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-log.test.ts +102 -0
- package/vendor/@anvil/common/src/util/__tests__/analytics-sampling.test.ts +143 -0
- package/vendor/@anvil/common/src/util/__tests__/anvil-streak.test.ts +202 -0
- package/vendor/@anvil/common/src/util/__tests__/anvil-usage-summary.test.ts +193 -0
- package/vendor/@anvil/common/src/util/__tests__/axiom-only-log.test.ts +140 -0
- package/vendor/@anvil/common/src/util/__tests__/client-user-agent.test.ts +148 -0
- package/vendor/@anvil/common/src/util/__tests__/engagement-tracker.test.ts +115 -0
- package/vendor/@anvil/common/src/util/__tests__/env-file-path.test.ts +57 -0
- package/vendor/@anvil/common/src/util/__tests__/error-abort.test.ts +774 -0
- package/vendor/@anvil/common/src/util/__tests__/error-api-details.test.ts +206 -0
- package/vendor/@anvil/common/src/util/__tests__/file-read-limits.test.ts +173 -0
- package/vendor/@anvil/common/src/util/__tests__/format-code-search.test.ts +60 -0
- package/vendor/@anvil/common/src/util/__tests__/log-mirror.test.ts +31 -0
- package/vendor/@anvil/common/src/util/__tests__/messages.test.ts +1252 -0
- package/vendor/@anvil/common/src/util/__tests__/partial-json-delta.test.ts +505 -0
- package/vendor/@anvil/common/src/util/__tests__/path.test.ts +22 -0
- package/vendor/@anvil/common/src/util/__tests__/project-ignore.test.ts +90 -0
- package/vendor/@anvil/common/src/util/__tests__/promise.test.ts +325 -0
- package/vendor/@anvil/common/src/util/__tests__/rate-limit.test.ts +28 -0
- package/vendor/@anvil/common/src/util/__tests__/reddit-anvil-retention.test.ts +130 -0
- package/vendor/@anvil/common/src/util/__tests__/saxy.test.ts +1008 -0
- package/vendor/@anvil/common/src/util/__tests__/split-data.test.ts +289 -0
- package/vendor/@anvil/common/src/util/__tests__/string.test.ts +239 -0
- package/vendor/@anvil/common/src/util/__tests__/thread-title.test.ts +70 -0
- package/vendor/@anvil/common/src/util/__tests__/tool-result-media-order.test.ts +108 -0
- package/vendor/@anvil/common/src/util/__tests__/ttft-histogram.test.ts +180 -0
- package/vendor/@anvil/common/src/util/__tests__/with-timeout.test.ts +24 -0
- package/vendor/@anvil/common/src/util/__tests__/zoned-time.test.ts +88 -0
- package/vendor/@anvil/common/src/util/ad-user-agent.ts +25 -0
- package/vendor/@anvil/common/src/util/agent-file-utils.ts +110 -0
- package/vendor/@anvil/common/src/util/agent-id-parsing.ts +136 -0
- package/vendor/@anvil/common/src/util/agent-name-normalization.ts +38 -0
- package/vendor/@anvil/common/src/util/agent-name-resolver.ts +86 -0
- package/vendor/@anvil/common/src/util/analytics-dispatcher.ts +82 -0
- package/vendor/@anvil/common/src/util/analytics-log.ts +78 -0
- package/vendor/@anvil/common/src/util/analytics-sampling.ts +255 -0
- package/vendor/@anvil/common/src/util/anvil-model-availability.ts +102 -0
- package/vendor/@anvil/common/src/util/anvil-privacy.ts +82 -0
- package/vendor/@anvil/common/src/util/anvil-streak-line.ts +103 -0
- package/vendor/@anvil/common/src/util/anvil-streak.ts +156 -0
- package/vendor/@anvil/common/src/util/anvil-usage-summary.ts +125 -0
- package/vendor/@anvil/common/src/util/array.ts +31 -0
- package/vendor/@anvil/common/src/util/axiom-only-log.ts +124 -0
- package/vendor/@anvil/common/src/util/cache-debug.ts +171 -0
- package/vendor/@anvil/common/src/util/client-user-agent.ts +114 -0
- package/vendor/@anvil/common/src/util/credentials.ts +26 -0
- package/vendor/@anvil/common/src/util/currency.ts +25 -0
- package/vendor/@anvil/common/src/util/dates.ts +81 -0
- package/vendor/@anvil/common/src/util/disposable-email.ts +277 -0
- package/vendor/@anvil/common/src/util/engagement-tracker.ts +129 -0
- package/vendor/@anvil/common/src/util/env-file-path.ts +40 -0
- package/vendor/@anvil/common/src/util/error.ts +562 -0
- package/vendor/@anvil/common/src/util/file-read-limits.ts +236 -0
- package/vendor/@anvil/common/src/util/file.ts +339 -0
- package/vendor/@anvil/common/src/util/format-code-search.ts +115 -0
- package/vendor/@anvil/common/src/util/lazy-response-ads.ts +92 -0
- package/vendor/@anvil/common/src/util/log-data.ts +57 -0
- package/vendor/@anvil/common/src/util/log-ingest.ts +58 -0
- package/vendor/@anvil/common/src/util/log-mirror.ts +34 -0
- package/vendor/@anvil/common/src/util/lru-cache.ts +67 -0
- package/vendor/@anvil/common/src/util/messages.ts +685 -0
- package/vendor/@anvil/common/src/util/min-heap.ts +87 -0
- package/vendor/@anvil/common/src/util/model-utils.ts +4 -0
- package/vendor/@anvil/common/src/util/object.ts +128 -0
- package/vendor/@anvil/common/src/util/partial-json-delta.ts +89 -0
- package/vendor/@anvil/common/src/util/path.ts +15 -0
- package/vendor/@anvil/common/src/util/project-ignore.ts +119 -0
- package/vendor/@anvil/common/src/util/promise.ts +73 -0
- package/vendor/@anvil/common/src/util/random.ts +15 -0
- package/vendor/@anvil/common/src/util/rate-limit.ts +56 -0
- package/vendor/@anvil/common/src/util/reddit-anvil-retention.ts +60 -0
- package/vendor/@anvil/common/src/util/reddit-capi-events.ts +36 -0
- package/vendor/@anvil/common/src/util/response-ad-positions.ts +54 -0
- package/vendor/@anvil/common/src/util/saxy.ts +741 -0
- package/vendor/@anvil/common/src/util/skills.test.ts +44 -0
- package/vendor/@anvil/common/src/util/skills.ts +36 -0
- package/vendor/@anvil/common/src/util/split-data.ts +259 -0
- package/vendor/@anvil/common/src/util/stop-sequence.ts +61 -0
- package/vendor/@anvil/common/src/util/string.ts +426 -0
- package/vendor/@anvil/common/src/util/system-info.ts +53 -0
- package/vendor/@anvil/common/src/util/thread-title.ts +46 -0
- package/vendor/@anvil/common/src/util/ttft-histogram.ts +73 -0
- package/vendor/@anvil/common/src/util/xml-parser.ts +27 -0
- package/vendor/@anvil/common/src/util/xml.ts +17 -0
- package/vendor/@anvil/common/src/util/zod-schema.ts +25 -0
- package/vendor/@anvil/common/src/util/zoned-time.ts +136 -0
- package/vendor/@anvil/common/src/utils/ask-user-bridge.ts +44 -0
- package/vendor/@anvil/sdk/dist/index.cjs +52297 -0
- package/vendor/@anvil/sdk/dist/index.cjs.map +440 -0
- package/vendor/@anvil/sdk/dist/index.d.ts +4277 -0
- package/vendor/@anvil/sdk/dist/index.mjs +52290 -0
- package/vendor/@anvil/sdk/dist/index.mjs.map +440 -0
- package/vendor/@anvil/sdk/dist/vendor/ai.cjs +30886 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-linux/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-darwin/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-linux/rg +0 -0
- package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-c-sharp.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-cpp.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-go.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-java.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-javascript.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-python.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-ruby.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-rust.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-tsx.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter-typescript.wasm +0 -0
- package/vendor/@anvil/sdk/dist/wasm/tree-sitter.wasm +0 -0
- package/vendor/@anvil/sdk/package.json +21 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generic min heap implementation that maintains elements ordered by a numeric score.
|
|
3
|
+
*/
|
|
4
|
+
export class MinHeap<T> {
|
|
5
|
+
private heap: { item: T; score: number }[] = []
|
|
6
|
+
|
|
7
|
+
private getParentIndex(index: number): number {
|
|
8
|
+
return Math.floor((index - 1) / 2)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private getLeftChildIndex(index: number): number {
|
|
12
|
+
return 2 * index + 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private getRightChildIndex(index: number): number {
|
|
16
|
+
return 2 * index + 2
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private swap(index1: number, index2: number): void {
|
|
20
|
+
const temp = this.heap[index1]
|
|
21
|
+
this.heap[index1] = this.heap[index2]
|
|
22
|
+
this.heap[index2] = temp
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private siftUp(index: number): void {
|
|
26
|
+
while (index > 0) {
|
|
27
|
+
const parentIndex = this.getParentIndex(index)
|
|
28
|
+
if (this.heap[parentIndex].score <= this.heap[index].score) {
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
this.swap(index, parentIndex)
|
|
32
|
+
index = parentIndex
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private siftDown(index: number): void {
|
|
37
|
+
while (true) {
|
|
38
|
+
let minIndex = index
|
|
39
|
+
const leftChild = this.getLeftChildIndex(index)
|
|
40
|
+
const rightChild = this.getRightChildIndex(index)
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
leftChild < this.heap.length &&
|
|
44
|
+
this.heap[leftChild].score < this.heap[minIndex].score
|
|
45
|
+
) {
|
|
46
|
+
minIndex = leftChild
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (
|
|
50
|
+
rightChild < this.heap.length &&
|
|
51
|
+
this.heap[rightChild].score < this.heap[minIndex].score
|
|
52
|
+
) {
|
|
53
|
+
minIndex = rightChild
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (minIndex === index) {
|
|
57
|
+
break
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.swap(index, minIndex)
|
|
61
|
+
index = minIndex
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
insert(item: T, score: number): void {
|
|
66
|
+
this.heap.push({ item, score })
|
|
67
|
+
this.siftUp(this.heap.length - 1)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
extractMin(): T | undefined {
|
|
71
|
+
if (this.heap.length === 0) return undefined
|
|
72
|
+
|
|
73
|
+
const minItem = this.heap[0].item
|
|
74
|
+
const last = this.heap.pop()!
|
|
75
|
+
|
|
76
|
+
if (this.heap.length > 0) {
|
|
77
|
+
this.heap[0] = last
|
|
78
|
+
this.siftDown(0)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return minItem
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get size(): number {
|
|
85
|
+
return this.heap.length
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Compatibility export for callers that imported the old utility path. The
|
|
2
|
+
// implementation lives beside `models`, avoiding the old model-config ->
|
|
3
|
+
// model-utils -> old-constants -> model-config cycle and its CommonJS require.
|
|
4
|
+
export { isExplicitlyDefinedModel } from '../constants/model-config'
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { isEqual, mapValues, union } from 'lodash'
|
|
2
|
+
|
|
3
|
+
type RemoveUndefined<T extends object> = {
|
|
4
|
+
[K in keyof T as T[K] extends undefined ? never : K]: Exclude<T[K], undefined>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const removeUndefinedProps = <T extends object>(
|
|
8
|
+
obj: T,
|
|
9
|
+
): RemoveUndefined<T> => {
|
|
10
|
+
const newObj: Record<string, unknown> = {}
|
|
11
|
+
|
|
12
|
+
for (const key of Object.keys(obj)) {
|
|
13
|
+
const value = obj[key as keyof T]
|
|
14
|
+
if (value !== undefined) {
|
|
15
|
+
newObj[key] = value
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return newObj as RemoveUndefined<T>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const removeNullOrUndefinedProps = <T extends object>(
|
|
23
|
+
obj: T,
|
|
24
|
+
exceptions?: string[],
|
|
25
|
+
): T => {
|
|
26
|
+
const newObj: Record<string, unknown> = {}
|
|
27
|
+
|
|
28
|
+
for (const key of Object.keys(obj)) {
|
|
29
|
+
const value = obj[key as keyof T]
|
|
30
|
+
if (
|
|
31
|
+
(value !== undefined && value !== null) ||
|
|
32
|
+
(exceptions ?? []).includes(key)
|
|
33
|
+
) {
|
|
34
|
+
newObj[key] = value
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return newObj as T
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const addObjects = <T extends { [key: string]: number }>(
|
|
41
|
+
obj1: T,
|
|
42
|
+
obj2: T,
|
|
43
|
+
): T => {
|
|
44
|
+
const keys = union(Object.keys(obj1), Object.keys(obj2))
|
|
45
|
+
const newObj: { [key: string]: number } = {}
|
|
46
|
+
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
newObj[key] = (obj1[key] ?? 0) + (obj2[key] ?? 0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return newObj as T
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const subtractObjects = <T extends { [key: string]: number }>(
|
|
55
|
+
obj1: T,
|
|
56
|
+
obj2: T,
|
|
57
|
+
): T => {
|
|
58
|
+
const keys = union(Object.keys(obj1), Object.keys(obj2))
|
|
59
|
+
const newObj: { [key: string]: number } = {}
|
|
60
|
+
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
newObj[key] = (obj1[key] ?? 0) - (obj2[key] ?? 0)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return newObj as T
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const hasChanges = <T extends object>(obj: T, partial: Partial<T>) => {
|
|
69
|
+
const currValues = mapValues(partial, (_, key: keyof T) => obj[key])
|
|
70
|
+
return !isEqual(currValues, partial)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const hasSignificantDeepChanges = <T extends object>(
|
|
74
|
+
obj: T,
|
|
75
|
+
partial: Partial<T>,
|
|
76
|
+
epsilonForNumbers: number,
|
|
77
|
+
): boolean => {
|
|
78
|
+
const compareValues = (currValue: any, partialValue: any): boolean => {
|
|
79
|
+
if (typeof currValue === 'number' && typeof partialValue === 'number') {
|
|
80
|
+
return Math.abs(currValue - partialValue) > epsilonForNumbers
|
|
81
|
+
}
|
|
82
|
+
if (typeof currValue === 'object' && typeof partialValue === 'object') {
|
|
83
|
+
return hasSignificantDeepChanges(
|
|
84
|
+
currValue,
|
|
85
|
+
partialValue,
|
|
86
|
+
epsilonForNumbers,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
return !isEqual(currValue, partialValue)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const key in partial) {
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(partial, key)) {
|
|
94
|
+
if (compareValues(obj[key], partial[key])) {
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const filterObject = <T extends object>(
|
|
104
|
+
obj: T,
|
|
105
|
+
predicate: (value: any, key: keyof T) => boolean,
|
|
106
|
+
): { [P in keyof T]: T[P] } => {
|
|
107
|
+
const result = {} as { [P in keyof T]: T[P] }
|
|
108
|
+
for (const key in obj) {
|
|
109
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
110
|
+
if (predicate(obj[key], key)) {
|
|
111
|
+
result[key] = obj[key]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Asserts that a condition is true. If the condition is false, it throws an error with the provided message.
|
|
120
|
+
* @param condition The condition to check
|
|
121
|
+
* @param message The error message to display if the condition is false
|
|
122
|
+
* @throws {Error} If the condition is false
|
|
123
|
+
*/
|
|
124
|
+
export function assert(condition: boolean, message: string): asserts condition {
|
|
125
|
+
if (!condition) {
|
|
126
|
+
throw new Error(`Assertion failed: ${message}`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export function parsePartialJsonObjectSingle(content: string): {
|
|
2
|
+
lastParamComplete: boolean
|
|
3
|
+
params: any
|
|
4
|
+
} {
|
|
5
|
+
try {
|
|
6
|
+
return { lastParamComplete: true, params: JSON.parse(content) }
|
|
7
|
+
} catch {}
|
|
8
|
+
|
|
9
|
+
if (!content.match(/\d$/)) {
|
|
10
|
+
try {
|
|
11
|
+
return { lastParamComplete: true, params: JSON.parse(content + '}') }
|
|
12
|
+
} catch {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
return { lastParamComplete: false, params: JSON.parse(content + '"}') }
|
|
17
|
+
} catch {}
|
|
18
|
+
|
|
19
|
+
if (content.endsWith('\\')) {
|
|
20
|
+
try {
|
|
21
|
+
return {
|
|
22
|
+
lastParamComplete: false,
|
|
23
|
+
params: JSON.parse(content.slice(0, -1) + '"}'),
|
|
24
|
+
}
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let commaPos = content.length
|
|
29
|
+
while ((commaPos = content.lastIndexOf(',', commaPos - 1)) !== -1) {
|
|
30
|
+
try {
|
|
31
|
+
return {
|
|
32
|
+
lastParamComplete: true,
|
|
33
|
+
params: JSON.parse(content.slice(0, commaPos) + '}'),
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { lastParamComplete: true, params: {} }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getPartialJsonDelta(
|
|
42
|
+
content: string,
|
|
43
|
+
previous: string,
|
|
44
|
+
): {
|
|
45
|
+
delta: Record<string, any>
|
|
46
|
+
result: Record<string, any>
|
|
47
|
+
lastParam: { key: string | undefined; complete: boolean }
|
|
48
|
+
} {
|
|
49
|
+
if (!content.startsWith(previous)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Content must be previous content plus new content. Content ${JSON.stringify(content)} does not start with previous content ${JSON.stringify(previous)}`,
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
const { lastParamComplete, params } = parsePartialJsonObjectSingle(content)
|
|
55
|
+
const lastParam = Object.keys(params).pop()
|
|
56
|
+
|
|
57
|
+
const { lastParamComplete: prevLastParamComplete, params: prevParams } =
|
|
58
|
+
parsePartialJsonObjectSingle(previous)
|
|
59
|
+
const prevLastParam = Object.keys(prevParams).pop()
|
|
60
|
+
|
|
61
|
+
const entries = Object.entries(params)
|
|
62
|
+
|
|
63
|
+
const delta: Record<string, any> = {}
|
|
64
|
+
for (const [key, value] of entries) {
|
|
65
|
+
if (prevParams[key] === value) {
|
|
66
|
+
if (prevLastParam === key && !prevLastParamComplete) {
|
|
67
|
+
delta[key] = ''
|
|
68
|
+
}
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
if (typeof value === 'string') {
|
|
72
|
+
delta[key] = value.slice((prevParams[key] ?? '').length)
|
|
73
|
+
} else {
|
|
74
|
+
delta[key] = value
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
delta,
|
|
80
|
+
result: params,
|
|
81
|
+
lastParam: {
|
|
82
|
+
key: lastParam,
|
|
83
|
+
complete:
|
|
84
|
+
prevLastParam === lastParam
|
|
85
|
+
? lastParamComplete && !prevLastParamComplete
|
|
86
|
+
: lastParamComplete,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* True when candidate is lexically root itself or one of its descendants.
|
|
5
|
+
* Resolve symlinks first when the caller needs filesystem-level containment.
|
|
6
|
+
*/
|
|
7
|
+
export function isPathInside(root: string, candidate: string): boolean {
|
|
8
|
+
const relativePath = path.relative(root, candidate)
|
|
9
|
+
return (
|
|
10
|
+
relativePath === '' ||
|
|
11
|
+
(relativePath !== '..' &&
|
|
12
|
+
!relativePath.startsWith(`..${path.sep}`) &&
|
|
13
|
+
!path.isAbsolute(relativePath))
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as ignore from 'ignore'
|
|
2
|
+
|
|
3
|
+
export const PROJECT_IGNORE_FILES = [
|
|
4
|
+
'.gitignore',
|
|
5
|
+
'.anvilignore',
|
|
6
|
+
'.manicodeignore',
|
|
7
|
+
// Legacy: respect ignore files created by pre-Anvil installs.
|
|
8
|
+
'.codebuffignore',
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
export type DirIgnore = { base: string; ig: ignore.Ignore }
|
|
12
|
+
|
|
13
|
+
function normalizeRelativePath(filePath: string): string | null {
|
|
14
|
+
const segments: string[] = []
|
|
15
|
+
for (const segment of filePath.replaceAll('\\', '/').split('/')) {
|
|
16
|
+
if (!segment || segment === '.') continue
|
|
17
|
+
if (segment === '..') {
|
|
18
|
+
if (segments.length === 0) return null
|
|
19
|
+
segments.pop()
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
segments.push(segment)
|
|
23
|
+
}
|
|
24
|
+
return segments.join('/') || null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function testChain(chain: DirIgnore[], candidate: string): boolean {
|
|
28
|
+
let ignored = false
|
|
29
|
+
for (const { base, ig } of chain) {
|
|
30
|
+
if (base && !candidate.startsWith(`${base}/`)) continue
|
|
31
|
+
const scoped = base ? candidate.slice(base.length + 1) : candidate
|
|
32
|
+
if (!scoped) continue
|
|
33
|
+
const result = ig.test(scoped)
|
|
34
|
+
if (result.ignored) ignored = true
|
|
35
|
+
else if (result.unignored) ignored = false
|
|
36
|
+
}
|
|
37
|
+
return ignored
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isIgnoredByIgnoreChain(
|
|
41
|
+
chain: DirIgnore[],
|
|
42
|
+
filePath: string,
|
|
43
|
+
): boolean {
|
|
44
|
+
const segments = filePath.split('/')
|
|
45
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
46
|
+
if (testChain(chain, `${segments.slice(0, i + 1).join('/')}/`)) {
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return testChain(chain, filePath)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function addIgnoreFileContents(
|
|
54
|
+
ig: ignore.Ignore,
|
|
55
|
+
contents: string,
|
|
56
|
+
onInvalidPattern?: (pattern: string, error: unknown) => void,
|
|
57
|
+
): void {
|
|
58
|
+
for (let line of contents.split('\n')) {
|
|
59
|
+
line = line.trim()
|
|
60
|
+
if (!line || line.startsWith('#')) continue
|
|
61
|
+
try {
|
|
62
|
+
ig.add(line)
|
|
63
|
+
} catch (error: unknown) {
|
|
64
|
+
// Match the local file-tree behavior: one invalid pattern must not make
|
|
65
|
+
// the remaining valid project rules disappear.
|
|
66
|
+
onInvalidPattern?.(line, error)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Build a fail-closed-friendly ignore-file reader for filesystems whose read
|
|
73
|
+
* errors do not reliably distinguish a missing file from an unreadable one.
|
|
74
|
+
*/
|
|
75
|
+
export function createProjectIgnoreFileReader(params: {
|
|
76
|
+
listDirectory: (directory: string) => Promise<readonly string[]>
|
|
77
|
+
readFile: (filePath: string) => Promise<string>
|
|
78
|
+
}): (filePath: string) => Promise<string | null> {
|
|
79
|
+
const directoryEntries = new Map<string, Promise<Set<string>>>()
|
|
80
|
+
|
|
81
|
+
return async (filePath) => {
|
|
82
|
+
const separator = filePath.lastIndexOf('/')
|
|
83
|
+
const directory = separator === -1 ? '' : filePath.slice(0, separator)
|
|
84
|
+
const fileName = filePath.slice(separator + 1)
|
|
85
|
+
|
|
86
|
+
let entries = directoryEntries.get(directory)
|
|
87
|
+
if (!entries) {
|
|
88
|
+
entries = params.listDirectory(directory).then((names) => new Set(names))
|
|
89
|
+
directoryEntries.set(directory, entries)
|
|
90
|
+
}
|
|
91
|
+
if (!(await entries).has(fileName)) return null
|
|
92
|
+
|
|
93
|
+
return params.readFile(filePath)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Evaluate project-owned ignore files without requiring a native Git binary. */
|
|
98
|
+
export async function isProjectFileIgnored(params: {
|
|
99
|
+
filePath: string
|
|
100
|
+
readIgnoreFile: (filePath: string) => Promise<string | null>
|
|
101
|
+
}): Promise<boolean> {
|
|
102
|
+
const filePath = normalizeRelativePath(params.filePath)
|
|
103
|
+
if (!filePath) return true
|
|
104
|
+
|
|
105
|
+
const parentSegments = filePath.split('/').slice(0, -1)
|
|
106
|
+
const chain: DirIgnore[] = []
|
|
107
|
+
for (let depth = 0; depth <= parentSegments.length; depth++) {
|
|
108
|
+
const base = parentSegments.slice(0, depth).join('/')
|
|
109
|
+
const ig = ignore.default()
|
|
110
|
+
for (const ignoreFile of PROJECT_IGNORE_FILES) {
|
|
111
|
+
const ignorePath = base ? `${base}/${ignoreFile}` : ignoreFile
|
|
112
|
+
const contents = await params.readIgnoreFile(ignorePath)
|
|
113
|
+
if (contents !== null) addIgnoreFileContents(ig, contents)
|
|
114
|
+
}
|
|
115
|
+
chain.push({ base, ig })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return isIgnoredByIgnoreChain(chain, filePath)
|
|
119
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const INITIAL_RETRY_DELAY = 1000 // 1 second
|
|
2
|
+
|
|
3
|
+
export async function withRetry<T>(
|
|
4
|
+
operation: () => Promise<T>,
|
|
5
|
+
options: {
|
|
6
|
+
maxRetries?: number
|
|
7
|
+
retryIf?: (error: any) => boolean
|
|
8
|
+
onRetry?: (error: any, attempt: number) => void
|
|
9
|
+
retryDelayMs?: number
|
|
10
|
+
} = {},
|
|
11
|
+
): Promise<T> {
|
|
12
|
+
const {
|
|
13
|
+
maxRetries = 3,
|
|
14
|
+
retryIf = (error) => error?.type === 'APIConnectionError',
|
|
15
|
+
onRetry = () => {},
|
|
16
|
+
retryDelayMs = INITIAL_RETRY_DELAY,
|
|
17
|
+
} = options
|
|
18
|
+
|
|
19
|
+
let lastError: any = null
|
|
20
|
+
|
|
21
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
22
|
+
try {
|
|
23
|
+
return await operation()
|
|
24
|
+
} catch (error) {
|
|
25
|
+
lastError = error
|
|
26
|
+
|
|
27
|
+
if (!retryIf(error) || attempt === maxRetries - 1) {
|
|
28
|
+
throw error
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onRetry(error, attempt + 1)
|
|
32
|
+
|
|
33
|
+
// Exponential backoff with jitter (±20%) to prevent thundering herd
|
|
34
|
+
const baseDelayMs = retryDelayMs * Math.pow(2, attempt)
|
|
35
|
+
const jitter = 0.8 + Math.random() * 0.4 // Random multiplier between 0.8 and 1.2
|
|
36
|
+
const delayMs = Math.round(baseDelayMs * jitter)
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
throw lastError
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Wraps a promise with a timeout
|
|
48
|
+
* @param promise The promise to wrap
|
|
49
|
+
* @param timeoutMs Timeout in milliseconds
|
|
50
|
+
* @param timeoutMessage Optional message for the timeout error
|
|
51
|
+
* @returns A promise that resolves with the result of the original promise or rejects with a timeout error
|
|
52
|
+
*/
|
|
53
|
+
export async function withTimeout<T>(
|
|
54
|
+
promise: Promise<T>,
|
|
55
|
+
timeoutMs: number,
|
|
56
|
+
timeoutMessage: string = `Operation timed out after ${timeoutMs}ms`,
|
|
57
|
+
): Promise<T> {
|
|
58
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
59
|
+
|
|
60
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
61
|
+
timeoutId = setTimeout(() => {
|
|
62
|
+
reject(new Error(timeoutMessage))
|
|
63
|
+
}, timeoutMs)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
return await Promise.race([promise, timeoutPromise])
|
|
68
|
+
} finally {
|
|
69
|
+
if (timeoutId !== undefined) {
|
|
70
|
+
clearTimeout(timeoutId)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import seedrandom from 'seedrandom'
|
|
2
|
+
|
|
3
|
+
export function sampleSizeWithSeed<T>(
|
|
4
|
+
array: T[],
|
|
5
|
+
size: number,
|
|
6
|
+
seed: string,
|
|
7
|
+
): T[] {
|
|
8
|
+
const rng = seedrandom(seed)
|
|
9
|
+
const result = array.slice()
|
|
10
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
11
|
+
const j = Math.floor(rng() * (i + 1))
|
|
12
|
+
;[result[i], result[j]] = [result[j], result[i]]
|
|
13
|
+
}
|
|
14
|
+
return result.slice(0, size)
|
|
15
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal in-memory fixed-window rate limiter, shared by the unauthenticated
|
|
3
|
+
* `/api/logs` ingest endpoints (browser + anonymous CLI). Per-instance and
|
|
4
|
+
* best-effort — good enough to blunt abuse/cost on a single Render instance,
|
|
5
|
+
* not a distributed guarantee. Kept dependency-free and `now`-injectable so the
|
|
6
|
+
* window logic is unit-testable.
|
|
7
|
+
*/
|
|
8
|
+
export interface FixedWindowRateLimiter {
|
|
9
|
+
/** Returns true if `key` has exceeded the window's request budget. */
|
|
10
|
+
limited(key: string, now: number): boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createFixedWindowRateLimiter(opts: {
|
|
14
|
+
windowMs: number
|
|
15
|
+
max: number
|
|
16
|
+
/** Prune expired entries once the map grows past this. Defaults to 10k. */
|
|
17
|
+
maxKeys?: number
|
|
18
|
+
}): FixedWindowRateLimiter {
|
|
19
|
+
const { windowMs, max, maxKeys = 10_000 } = opts
|
|
20
|
+
const hits = new Map<string, { count: number; resetAt: number }>()
|
|
21
|
+
let lastPruneAt = 0
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
limited(key: string, now: number): boolean {
|
|
25
|
+
const entry = hits.get(key)
|
|
26
|
+
if (!entry || now >= entry.resetAt) {
|
|
27
|
+
hits.set(key, { count: 1, resetAt: now + windowMs })
|
|
28
|
+
// Bound map growth: prune expired entries, but at most once per window
|
|
29
|
+
// so a steady stream of live keys can't trigger an O(n) scan per call.
|
|
30
|
+
if (hits.size > maxKeys && now - lastPruneAt >= windowMs) {
|
|
31
|
+
lastPruneAt = now
|
|
32
|
+
for (const [k, v] of hits) if (now >= v.resetAt) hits.delete(k)
|
|
33
|
+
}
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
entry.count++
|
|
37
|
+
return entry.count > max
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Best-effort client IP for per-IP rate limiting on the unauthenticated ingest
|
|
44
|
+
* endpoints. Prefers the proxy-set `x-real-ip` (harder to spoof than the
|
|
45
|
+
* left-most `x-forwarded-for` token). Accepts any Headers-like object so it
|
|
46
|
+
* works with `NextRequest.headers` without a Next dependency here.
|
|
47
|
+
*/
|
|
48
|
+
export function extractClientIp(headers: {
|
|
49
|
+
get(name: string): string | null
|
|
50
|
+
}): string {
|
|
51
|
+
return (
|
|
52
|
+
headers.get('x-real-ip')?.trim() ||
|
|
53
|
+
headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
|
|
54
|
+
'unknown'
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const DAY_MS = 24 * 60 * 60 * 1000
|
|
2
|
+
|
|
3
|
+
/** PostHog-style retention windows to mirror in Reddit CAPI custom events. */
|
|
4
|
+
export const ANVIL_REDDIT_RETENTION_MILESTONE_DAYS = [1, 7, 24] as const
|
|
5
|
+
|
|
6
|
+
export type AnvilRedditRetentionMilestoneDays =
|
|
7
|
+
(typeof ANVIL_REDDIT_RETENTION_MILESTONE_DAYS)[number]
|
|
8
|
+
|
|
9
|
+
export type AnvilRedditConversionPlan = {
|
|
10
|
+
fireFirstPrompt: boolean
|
|
11
|
+
retentionMilestones: AnvilRedditRetentionMilestoneDays[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function daysBetween(fromDateKey: string, toDateKey: string): number {
|
|
15
|
+
const from = new Date(`${fromDateKey}T00:00:00.000Z`).getTime()
|
|
16
|
+
const to = new Date(`${toDateKey}T00:00:00.000Z`).getTime()
|
|
17
|
+
if (Number.isNaN(from) || Number.isNaN(to)) {
|
|
18
|
+
throw new Error(`Invalid date key range: ${fromDateKey} -> ${toDateKey}`)
|
|
19
|
+
}
|
|
20
|
+
return Math.round((to - from) / DAY_MS)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** First successful anvil prompt = first-ever usage day recorded. */
|
|
24
|
+
export function isFirstAnvilPrompt(params: {
|
|
25
|
+
previousUsageDays: readonly string[]
|
|
26
|
+
newUsageDayRecorded: boolean
|
|
27
|
+
}): boolean {
|
|
28
|
+
return params.newUsageDayRecorded && params.previousUsageDays.length === 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Exact calendar-day milestones reached by this newly recorded usage day. */
|
|
32
|
+
export function getAnvilRetentionMilestonesToFire(params: {
|
|
33
|
+
previousUsageDays: readonly string[]
|
|
34
|
+
todayDateKey: string
|
|
35
|
+
newUsageDayRecorded: boolean
|
|
36
|
+
}): AnvilRedditRetentionMilestoneDays[] {
|
|
37
|
+
if (!params.newUsageDayRecorded) {
|
|
38
|
+
return []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const firstDay = [...params.previousUsageDays, params.todayDateKey].reduce(
|
|
42
|
+
(min, dateKey) => (dateKey < min ? dateKey : min),
|
|
43
|
+
)
|
|
44
|
+
const daysSinceFirstToday = daysBetween(firstDay, params.todayDateKey)
|
|
45
|
+
|
|
46
|
+
return ANVIL_REDDIT_RETENTION_MILESTONE_DAYS.filter(
|
|
47
|
+
(milestone) => daysSinceFirstToday === milestone,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function planAnvilRedditConversionEvents(params: {
|
|
52
|
+
previousUsageDays: readonly string[]
|
|
53
|
+
todayDateKey: string
|
|
54
|
+
newUsageDayRecorded: boolean
|
|
55
|
+
}): AnvilRedditConversionPlan {
|
|
56
|
+
return {
|
|
57
|
+
fireFirstPrompt: isFirstAnvilPrompt(params),
|
|
58
|
+
retentionMilestones: getAnvilRetentionMilestonesToFire(params),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AnvilRedditRetentionMilestoneDays } from '@anvil/common/util/reddit-anvil-retention'
|
|
2
|
+
|
|
3
|
+
export type RedditConversionSurface = 'cli' | 'web' | 'cloud' | 'chat'
|
|
4
|
+
|
|
5
|
+
export type RedditCapiEventName =
|
|
6
|
+
| 'FirstPrompt'
|
|
7
|
+
| 'Retention1d'
|
|
8
|
+
| 'Retention7d'
|
|
9
|
+
| 'Retention24d'
|
|
10
|
+
|
|
11
|
+
export const REDDIT_FIRST_PROMPT_EVENT = 'FirstPrompt' as const
|
|
12
|
+
|
|
13
|
+
export function redditRetentionCapiEventName(
|
|
14
|
+
milestone: AnvilRedditRetentionMilestoneDays,
|
|
15
|
+
): RedditCapiEventName {
|
|
16
|
+
return `Retention${milestone}d`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The day boundary D1/D7/D24 eligibility is computed in.
|
|
21
|
+
*
|
|
22
|
+
* Anything deciding "have we already done this user's tracking today?" MUST
|
|
23
|
+
* use this same boundary. A memo keyed on a different day (Pacific, say) opens
|
|
24
|
+
* a window where the UTC day has rolled over but the memo has not, and a
|
|
25
|
+
* milestone claim can never be created in it — the conversion is lost, not
|
|
26
|
+
* delayed. Do not reimplement.
|
|
27
|
+
*
|
|
28
|
+
* It lives in this dependency-free module rather than next to its consumer in
|
|
29
|
+
* `packages/internal/src/reddit-conversions.ts`, because that module imports
|
|
30
|
+
* `./db`, which constructs the application connection pool at module scope.
|
|
31
|
+
* A pure date helper must not drag a database pool into everything that needs
|
|
32
|
+
* a day key.
|
|
33
|
+
*/
|
|
34
|
+
export function utcDateKey(now: Date): string {
|
|
35
|
+
return now.toISOString().slice(0, 10)
|
|
36
|
+
}
|