Skip to content

Fix frontend settings API configuration persistence - #34

Draft
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-investigation-311d
Draft

Fix frontend settings API configuration persistence#34
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-investigation-311d

Conversation

@cursor

@cursor cursor Bot commented Jun 5, 2026

Copy link
Copy Markdown

Summary

  • Restore a shared frontend API configuration path so Settings and SettingsModal persist/read the same backend URL key.
  • Keep auth tokens memory-only while allowing the API client to attach the current session token.
  • Repair frontend validation gates for Next 16 (Jest ignores E2E/build output, ESLint flat config, webpack dev/build, latest viem for wagmi build compatibility).
  • Clear small Python ruff/bandit gate findings found while running required checks.

Bug and impact

Recent security-hardening commits changed settings storage keys and removed localStorage auth-token persistence, but frontend/src/lib/api.ts still read the old api_url / auth_token keys. Configured users could save a backend URL/token in the UI, then authenticated API flows would hit the wrong backend and omit Authorization.

Validation

  • pnpm --filter @agentnexus/frontend test -- --runInBand
  • pnpm --filter @agentnexus/frontend type-check
  • pnpm --filter @agentnexus/frontend lint (0 errors, 3 existing warnings)
  • pnpm --filter @agentnexus/frontend build
  • python3 -m ruff check backend/src/agents/trading_bot agents/summarizer agents/paper-trader agent-runtime/docker/python-agent
  • python3 -m bandit -r backend/src/agents/trading_bot agents/summarizer agents/paper-trader agent-runtime/docker/python-agent -x agents/paper-trader/test_price_feed.py
  • python3 -m pytest agents/paper-trader/test_price_feed.py
  • Manual browser walkthrough recorded: settings reload preserves agentnexus_backend_url, leaves API key field empty, and auth_token remains absent from localStorage.
Open in Web View Automation 

Greptile Summary

This PR fixes a key-mismatch bug where frontend/src/lib/api.ts was reading api_url / auth_token from localStorage while the Settings UI was writing to a different key (agentnexus_backend_url), causing authenticated API calls to target the wrong backend. It introduces a shared api-config.ts module with legacy key migration and keeps auth tokens in memory only.

  • Introduces api-config.ts with a canonical storage key (agentnexus_backend_url), a one-time migration loop for the two legacy keys, and an in-memory-only session auth token — both settings surfaces (settings/page.tsx and SettingsModal.tsx) now read/write through this shared module.
  • Repairs the frontend tooling chain for Next 16: flat ESLint config, Jest path exclusions, webpack-mode dev/build flags, and a viem version bump to resolve wagmi peer-dep conflicts.
  • Cleans up Python bandit/ruff findings (adds URL scheme allowlist before urlopen, usedforsecurity=False for non-security md5, removes unused imports).

Confidence Score: 4/5

Safe to merge once the open thread about token-clearing behaviour is resolved; the core storage-key fix is correct and well-tested.

The storage-key unification and request-interceptor wiring are correct. However, the setSessionAuthToken function in api-config.ts silently ignores an empty string, so saving settings with a blank API Key field leaves the in-memory token unchanged even though the UI confirms the save — a real gap when a user needs to revoke a session token short of a full page reload. That unresolved behavioural issue (called out in a prior review thread) keeps this from a clean bill of health.

frontend/src/lib/api-config.ts — specifically the setSessionAuthToken guard that prevents callers from clearing the in-memory token via an empty string.

Important Files Changed

Filename Overview
frontend/src/lib/api-config.ts New canonical storage key and legacy migration logic; setSessionAuthToken silently ignores empty strings, preventing token revocation from the settings UI (already flagged in previous thread)
frontend/src/lib/api.ts Switches to new api-config imports; request interceptor correctly re-reads backend URL and session token on every request, fixing the stale-key bug described in the PR
frontend/src/app/settings/page.tsx Migrated to new api-config functions; API key field correctly starts empty on mount; same empty-string no-op path as SettingsModal
frontend/src/components/layout/SettingsModal.tsx Migrated to new api-config functions; resets API key field on each open; same empty-string token no-op as settings page
agents/paper-trader/price_feed.py Adds URL scheme allowlist (http/https only) before urllib.request.urlopen to address bandit B310; nosec comment added with clear justification
frontend/eslint.config.js Replaces .eslintrc.json with ESLint flat config; contains three non-existent react-hooks rule names set to 'off', which are harmless but spurious
frontend/src/lib/tests/api-config.test.js New unit tests covering migration of both legacy keys and in-memory auth token behaviour; good coverage of the happy paths
frontend/package.json Bumps viem to ^2.52.2, removes viem override pin, switches dev and lint scripts for Next 16 / flat ESLint compatibility

