Skip to content

switch from lite-api to api.jup.ag with API key auth#4

Open
odilitime wants to merge 163 commits into1.xfrom
odi-dev
Open

switch from lite-api to api.jup.ag with API key auth#4
odilitime wants to merge 163 commits into1.xfrom
odi-dev

Conversation

@odilitime
Copy link
Copy Markdown
Member

@odilitime odilitime commented Feb 11, 2026

lite-api.jup.ag no longer works; basic tier now requires an API key. Reads JUPITER_API_KEY from runtime settings and sends x-api-key header on all quote and swap requests. Also adds build.ts, tsconfig.build.json, expanded .gitignore, and various type annotation fixes.


Note

Medium Risk
Moves all Jupiter quote/swap/metadata calls to authenticated api.jup.ag endpoints and introduces a shared, serialized rate-limit queue plus caching; mistakes here could break swaps/quotes or stall request processing. Build tooling is also replaced (tsup → Bun/tsc), which could affect packaging and declaration output.

Overview
Migrates Jupiter integration off lite-api.jup.ag to authenticated api.jup.ag, requiring JUPITER_API_KEY at service start and adding x-api-key headers to quote, swap, token search, and price requests.

Reworks request handling into a single shared rate-limited queue (quotes/swaps/metadata) with retry/backoff on 429s, plus a bounded 30s route cache keyed by pair/amount/slippage; service stop now drains pending queue items, clears API key, and clears cache.

Replaces tsup with a Bun-based build.ts (ESM bundle + tsc declarations via new tsconfig.build.json), updates package.json scripts/engines/devDependencies accordingly, and expands .gitignore (also adds auto-generated .prr/lessons.md).

Reviewed by Cursor Bugbot for commit de1d04d. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • API key–based authentication with queued, rate-limited request processing and route caching for repeated queries.
    • New Bun-based build runner and build configuration.
  • Improvements

    • Exponential-backoff retries, clearer consolidated error logging, and updated build flow that ensures TypeScript declarations.
  • Chores

    • Expanded ignore rules to cover build artifacts, env files, IDE/system caches, logs and temp files.
  • Tests

    • Added a test demonstrating failure handling for missing commands.
  • Documentation

    • Added internal PR review lessons file.

Greptile Overview

Greptile Summary

Migrates Jupiter API integration from lite-api.jup.ag to api.jup.ag with API key authentication via x-api-key header, sourced from JUPITER_API_KEY runtime setting.

Key Changes:

  • Adds getApiHeaders() function to inject x-api-key header when API key is configured
  • Updates base URL constant to https://api.jup.ag/swap/v1 for quote and swap endpoints
  • Improves TypeScript typing for queue handlers, quote responses, and cache utilities
  • Refines cache key to include amount parameter for better cache granularity
  • Replaces tsup build with custom Bun-based build.ts that runs ESM bundling + TypeScript declaration generation

Issues Found:

  • getTokenPair() and getHistoricalPrices() methods still call the old https://public.jupiterapi.com endpoints, which may not accept the new API key header
  • dev script references --watch flag, but build.ts doesn't implement watch mode
  • Dead code in build.ts (unreachable else branch due to if (true) condition)

Confidence Score: 3/5

  • Mostly safe to merge, but has endpoint inconsistencies that could cause runtime issues
  • Core quote/swap functionality properly migrated to new API with authentication, but two methods (getTokenPair, getHistoricalPrices) still use old endpoints that may not work. Build system changes are functional despite minor issues with watch mode and dead code.
  • Pay close attention to src/service.ts - verify that getTokenPair and getHistoricalPrices work with the old endpoint or need migration

Important Files Changed

Filename Overview
src/service.ts Updates to api.jup.ag with API key auth; two methods still use old public.jupiterapi.com endpoint
build.ts New Bun-based build script; contains unreachable dead code in conditional branch
package.json Switches from tsup to custom build.ts; dev script references unimplemented --watch flag

Sequence Diagram

sequenceDiagram
    participant Client as JupiterService
    participant API as api.jup.ag
    participant Queue as Request Queue
    
    Note over Client: Service starts, reads API key from settings
    Client->>Client: Load API key configuration
    
    Note over Client,API: Quote Request Flow
    Client->>Queue: quoteEnqueue(url)
    Queue->>Queue: Add to quotes queue
    Queue->>API: GET /swap/v1/quote (with auth header)
    API-->>Queue: Quote response
    Queue-->>Client: Return quote data
    
    Note over Client,API: Swap Request Flow
    Client->>Queue: swapEnqueue(url, payload)
    Queue->>Queue: Add to swaps queue
    Queue->>API: POST /swap/v1/swap (with auth header)
    API-->>Queue: Swap transaction data
    Queue-->>Client: Return swap data
    
    Note over Queue: Queues process at 1 req/sec<br/>with exponential backoff on 429
Loading

lite-api.jup.ag no longer works; basic tier now requires an API key.
Reads JUPITER_API_KEY from runtime settings and sends x-api-key header
on all quote and swap requests. Also adds build.ts, tsconfig.build.json,
expanded .gitignore, and various type annotation fixes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings February 11, 2026 00:58
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a Bun-based build orchestrator and tsconfig.build.json, expands .gitignore rules, introduces a Bun-oriented test file, adds PR-review lessons, and refactors src/service.ts to add API-key startup gating, shared rate-limit queues with retries/backoff, a TTL FIFO route cache, centralized API headers, and improved error logging.

Changes

Cohort / File(s) Summary
Build system
build.ts, package.json, tsconfig.build.json
Adds build.ts (Bun-based build orchestrator), updates package.json build script to bun run build.ts and adds engines.bun + typescript devDependency, and introduces tsconfig.build.json for dist output, declarations, and sourcemaps.
Jupiter service
src/service.ts
Large refactor: introduces module-level API key and startup gating, shared per-operation rate-limit queues (quotes/swaps/metadata) with retry/backoff, centralized getApiHeaders, TTL FIFO route cache with eviction, unified JUPITER_BASE_URL routing, and standardized error handling/logging. Adds private fields for routeCache and apiKey.
Repository ignore rules
.gitignore
Expands ignore list (build outputs, .tsbuildinfo, .turbo, env files, IDE folders, coverage, logs, caches, temp files, bundler artifacts, .pr-resolver-state.json); removes node_modules entry from Build outputs section.
Tests & scripts
test_fail.ts
Adds a Bun-targeted test script that runs a non-existent command to exercise failure handling; logs and exits with non-zero on error.
PR review notes
.prr/lessons.md
Adds auto-generated PR review lessons documenting concerns and recommended fixes specific to src/service.ts (instructional only, no code changes).

Sequence Diagram(s)

sequenceDiagram
    rect rgba(52,152,219,0.5)
    participant Client
    end
    rect rgba(46,204,113,0.5)
    participant JupiterService
    end
    rect rgba(241,196,15,0.5)
    participant RouteCache
    end
    rect rgba(155,89,182,0.5)
    participant RateLimitQueue
    end
    rect rgba(231,76,60,0.5)
    participant JupiterAPI
    end

    Client->>JupiterService: request quote/swap/metadata(params)
    JupiterService->>RouteCache: check cache(key)
    alt cache hit
        RouteCache-->>JupiterService: cached result
        JupiterService-->>Client: return cached result
    else cache miss
        JupiterService->>RateLimitQueue: enqueue request(params, apiKey, headers)
        RateLimitQueue-->>JupiterService: dequeue/process
        JupiterService->>JupiterAPI: HTTP request (with getApiHeaders)
        alt 429 or transient error
            JupiterAPI-->>RateLimitQueue: error
            RateLimitQueue->>RateLimitQueue: schedule retry (exponential backoff)
            RateLimitQueue-->>JupiterService: retry processed
            JupiterService->>JupiterAPI: retry request
        end
        JupiterAPI-->>JupiterService: response
        JupiterService->>RouteCache: set cache(key, response, ttl)
        JupiterService-->>Client: return response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 Bun warms my paws at break of day,
Queues hum softly while I hop and play.
Keys and caches guard the trail I roam,
Backoffs nudge retries till requests come home.
Hooray — this rabbit hops back to the code!

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating from lite-api to api.jup.ag with API key authentication, which is the core objective reflected throughout the changeset.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into 1.x
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch odi-dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 11, 2026

Additional Comments (2)

src/service.ts
getTokenPair still uses the old https://public.jupiterapi.com endpoint which may not accept the new API key header


src/service.ts
getHistoricalPrices still uses the old https://public.jupiterapi.com endpoint which may not accept the new API key header

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/service.ts (2)

356-374: ⚠️ Potential issue | 🔴 Critical

Bug: outAmount is divided by input decimals instead of output decimals.

outAmount is denominated in the output token's atomic units (USDC with 6 decimals by default), but line 369 divides by 10 ** inputDecimals. For any input token whose decimals differ from the output token's (e.g., SOL with 9 decimals), the returned price will be off by orders of magnitude.

Example: 1 SOL → 150 USDC yields outAmount = 150_000_000, but 150_000_000 / 10^9 = 0.15 instead of the correct 150_000_000 / 10^6 = 150.