Sequence Diagram

sequenceDiagram
    participant UI as Settings UI (page / modal)
    participant AC as api-config.ts
    participant LS as localStorage
    participant API as api.ts (Axios)
    participant BE as Backend

    UI->>AC: persistBackendBaseUrl(url)
    AC->>LS: setItem('agentnexus_backend_url', url)
    AC->>LS: removeItem('backend_base_url')
    AC->>LS: removeItem('api_url')

    UI->>AC: setSessionAuthToken(token)
    AC-->>AC: "sessionAuthToken = token (memory only)"

    UI-->>UI: Settings saved

    Note over API,BE: On next API request
    API->>AC: getBackendBaseUrl()
    AC->>LS: getItem('agentnexus_backend_url')
    LS-->>AC: url
    AC-->>API: url
    API->>AC: getSessionAuthToken()
    AC-->>API: sessionAuthToken (memory)
    API->>BE: GET /api/... Authorization: Bearer token
    BE-->>API: 200 OK
Loading

Fix All in Codex Fix All in Cursor

Reviews (2): Last reviewed commit: "chore: refresh frontend lockfile for con..." | Re-trigger Greptile

cursoragent and others added 2 commits June 5, 2026 11:53
Co-authored-by: up2itnow0822 <up2itnow0822@gmail.com>
Co-authored-by: up2itnow0822 <up2itnow0822@gmail.com>
Comment on lines +47 to +52
export const setSessionAuthToken = (token: string) => {
const trimmedToken = token.trim();
if (trimmedToken) {
sessionAuthToken = trimmedToken;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 setSessionAuthToken silently no-ops when called with an empty string, making it impossible to clear an active in-memory token through the settings UI. Both settings/page.tsx and SettingsModal.tsx call setSessionAuthToken(apiKeyInputRef.current?.value || '') on save; if a token is already set and the user saves with a blank API Key field (e.g., to update only the backend URL), the old token persists with no indication. The "Settings saved" confirmation fires regardless, leaving users no way to revoke their session token short of a full page reload.

Suggested change
export const setSessionAuthToken = (token: string) => {
const trimmedToken = token.trim();
if (trimmedToken) {
sessionAuthToken = trimmedToken;
}
};
export const setSessionAuthToken = (token: string) => {
sessionAuthToken = token.trim();
};

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Cursor

Comment on lines +19 to +26
for (const legacyKey of LEGACY_BACKEND_URL_STORAGE_KEYS) {
const legacyUrl = storage.getItem(legacyKey)?.trim();
if (legacyUrl) {
storage.setItem(BACKEND_URL_STORAGE_KEY, legacyUrl);
storage.removeItem(legacyKey);
return legacyUrl;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 When both legacy keys (backend_base_url and api_url) are present simultaneously — a realistic state since each was written by a different component before this PR — the migration loop migrates the first match but only removes that one key. The canonical key is written and subsequent calls return early, so api_url stays in localStorage indefinitely. It never affects the returned URL, but the old key is never cleaned up unless the user explicitly saves settings again via persistBackendBaseUrl.

Suggested change
for (const legacyKey of LEGACY_BACKEND_URL_STORAGE_KEYS) {
const legacyUrl = storage.getItem(legacyKey)?.trim();
if (legacyUrl) {
storage.setItem(BACKEND_URL_STORAGE_KEY, legacyUrl);
storage.removeItem(legacyKey);
return legacyUrl;
}
}
for (const legacyKey of LEGACY_BACKEND_URL_STORAGE_KEYS) {
const legacyUrl = storage.getItem(legacyKey)?.trim();
if (legacyUrl) {
storage.setItem(BACKEND_URL_STORAGE_KEY, legacyUrl);
LEGACY_BACKEND_URL_STORAGE_KEYS.forEach((k) => storage.removeItem(k));
return legacyUrl;
}
}

Fix in Codex Fix in Cursor

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Duplicate draft — recommend close

Superseded by #35. Note: this branch has a large lockfile diff that may cause container-scan noise — #35 has a cleaner incremental lockfile update.

Open in Web View Automation 

Sent by Cursor Automation: Untitled

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