Proposed fix
   async getTokenPrice(
     tokenMint: string,
     quoteMint: string = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
-    inputDecimals: number = 6
+    inputDecimals: number = 6,
+    outputDecimals: number = 6
   ): Promise<number> {
     try {
       const baseAmount = 10 ** inputDecimals;
       const quote = await this.getQuote({
         inputMint: tokenMint,
         outputMint: quoteMint,
         amount: baseAmount,
         slippageBps: 50,
       });
-      return Number((quote as any).outAmount) / 10 ** inputDecimals;
+      return Number((quote as any).outAmount) / 10 ** outputDecimals;
     } catch (error) {

218-233: ⚠️ Potential issue | 🟡 Minor

Cache key uses raw amount but the API receives intAmount — key mismatch possible.

Line 218 parses amount to an integer (intAmount), but line 224 builds the cache key using the original amount. If amount is a float (e.g., 1000.5), the cache key will differ from what's actually queried, causing cache misses or stale hits for the truncated value.

Proposed fix
-      const key = inputMint + '_' + outputMint + '_' + amount
+      const key = inputMint + '_' + outputMint + '_' + intAmount
🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 15-16: The dev script is misleading because "dev": "bun run
build.ts --watch" passes --watch but build.ts doesn't handle it, and tsup
remains unused in devDependencies; either implement watch handling in build.ts
(detect the "--watch" flag in build.ts's argument parsing and start a
file-watcher or call the existing build loop when watch=true) or remove the
watch flag from package.json and delete the unused "tsup" entry from
devDependencies; locate references to the "build" and "dev" scripts in
package.json and the build logic in build.ts to add flag parsing (e.g., checking
process.argv for "--watch") or to simplify scripts and prune "tsup" from
devDependencies accordingly.

In `@src/service.ts`:
- Line 178: routeCache currently accumulates entries added by getQuote and never
evicts them; fix by applying a TTL and/or size cap when inserting and reading
from routeCache: use the existing setCacheExp/getCacheExp helpers to attach and
check expiration for entries stored under the composite key used in getQuote
(inputMint, outputMint, amount), and remove expired entries on access;
additionally implement a simple size cap (e.g., LRU or FIFO) in the same module
so when routeCache.length exceeds the limit you evict oldest entries—ensure the
logic is centralized around the routeCache field and the getQuote function so
all inserts and lookups respect TTL and cap.
- Around line 8-21: The module-level jupiterApiKey and import-time queue timers
cause global shared state and last-writer-wins behavior; move the API key and
header logic into the JupiterService instance (e.g., make jupiterApiKey an
instance property and convert getApiHeaders into an instance method on
JupiterService), update any call sites to use the instance method, and pass the
instance API key into the queue/retry helpers rather than reading a module-level
variable; also stop starting the queue timers at import—initialize and start
those timers from JupiterService.start() or the constructor so each service
instance controls its own timers and retry behavior.
🧹 Nitpick comments (3)
build.ts (1)

36-54: Remove if (true) constant condition and dead else branch.

The if (true) guard (flagged by Biome's noConstantCondition) makes the type-check-only path on lines 45–54 unreachable dead code. If the alternative path is kept for future toggling, consider using an explicit config flag or environment variable instead.

Proposed cleanup
   const dtsStart = Date.now();
-  if (true) { // Always generate .d.ts
-    console.log("📝 Generating TypeScript declarations...");
-    try {
-      await $`tsc --project tsconfig.build.json`;
-      console.log(`✅ Declarations generated in ${((Date.now() - dtsStart) / 1000).toFixed(2)}s`);
-    } catch (error) {
-      console.warn(`⚠️  TypeScript declaration generation had errors (${((Date.now() - dtsStart) / 1000).toFixed(2)}s)`);
-      console.warn("   Build will continue - fix type errors when possible");
-    }
-  } else {
-    console.log("🔍 Type checking...");
-    try {
-      await $`tsc --noEmit --incremental --project tsconfig.build.json`;
-      console.log(`✅ Type check passed in ${((Date.now() - dtsStart) / 1000).toFixed(2)}s`);
-    } catch (error) {
-      console.warn(`⚠️  Type checking had errors (${((Date.now() - dtsStart) / 1000).toFixed(2)}s)`);
-      console.warn("   Build will continue - fix type errors when possible");
-    }
+  console.log("📝 Generating TypeScript declarations...");
+  try {
+    await $`tsc --project tsconfig.build.json`;
+    console.log(`✅ Declarations generated in ${((Date.now() - dtsStart) / 1000).toFixed(2)}s`);
+  } catch (error) {
+    console.warn(`⚠️  TypeScript declaration generation had errors (${((Date.now() - dtsStart) / 1000).toFixed(2)}s)`);
+    console.warn("   Build will continue - fix type errors when possible");
   }
src/service.ts (2)

312-316: Redundant API headers — applied both here and inside getSwapWithRetry.

getApiHeaders() is called here at line 314 to build the payload, and then again inside getSwapWithRetry (line 119) where it's merged with payload.headers. The duplication is harmless but confusing. Pick one site — either the caller or the retry helper — to own header injection.


76-96: Queue polling starts at module import — before any service is configured.

checkQuoteQueues() (line 96) and checkSwapQueues() (line 172) fire as soon as this module is imported, running setTimeout loops indefinitely even if no JupiterService is ever started. These timers should be started from start() and cleared in stop() to avoid unnecessary work and allow clean shutdown.

Also applies to: 153-172

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Jupiter integration to use api.jup.ag and attach an x-api-key header sourced from runtime settings, and replaces the previous tsup-based build with a Bun-based build script plus a dedicated TS build tsconfig.

Changes:

  • Switch quote/swap requests from lite-api.jup.ag to api.jup.ag and add API-key header support via JUPITER_API_KEY.
  • Add Bun-based build pipeline (build.ts) and tsconfig.build.json for declaration generation.
  • Expand .gitignore to include additional build/cache/artifact patterns.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/service.ts Uses new Jupiter base URL, injects x-api-key header, and adds some type annotations/caching tweaks.
build.ts New Bun build script to bundle ESM output and generate .d.ts via tsc.
tsconfig.build.json New TS config intended for declaration-only emit.
package.json Switches build/dev scripts from tsup to Bun build script.
.gitignore Adds common dependency/build/cache/log ignore patterns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

odilitime and others added 5 commits February 11, 2026 01:04
build.ts doesn't implement watch mode; tsup does natively.

Co-authored-by: Cursor <cursoragent@cursor.com>
Both methods were using fabricated public.jupiterapi.com URLs that never
existed. getTokenPair now uses Tokens API V2 Search endpoint with API key
auth. getHistoricalPrices now uses Price API V3 (current prices only, as
Jupiter does not offer a historical price endpoint).

Co-authored-by: Cursor <cursoragent@cursor.com>
api.jup.ag requires an API key for all requests. If none is configured,
log an error and bail out of start() instead of silently failing on
every request.

Co-authored-by: Cursor <cursoragent@cursor.com>
getTokenPrice was dividing outAmount by inputDecimals instead of
outputDecimals — e.g. 1 SOL (9 decimals) to USDC (6 decimals) would
return 0.15 instead of 150. Added outputDecimals param defaulting to 6.

Cache key in getQuote now uses intAmount (the parsed value actually sent
to the API) instead of the raw amount param, preventing mismatches when
a float is passed in.

Co-authored-by: Cursor <cursoragent@cursor.com>
routeCache was an unbounded Record that never evicted entries. Now uses
a Map with 30s TTL and 100-entry FIFO cap. Expired entries are swept on
insert, and oldest entries are evicted when at capacity.

Queue timers (checkQuoteQueues/checkSwapQueues) were starting at import
time even if no service was created or JUPITER_API_KEY was missing. Now
deferred behind startQueues() which is called once from start().

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/service.ts (2)

751-765: ⚠️ Potential issue | 🟠 Major

stop() doesn't cancel queue timers — processing continues after "stop".

checkQuoteQueues and checkSwapQueues schedule themselves with setTimeout indefinitely. stop() only flips isRunning but never breaks the timer chain, so the queues keep draining (or spinning) after the service claims to be stopped. Additionally, queuesStarted is never reset, so a subsequent start() on a new instance won't re-initialize the timers.

Store the timer handles and clear them in stop():

♻️ Sketch
-let queuesStarted = false;
+let queuesStarted = false;
+let quoteTimer: ReturnType<typeof setTimeout> | null = null;
+let swapTimer: ReturnType<typeof setTimeout> | null = null;

 // in checkQuoteQueues:
-  setTimeout(checkQuoteQueues, delayInMs)
+  quoteTimer = setTimeout(checkQuoteQueues, delayInMs)

 // in checkSwapQueues:
-  setTimeout(checkSwapQueues, delayInMs)
+  swapTimer = setTimeout(checkSwapQueues, delayInMs)

+function stopQueues() {
+  if (quoteTimer) clearTimeout(quoteTimer);
+  if (swapTimer) clearTimeout(swapTimer);
+  queuesStarted = false;
+}

 // in stop():
+  stopQueues();
   this.isRunning = false;

711-749: ⚠️ Potential issue | 🟠 Major

Static start() returns a non-running service silently when the API key is missing.

If JUPITER_API_KEY is absent, instance start() logs an error and returns (line 738), but the static start() (line 711-715) still returns the service object to the caller. Downstream code will receive a JupiterService where isRunning === false and no queue timers are active, yet no error is thrown. This makes misconfiguration hard to detect.

Either throw from start() so the caller knows the service failed, or have the static factory propagate the failure:

      if (!apiKey) {
-       logger.error('JUPITER_API_KEY is not set — Jupiter service will not start. Get one at https://portal.jup.ag');
-       return;
+       throw new Error('JUPITER_API_KEY is not set — Jupiter service cannot start. Get one at https://portal.jup.ag');
      }
🧹 Nitpick comments (6)
src/service.ts (6)

224-239: Route cache TTL + eviction looks good; minor lint fix on line 231.

The static analysis tool flags line 231 because the forEach callback implicitly returns the boolean from Map.prototype.delete. While harmless at runtime, it's easy to silence:

♻️ Suggested fix
-    expired.forEach(k => this.routeCache.delete(k));
+    for (const k of expired) this.routeCache.delete(k);

123-129: getApiHeaders() called twice in the swap path — redundant merge.

executeSwap already sets headers: getApiHeaders() in the payload (line 342), and then getSwapWithRetry merges getApiHeaders() again at line 128 before spreading payload.headers on top. The result is correct but getApiHeaders() is invoked twice with identical output.

Pick one site — either let the caller set headers (and don't call getApiHeaders() again inside getSwapWithRetry), or always apply them inside getSwapWithRetry and don't require callers to pass them.


262-266: parseInt(String(amount)) silently truncates fractional values.

amount is typed number. If a caller computes atomic units via floating-point math and passes e.g. 1000000.7, parseInt silently drops the fraction. Math.round (or Math.trunc if truncation is intentional) makes the intent explicit and avoids the unnecessary String() conversion:

-      const intAmount = parseInt(String(amount))
+      const intAmount = Math.round(amount)

78-84: Untyped quote / swap parameters in queue processors.

processQuoteQueue and processSwapQueue accept untyped parameters. A lightweight interface (e.g., { url: string; resolveHandle: (v: unknown) => void; rejectHandle: (r?: any) => void }) would prevent accidental property misspellings and align with the typed enqueue functions.


590-628: getHistoricalPrices doesn't use the timeframe parameter and returns at most 2 current-price entries.

The method signature accepts timeframe but ignores it, and the implementation returns current snapshots, not historical data. The inline comment on lines 587-589 acknowledges this, but callers relying on the name and signature will be misled. Consider renaming to getCurrentPrices or removing the unused timeframe field to avoid confusion.


772-788: Remove unused getCacheExp and setCacheExp functions.

These helper functions are never called anywhere in the codebase and are not exported. They appear to be leftover experimental code (indicated by the "hack these in here" comment). Removing them will reduce confusion and clutter.

Iteration 1

prr-fix:prrc_kwdootnhpm6mw0f0
prr-fix:prrc_kwdootnhpm6mw0my
prr-fix:prrc_kwdootnhpm6mw0nz
prr-fix:prrc_kwdootnhpm6mw0m7
prr-fix:prrc_kwdootnhpm6mw0my
prr-fix:prrc_kwdootnhpm6mw0nt
prr-fix:prrc_kwdootnhpm6mw0ne
prr-fix:prrc_kwdootnhpm6mw0nj
prr-fix:prrc_kwdootnhpm6mw1ez
prr-fix:prrc_kwdootnhpm6mw-ag
prr-fix:prrc_kwdootnhpm6mw-ai
prr-fix:prrc_kwdootnhpm6mw-ak
prr-fix:prrc_kwdootnhpm6mw1ex
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/service.ts (1)

756-769: ⚠️ Potential issue | 🟡 Minor

stop() does not halt the queue polling timers.

checkQuoteQueues and checkSwapQueues use recursive setTimeout that runs indefinitely once startQueues() is called. stop() only flips isRunning but never cancels these timers, so the polling loops continue after the service is "stopped". This also means any items enqueued after stop() will still be processed.

Consider storing the timeout handles and clearing them in stop(), or checking a running flag at the top of each check*Queues function to break the loop.

🤖 Fix all issues with AI agents
In `@build.ts`:
- Around line 37-43: The catch block after the tsc invocation (the await $`tsc
--project tsconfig.build.json` around dtsStart) is currently swallowing errors;
change it to log the actual error/details (include the caught error variable)
and fail the build in CI/strict modes by exiting non‑zero (e.g.,
process.exit(1>) or rethrow) when an env flag like CI or STRICT_BUILD (or a
--strict CLI flag you add) is set; otherwise still warn but print full error
output and include the elapsed time using dtsStart so you can debug
missing/failed .d.ts generation.

In `@src/service.ts`:
- Around line 562-565: getTokenPair and getHistoricalPrices are calling fetch()
directly (the two fetch(...) calls creating inputResponse/outputResponse and the
later historical price fetches), bypassing the app's rate-limiting queue and
risking 429s; change these to use the same queued/throttled request path used
for quotes/swaps (i.e., route the token metadata and historical-price HTTP
requests through the existing queue dispatch/enqueue method instead of calling
fetch() directly), or add a dedicated throttled wrapper that uses the queue for
all requests that hit api.jup.ag so both getTokenPair and getHistoricalPrices
use the centralized rate-limiter.
🧹 Nitpick comments (7)
package.json (2)

17-21: Build/dev script inconsistency: build uses Bun but dev still uses tsup.

The production build (bun run build.ts) and the dev build (tsup --format esm --dts --watch) use entirely different toolchains, which can produce subtly different output (module resolution, tree-shaking, source maps). This makes it easy to miss issues that only appear in the production build.

Consider either:

  • Adding --watch support to build.ts and using it for dev too, or
  • Keeping tsup for both until the migration is complete.

7-9: engines.bun constraint may be too loose and lacks a node engine entry.

"bun": ">=1.0.0" allows any Bun version. Since Bun.build() API has evolved across versions, consider pinning to a narrower range (e.g., ">=1.1.0"). Also, if the package is consumed by Node-based projects (the build targets node), you may want a node engine entry too.

src/service.ts (5)

228-231: Static analysis: forEach callback implicitly returns a value.

Array.push() returns the new length, so the arrow function body implicitly returns a number. While forEach ignores return values, the linter flags this as suspicious. Use a block body or prefix with void.

Proposed fix
-    this.routeCache.forEach((v, k) => {
-      if (now > v.expiry) expired.push(k);
-    });
+    for (const [k, v] of this.routeCache) {
+      if (now > v.expiry) expired.push(k);
+    }

123-129: Headers are constructed twice for swap requests — redundant but worth simplifying.

executeSwap (line 342) already calls getApiHeaders() to build payload.headers. Then getSwapWithRetry calls getApiHeaders() again and merges with payload.headers (line 128). The double call is functionally harmless but adds confusion.

Let getSwapWithRetry own header construction exclusively, and have callers pass only the request body/method — or at least document the merge contract.

Proposed simplification in executeSwap
       const swapData = await swapEnqueue(`${JUPITER_BASE_URL}/swap`, {
         method: 'POST',
-        headers: getApiHeaders(),
         body: JSON.stringify(body),
       })

34-60: Network-level fetch failures (DNS, TCP, TLS) are not retried.

Both getQuoteWithRetry and getSwapWithRetry only retry on HTTP 429 responses. If fetch itself throws (e.g., TypeError for DNS resolution failure, connection reset, or timeout), the error propagates immediately and exhausts no retries. Wrapping the fetch call in a try/catch inside the loop and treating transient network errors as retryable would improve resilience.

Also applies to: 123-151


37-37: Debug console.log statements should use logger or be removed.

Multiple console.log calls are scattered throughout the queue and retry functions (e.g., lines 37, 43, 91, 100, 126, 166, 175). These bypass the structured logger used elsewhere in the service and will produce noisy output in production. Either promote them to logger.debug / logger.info or remove them.

Also applies to: 91-91, 100-100, 126-126, 166-166, 175-175


262-262: Use Math.trunc() instead of parseInt(String(amount)) for clarity and efficiency.

The parseInt(String(amount)) pattern works correctly, but is unnecessarily verbose. Since amount is already a number, Math.trunc(amount) directly truncates the value and is the idiomatic approach in modern JavaScript.

Suggested change
-      const intAmount = parseInt(String(amount))
+      const intAmount = Math.trunc(amount)

Iteration 1

prr-fix:prrc_kwdootnhpm6mw0no
prr-fix:prrc_kwdootnhpm6mw0nq
prr-fix:prrc_kwdootnhpm6nec1l
prr-fix:prrc_kwdootnhpm6nec1n
prr-fix:prrc_kwdootnhpm6neder
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/service.ts`:
- Around line 244-251: The queue timer loops started by startQueues() (which
calls checkQuoteQueues, checkSwapQueues, checkMetadataQueues) never get
cancelled because stop() only flips isRunning and queuesStarted prevents
restart; update the implementation to track the timer IDs (e.g., store them in a
module-level object or array like queueTimerIds) when scheduling setTimeout in
each check*Queue and add a new stopQueues() that clears those timers with
clearTimeout and resets queuesStarted; alternatively (or in addition) make each
check*Queue read a running flag (isRunning or queuesRunning) at the top and stop
rescheduling when false; finally call stopQueues() from stop() to ensure timers
are cancelled and stale chains are broken.
- Line 327: The code uses parseInt(String(amount)) to derive intAmount which is
fragile for large number-typed amount values; replace this expression with an
explicit numeric truncation like Math.trunc(Number(amount)) (or
Math.floor(Number(amount)) if you prefer rounding down) to drop any fractional
atomic units safely; update the assignment to intAmount and remove
parseInt(String(amount)) references so amount (the number parameter) is
converted via Number(...) and truncated with Math.trunc to avoid
scientific-notation parsing issues.
🧹 Nitpick comments (4)
src/service.ts (4)

405-409: Redundant double-application of API headers in executeSwap.

getApiHeaders(this.apiKey) is set in the payload at line 407, and then this.apiKey is also passed as the third argument to swapEnqueue. Inside getSwapWithRetry (line 127), headers are rebuilt from the apiKey param and merged with payload.headers — so the same headers are computed and spread twice.

Pick one path: either pass headers in the payload or rely on the apiKey parameter. The current duplication will silently mask bugs if the two diverge.

Suggested: drop headers from payload, let getSwapWithRetry handle it
       const swapData = await swapEnqueue(`${JUPITER_BASE_URL}/swap`, {
         method: 'POST',
-        headers: getApiHeaders(this.apiKey),
         body: JSON.stringify(body),
       }, this.apiKey)

293-296: Arrow function in forEach implicitly returns the result of Map.delete().

Static analysis flags this as useIterableCallbackReturn. Trivial fix: add braces or use a for...of loop.

Fix
-    expired.forEach(k => this.routeCache.delete(k));
+    for (const k of expired) this.routeCache.delete(k);

32-34: Scattered console.log debug statements — use logger consistently.

The module imports logger from @elizaos/core but the queue/retry functions use raw console.log for debug output (lines 34, 40, 89, 98, 125, 133, 174, 203, 231, 238). These should use logger.debug or logger.info for consistent log levels and structured output, and the commented-out console.log lines (32, 50, etc.) should be removed.

Also applies to: 40-40, 89-89, 98-98


648-684: getHistoricalPrices returns current snapshots — consider renaming.

The method name and return type (Array<{ timestamp: number; price: number }>) suggest time-series data, but it returns at most two current-price entries. The inline comments acknowledge this, but callers will find the API misleading. Consider renaming to getCurrentPrices or getPriceSnapshot to match the actual behavior. If the historical signature must be preserved for backward compatibility, a @deprecated JSDoc pointing to the new name would help.

Iteration 1

prr-fix:prrc_kwdootnhpm6nec1p
prr-fix:prrc_kwdootnhpm6neg29
prr-fix:prrc_kwdootnhpm6neg2-
Iteration 3

prr-fix:prrc_kwdootnhpm6mw0my
prr-fix:prrc_kwdootnhpm6mw1ex
prr-fix:prrc_kwdootnhpm6nec1n
prr-fix:prrc_kwdootnhpm6neipk
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/service.ts`:
- Around line 659-692: getCurrentPrices currently returns Array<{
timestamp:number; price:number }> with no mint identifier, breaking mapping when
one token is missing; modify the function (getCurrentPrices) to include the mint
address in each returned entry (e.g. { mint: string; timestamp: number; price:
number }) and update the method's return type accordingly, ensure the loop that
builds prices uses the loop variable mint to set the mint field and still only
includes entries when usdPrice is defined, and adjust any callers or type
annotations that expect the old shape.
- Around line 245-258: stopQueues() currently clears timers but leaves pending
items in the quotes, swaps, and metadata queues whose promises never settle;
modify stopQueues to iterate each queue (quotes, swaps, metadata), for each
queued item call its rejectHandle (or resolveHandle if you prefer resolve) with
a clear Error explaining the queue was stopped, then clear those arrays and
clear the corresponding timers (queueTimers.quotes, queueTimers.swaps,
queueTimers.metadata) before setting queuesStarted = false; reference the
stopQueues function and the queued-item fields resolveHandle/rejectHandle to
implement the drain-and-reject behavior so callers don't hang.

In `@test_fail.ts`:
- Around line 3-11: The test currently treats the thrown error as failure;
invert the exit logic in async function main so the expected path (await $`bun
run non_existent_command` throws) results in process.exit(0) and the unexpected
path (no throw) results in process.exit(1). Update the try/catch around the
awaited command in main: on success (no exception) log the unexpected outcome
and call process.exit(1), and in the catch block log the expected outcome and
call process.exit(0).
🧹 Nitpick comments (5)
src/service.ts (5)

297-312: Static analysis: forEach callback implicitly returns the boolean from Map.delete().

Line 304's arrow function k => this.routeCache.delete(k) returns the boolean result of delete(). While harmless at runtime, it triggers the Biome lint rule useIterableCallbackReturn. Use a block body to suppress the implicit return.

Proposed fix
-    expired.forEach(k => this.routeCache.delete(k));
+    expired.forEach(k => { this.routeCache.delete(k); });

413-417: Headers are applied twice for swap requests.

executeSwap constructs the payload with headers: getApiHeaders(this.apiKey) (line 415) and also passes this.apiKey as the third argument to swapEnqueue (line 417). Inside getSwapWithRetry (line 128-129), the API headers are merged again via { ...getApiHeaders(apiKey), ...payload.headers }. The result is correct (same key both times) but redundant — either pass headers in the payload or via apiKey, not both.

Proposed fix — let getSwapWithRetry own the headers
       const swapData = await swapEnqueue(`${JUPITER_BASE_URL}/swap`, {
         method: 'POST',
-        headers: getApiHeaders(this.apiKey),
         body: JSON.stringify(body),
       }, this.apiKey)

7-7: Queue items and enqueue functions are entirely untyped.

The three queues use any[] and the process*Queue functions accept untyped parameters. Introducing a simple interface per queue item would catch mismatches at compile time and improve IDE support.

Example interface sketch
interface QueueItem<T = unknown> {
  url: string;
  apiKey: string | null;
  resolveHandle: (value: T) => void;
  rejectHandle: (reason?: unknown) => void;
}

interface SwapQueueItem extends QueueItem {
  payload: RequestInit;
}

Also applies to: 61-74, 106-121, 182-196


636-639: Promise.all with two metadata-queue items serialises to ~2 s minimum.

Both metadataEnqueue calls enter the same 1 RPS queue, so they execute sequentially despite the Promise.all. This is technically correct for rate limiting, but if latency matters for getTokenPair, consider batching both mints into a single request (if the API supports it) or documenting the expected delay.


32-58: Retry loops lack a jitter component, risking thundering-herd on shared queues.

Both getQuoteWithRetry and getMetadataWithRetry (and getSwapWithRetry) use pure exponential backoff (delay *= 2) on 429s. When multiple items are queued and all hit 429 simultaneously, they'll all retry at the same wall-clock intervals. Adding a small random jitter (e.g., delay *= 2; delay += Math.random() * 500;) would spread retries out.

Also applies to: 198-218

Iteration 1

prr-fix:prrc_kwdootnhpm6mw1ex
prr-fix:prrc_kwdootnhpm6nedeq
prr-fix:prrc_kwdootnhpm6nfiha
prr-fix:prrc_kwdootnhpm6nfijz
prr-fix:prrc_kwdootnhpm6nfij1
Iteration 3

prr-fix:prrc_kwdootnhpm6mw-ag
prr-fix:prrc_kwdootnhpm6neg2-
prr-fix:prrc_kwdootnhpm6mw0nd
prr-fix:prrc_kwdootnhpm6neiph
prr-fix:prrc_kwdootnhpm6neipi
prr-fix:prrc_kwdootnhpm6nfig9
prr-fix:prrc_kwdootnhpm6nfijx
Changes:
- package.json: The `--watch` flag is passed to `build.ts`, but `build.ts` doesn't implement ...
- package.json: package.json switches build/dev scripts to Bun, but the repo doesn’t declare ...
- package.json: ### Dev script `--watch` flag is silently ignored

**Medium Severity**

<!-- ...
- package.json: ### Dev watch mode never activates for source files

**Medium Severity**

<!-...
Iteration 1

prr-fix:prrc_kwdootnhpm6pqgdl
Changes:
- build.ts: The `if (true) { // Always generate .d.ts } else { ... }` block is dead code ...
- build.ts: `dev` runs `bun run build.ts --watch`, but build.ts never reads CLI args and ...
- build.ts: build.ts invokes `tsc --project tsconfig.build.json`, but `typescript` is not...
- build.ts: ### Dead else branch due to `if (true)` conditional

**Low Severity**

<!-- D...
- build.ts: ### Build succeeds after declaration failures

**Medium Severity**

<!-- DESC...
- build.ts: _⚠️ Potential issue_ | _🟠 Major_

**Silently continuing after declaration ge...
- build.ts: ### Dev watch mode never activates in build script

**Medium Severity**

<!--...
- build.ts: ### Watch mode creates exponentially growing duplicate watchers

**Medium Sev...
- build.ts: ### Triple overlapping watch mechanisms cause redundant builds

**Low Severit...
- build.ts: ### Double watch creates repeated rebuild loops

**Medium Severity**

<!-- DE...
- build.ts: ### Watch mode skips d.ts regeneration

**Medium Severity**

<!-- DESCRIPTION...
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Changes:
- build.ts: The `if (true) { // Always generate .d.ts } else { ... }` block is dead code ...
- build.ts: `dev` runs `bun run build.ts --watch`, but build.ts never reads CLI args and ...
- build.ts: build.ts invokes `tsc --project tsconfig.build.json`, but `typescript` is not...
- build.ts: ### Dead else branch due to `if (true)` conditional

**Low Severity**

<!-- D...
- build.ts: ### Build succeeds after declaration failures

**Medium Severity**

<!-- DESC...
- build.ts: _⚠️ Potential issue_ | _🟠 Major_

**Silently continuing after declaration ge...
- build.ts: ### Dev watch mode never activates in build script

**Medium Severity**

<!--...
- build.ts: ### Watch mode creates exponentially growing duplicate watchers

**Medium Sev...
- build.ts: ### Triple overlapping watch mechanisms cause redundant builds

**Low Severit...
- build.ts: ### Double watch creates repeated rebuild loops

**Medium Severity**

<!-- DE...
- build.ts: ### Watch mode skips d.ts regeneration

**Medium Severity**

<!-- DESCRIPTION...
Changes:
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Module-level `jupiterApiKey` is shared a...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**`routeCache` grows without bound — poten...
- service.ts: If `JUPITER_API_KEY` is missing, start() only logs a warning and continues, b...
- build.ts: The `if (true) { // Always generate .d.ts } else { ... }` block is dead code ...
- build.ts: `dev` runs `bun run build.ts --watch`, but build.ts never reads CLI args and ...
- build.ts: build.ts invokes `tsc --project tsconfig.build.json`, but `typescript` is not...
- service.ts: ### API key not reset when service starts without key

**Low Severity**

<!--...
- build.ts: ### Dead else branch due to `if (true)` conditional

**Low Severity**

<!-- D...
- service.ts: ### Route cache key omits slippageBps causing stale results

**Medium Severit...
- service.ts: ### Token search with concatenated mints returns no results

**High Severity*...
- service.ts: ### Missing API key silently deadlocks all queued requests

**High Severity**...
- service.ts: ### Token search matches wrong field

**Medium Severity**

<!-- DESCRIPTION S...
- service.ts: ### Price parsing uses non-existent fields

**Medium Severity**

<!-- DESCRIP...
- build.ts: ### Build succeeds after declaration failures

**Medium Severity**

<!-- DESC...
- build.ts: _⚠️ Potential issue_ | _🟠 Major_

**Silently continuing after declaration ge...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Token and price fetches bypass the rate-...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Queue timers are never cancelled on `sto...
- service.ts: _⚠️ Potential issue_ | _🟡 Minor_

**`parseInt(String(amount))` is fragile fo...
- service.ts: ### Historical prices now return current snapshot

**Medium Severity**

<!-- ...
- service.ts: ### Global queue shutdown breaks active instances

**Medium Severity**

<!-- ...
- service.ts: ### Queue workers restart after service stop

**Medium Severity**

<!-- DESCR...
- service.ts: ### Zero-priced tokens are silently dropped

**Low Severity**

<!-- DESCRIPTI...
- service.ts: ### Queue timers can restart after service stop

**Medium Severity**

<!-- DE...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**`stopQueues()` leaves enqueued items wit...
- service.ts: _⚠️ Potential issue_ | _🟡 Minor_

**`getCurrentPrices` return type doesn't i...
- service.ts: ### Public price API contract was broken

**Medium Severity**

<!-- DESCRIPTI...
- service.ts: ### Stop leaves queued requests unresolved

**Medium Severity**

<!-- DESCRIP...
- service.ts: ### Service starts without required API key

**Medium Severity**

<!-- DESCRI...
- service.ts: ### Rate limiting no longer global

**Medium Severity**

<!-- DESCRIPTION STA...
- service.ts: ### Requests can hang when service is stopped

**Medium Severity**

<!-- DESC...
- service.ts: ### Token pair metrics are no longer pair-specific

**Medium Severity**

<!--...
- service.ts: ### Historical price endpoint no longer supported

**Medium Severity**

<!-- ...
- service.ts: ### Token volume field parsed incorrectly

**Low Severity**

<!-- DESCRIPTION...
- service.ts: ### Historical price endpoint path is invalid

**Medium Severity**

<!-- DESC...
- service.ts: ### Unsupported markets endpoint breaks pair lookup

**Medium Severity**

<!-...
- service.ts: ### Historical price path is not valid

**Medium Severity**

<!-- DESCRIPTION...
- service.ts: ### Token pair silently hides API failures

**Medium Severity**

<!-- DESCRIP...
- service.ts: ### Token pair response keys unexpectedly renamed

**Medium Severity**

<!-- ...
- service.ts: ### Historical prices return shape was broken

**Medium Severity**

<!-- DESC...
- service.ts: ### Token pair silently masks API failures

**Medium Severity**

<!-- DESCRIP...
- service.ts: ### Token pair failures are silently masked

**Medium Severity**

<!-- DESCRI...
- service.ts: ### Three independent queues exceed combined API rate limit

**Medium Severit...
- service.ts: ### Metadata queue infrastructure duplicates quote queue logic

**Medium Seve...
- service.ts: ### Sequential metadata queue makes `getTokenPair` very slow

**Medium Severi...
- service.ts: ### PR review tool comments left in production code

**Low Severity**

<!-- D...
- service.ts: ### Three queue checker functions are unreachable dead code

**Medium Severit...
- service.ts: ### PR review tool comments committed as code

**Low Severity**

<!-- DESCRIP...
- service.ts: ### Dead per-queue processor functions never called

**Medium Severity**

<!-...
- service.ts: ### Automated review comments left in production code

**Low Severity**

<!--...
- service.ts: ### `getTokenPair` bypasses rate-limiting queue with concurrent requests

**H...
- build.ts: ### Dev watch mode never activates in build script

**Medium Severity**

<!--...
- service.ts: ### Duplicate queue chains after rapid stop-start cycle

**Low Severity**

<!...
- build.ts: ### Watch mode creates exponentially growing duplicate watchers

**Medium Sev...
- service.ts: ### Markets endpoint likely non-existent, breaking `getTokenPair`

**Medium S...
- build.ts: ### Triple overlapping watch mechanisms cause redundant builds

**Low Severit...
- service.ts: ### `getTokenPair` calls non-existent Jupiter Markets API endpoint

**Medium ...
- service.ts: ### Auto-generated review comments left in production code

**Low Severity**
...
- service.ts: ### Unused timer fields create dead cleanup code

**Low Severity**

<!-- DESC...
- service.ts: ### Auto-generated review comments left in production code

**Medium Severity...
- service.ts: ### Two token lookups when one API call suffices

**Medium Severity**

<!-- D...
- build.ts: ### Double watch creates repeated rebuild loops

**Medium Severity**

<!-- DE...
- service.ts: ### Token price conversion assumes output has 6 decimals

**Medium Severity**...
- build.ts: ### Watch mode skips d.ts regeneration

**Medium Severity**

<!-- DESCRIPTION...
- build.ts: ### Review comment accidentally committed in build script

**Low Severity**

...
- service.ts: ### Unreliable token metadata lookup using search fallback

**Medium Severity...
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Iteration 2

prr-fix:prrc_kwdootnhpm6prb6t
prr-fix:prrc_kwdootnhpm6nf-eb
prr-fix:prrc_kwdootnhpm6mw1ex
prr-fix:prrc_kwdootnhpm6mw0my
prr-fix:prrc_kwdootnhpm6ngxtt
prr-fix:prrc_kwdootnhpm6pl9o2
prr-fix:prrc_kwdootnhpm6nla7p
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

### .prr/lessons.md

// Review: .prr/ is tool-managed directory; internal logs preserved for tool operation
- Fix for .prr/lessons.md:31 - tool made no changes without explanation - trying different approach
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal tool debugging state accidentally committed to repo

Low Severity

The .prr/lessons.md file contains internal debugging state from an automated review tool (prr), including entries like "tool made no changes without explanation - trying different approach" and "Direct LLM CANNOT_FIX" logs. This is tool-internal scaffolding and fix-attempt tracking, not meaningful project documentation. It adds noise to the repository and exposes internal automated review workflows that have no value for developers.

Fix in Cursor Fix in Web

Changes:
- service.ts: If `JUPITER_API_KEY` is missing, start() only logs a warning and continues, b...
- service.ts: ### API key not reset when service starts without key

**Low Severity**

<!--...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Queue timers are never cancelled on `sto...
- service.ts: _⚠️ Potential issue_ | _🟡 Minor_

**`getCurrentPrices` return type doesn't i...
- service.ts: ### Historical price API contract removed

**Medium Severity**

<!-- DESCRIPT...
- service.ts: ### Service starts without required API key

**Medium Severity**

<!-- DESCRI...
- service.ts: ### Requests can hang when service is stopped

**Medium Severity**

<!-- DESC...
- service.ts: ### Token pair metrics are no longer pair-specific

**Medium Severity**

<!--...
- service.ts: ### Unsupported markets endpoint breaks pair lookup

**Medium Severity**

<!-...
- service.ts: ### Markets endpoint likely non-existent, breaking `getTokenPair`

**Medium S...
- service.ts: ### Historical prices ratio formula is inverted

**High Severity**

<!-- DESC...
Changes:
- service.ts: If `JUPITER_API_KEY` is missing, start() only logs a warning and continues, b...
- service.ts: ### API key not reset when service starts without key

**Low Severity**

<!--...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Queue timers are never cancelled on `sto...
- service.ts: _⚠️ Potential issue_ | _🟡 Minor_

**`getCurrentPrices` return type doesn't i...
- service.ts: ### Historical price API contract removed

**Medium Severity**

<!-- DESCRIPT...
- service.ts: ### Service starts without required API key

**Medium Severity**

<!-- DESCRI...
- service.ts: ### Requests can hang when service is stopped

**Medium Severity**

<!-- DESC...
- service.ts: ### Token pair metrics are no longer pair-specific

**Medium Severity**

<!--...
- service.ts: ### Unsupported markets endpoint breaks pair lookup

**Medium Severity**

<!-...
- service.ts: ### `getTokenPair` bypasses rate-limiting queue with concurrent requests

**H...
- service.ts: ### Markets endpoint likely non-existent, breaking `getTokenPair`

**Medium S...
- service.ts: ### Historical prices ratio formula is inverted

**High Severity**

<!-- DESC...
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

return;
}
await buildDts();
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling in build onRebuild callback

Low Severity

The onRebuild callback calls await buildDts() without error handling. If buildDts() throws (like when type declarations are missing at line 48), the error becomes an unhandled promise rejection in the watch mode, potentially crashing the watch process or leaving it in an inconsistent state without clear error reporting.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6a16607.

Changes:
- service.ts: If `JUPITER_API_KEY` is missing, start() only logs a warning and continues, b...
- build.ts: The `if (true) { // Always generate .d.ts } else { ... }` block is dead code ...
- service.ts: ### API key not reset when service starts without key

**Low Severity**

<!--...
- build.ts: _⚠️ Potential issue_ | _🟠 Major_

**Silently continuing after declaration ge...
- service.ts: _⚠️ Potential issue_ | _🟠 Major_

**Queue timers are never cancelled on `sto...
- service.ts: _⚠️ Potential issue_ | _🟡 Minor_

**`getCurrentPrices` return type doesn't i...
- service.ts: ### Historical price API contract removed

**Medium Severity**

<!-- DESCRIPT...
- service.ts: ### Service starts without required API key

**Medium Severity**

<!-- DESCRI...
- service.ts: ### Requests can hang when service is stopped

**Medium Severity**

<!-- DESC...
- service.ts: ### Token pair metrics are no longer pair-specific

**Medium Severity**

<!--...
- service.ts: ### Unsupported markets endpoint breaks pair lookup

**Medium Severity**

<!-...
- service.ts: ### `getTokenPair` bypasses rate-limiting queue with concurrent requests

**H...
- build.ts: ### Dev watch mode never activates in build script

**Medium Severity**

<!--...
- service.ts: ### Markets endpoint likely non-existent, breaking `getTokenPair`

**Medium S...
- service.ts: ### Historical prices ratio formula is inverted

**High Severity**

<!-- DESC...
Explains reasoning for dismissed issues inline in code
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

src/service.ts Outdated
this.getApiHeaders()
) as any

(quoteData as any).totalLamportsNeeded = this.estimateLamportsNeeded(quoteData)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon causes ASI function-call misparse

High Severity

The expression on line 359 ending with ) as any has no semicolon, and line 361 starts with (quoteData as any). JavaScript's ASI rules do not insert a semicolon before (, so the parser chains these as a call expression: (await enqueueWithHeaders(...) as any)(quoteData as any).totalLamportsNeeded = .... At runtime this tries to invoke the resolved quote object as a function. Because quoteData is also referenced within its own const initializer, a ReferenceError (temporal dead zone) is thrown. TypeScript does not catch this since any is callable.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6a16607.

…o double watch

- build.ts: document single watch (Bun.build onRebuild only), no fs.watch
- build.ts: comment that we always generate .d.ts (no dead branch)
- service: fail-fast comment when JUPITER_API_KEY missing; already throws
- getHistoricalPrices: JSDoc + @deprecated, error points to getCurrentPrices()
- getTokenPair: return type includes liquidity/volume24h; remove duplicate fragment
- getTokenPrice: type metadataEnqueue result for decimals

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

src/service.ts Outdated
} else {
const outputToken = (await metadataEnqueue(
this.queueState,
`https://api.jup.ag/tokens/v2/${quoteMint}`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect Jupiter Tokens V2 API endpoint URL

Medium Severity

The token metadata URLs use https://api.jup.ag/tokens/v2/${mint} which is not a valid Jupiter API endpoint. According to Jupiter's API docs, the correct endpoint is https://api.jup.ag/tokens/v2/search?query=${mint} (a search endpoint that returns an array of MintInformation objects). The current URLs will likely return 404 errors, causing getTokenPrice to silently fall back to 0 for non-USDC quote mints and getTokenPair to always return null token metadata.

Additional Locations (2)

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6a16607.

### .prr/lessons.md

// Review: .prr/ is tool-managed directory; internal logs preserved for tool operation
- Fix for .prr/lessons.md:31 - tool made no changes without explanation - trying different approach
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal PRR tool debugging state committed to repo

Low Severity

The .prr/lessons.md file contains internal automated tool debugging state, including entries like "Direct LLM CANNOT_FIX", "Direct LLM ALREADY_FIXED", and "tool made no changes without explanation - trying different approach." These are LLM fix-loop artifacts, not curated team lessons. The .prr/ directory is also not listed in .gitignore, so it will persist in future commits.

Fix in Cursor Fix in Web

Iteration 1

prr-fix:prrc_kwdootnhpm6pevbs
prr-fix:prrc_kwdootnhpm6oy_tm
prr-fix:prrc_kwdootnhpm6ngxtt
prr-fix:prrc_kwdootnhpm6nehjm
prr-fix:prrc_kwdootnhpm6pfpkq
prr-fix:prrc_kwdootnhpm6pf8h3
prr-fix:prrc_kwdootnhpm6pgtuj
prr-fix:prrc_kwdootnhpm6neg29
prr-fix:prrc_kwdootnhpm6nfijz
Iteration 2

prr-fix:prrc_kwdootnhpm6mw0nk
prr-fix:prrc_kwdootnhpm6phdfg
Iteration 2

prr-fix:prrc_kwdootnhpm6ngqp5
prr-fix:prrc_kwdootnhpm6phdsy
Iteration 1

prr-fix:prrc_kwdootnhpm6nfiha
prr-fix:prrc_kwdootnhpm6ngpvu
Changes:
- build.ts: ### Missing error handling in build onRebuild callback Low Severity The `onRe...
Changes:
- build.ts: ### Missing error handling in build onRebuild callback Low Severity The `onRe...
Changes:
- package.json: package.json switches build/dev scripts to Bun, but the repo doesn’t declare ...
- package.json: ### Dev script `--watch` flag is silently ignored Medium Severity The `dev` s...
- package.json: ### Dev watch mode never activates for source files Medium Severity The `dev`...
Explains reasoning for dismissed issues inline in code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants