Skip to content

perf: cut dist size 56MB->8MB, dedupe hardware-probe fetches via React Query - #161

Merged
tonythethompson merged 7 commits into
mainfrom
perf/vite-bundle-size
Aug 7, 2026
Merged

tonythethompson merged 7 commits into
mainfrom
perf/vite-bundle-size

Conversation

@tonythethompson

@tonythethompson tonythethompson commented Aug 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Strip onnxruntime-web's unused .wasm from dist at build time (all call sites load wasm from jsdelivr CDN, never local) — dist 56MB -> ~8MB
  • Serve precompressed assets via express-static-gzip with immutable cache headers on hashed assets, no-cache on index.html
  • Move @openai/codex-sdk to optionalDependencies (~408MB installer savings when opted out)
  • Delete dead BatchComparisonChart.tsx + recharts dependency (zero importers)
  • Resize logo.png 966x966/688KB -> 128x128/24KB, add explicit width/height to tags, delete unreferenced app.ico
  • Lazy-load jszip in ExecutionWorkspace's export handler instead of the eager entry chunk
  • Add shared useHardwareProbe() query — 7 panels on the single-scroll dashboard each independently fetched the hardware probe on mount; React Query now dedupes concurrent mounts into one request and caches across navigation
  • Migrate useKbSync polling and InputEnvironmentPanel's HF-token status/save/delete from hand-rolled fetch+state to useQuery/useMutation

Test plan

  • tsc --noEmit clean
  • eslint 0 errors (1 pre-existing warning, unrelated)
  • pnpm build succeeds, dist verified wasm-free
  • 905/905 unit tests pass
  • 128/128 component tests pass
  • 63/63 integration tests pass
  • 1 pre-existing unrelated failure in pathIsolation.test.ts (Windows-PATH assumption bug, file untouched by this PR)
  • Ran built server, verified Cache-Control/Content-Encoding headers over HTTP

🤖 Generated with Claude Code

Review in cubic

- Serve precompressed assets via express-static-gzip with immutable
  cache headers on hashed assets, no-cache on index.html
- Strip onnxruntime-web's unused .wasm binaries from dist at build time
  (all call sites load wasm from the jsdelivr CDN, never the local copy)
- Move @openai/codex-sdk to optionalDependencies (saves ~408MB install
  for consumers who skip optional deps)
- Delete dead BatchComparisonChart.tsx and the recharts dependency
  (zero importers)
- Resize logo.png from 966x966/688KB to 128x128/24KB, add explicit
  width/height to both <img> usages, delete unreferenced app.ico
- Lazy-load jszip in ExecutionWorkspace's export handler instead of
  bundling it into the eager entry chunk
…React Query

The dashboard mounts all pipeline panels simultaneously (single scroll
page, not tab-switched), so 7 components each independently calling
fetchHardwareProbe() on mount meant up to 7 duplicate probe requests
per page load with no cache reuse across navigation.

- Add useHardwareProbe()/useRefreshHardwareProbe() sharing one query
  key across BatchProcessingPanel, ExecutionWorkspace, IHVIntegrationPanel,
  InputEnvironmentPanel, ProviderInspector, StepInspector, and
  VramEstimateBanner. React Query collapses concurrent mounts into a
  single request instead of firing one per component.
- IHVIntegrationPanel keeps its install-triggered "refresh" and
  first-probe auto-apply-recommended-provider behavior, now driven by
  the shared query instead of local-only state.
- VramEstimateBanner's prop-driven fallback (force-refresh when the
  passed-in probe is missing RAM info) preserved as-is, just reading
  from the shared cache when no prop is given.
- Migrate useKbSync's status polling to useQuery (interval + focus
  refetch replace the old setInterval + visibilitychange listener) and
  its sync action to useMutation.
- Migrate InputEnvironmentPanel's HF token status/save/delete from
  hand-rolled fetch+state to useQuery + two useMutations.
- Add renderWithProviders test helper (QueryClientProvider via RTL's
  `wrapper` option, so it survives rerender()) for the 4 component test
  files that now exercise React Query hooks.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @tonythethompson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@vercel

vercel Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
olive-studio Ready Ready Preview Aug 7, 2026 2:32pm

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR centralizes hardware probing and knowledge-base synchronization with React Query. It adds gzip-aware asset serving, removes unused build output, lazy-loads JSZip, improves optional SDK errors, and adds intrinsic logo dimensions.

Changes

Asset delivery and bundle output

Layer / File(s) Summary
Gzip static serving
package.json, server.ts
Adds express-static-gzip and applies gzip preference with separate cache policies for entry and asset responses.
Bundle output cleanup
vite.config.ts, src/components/features/ExecutionWorkspace.tsx
Removes ORT WebAssembly assets and the manual chart vendor chunk during builds. Loads JSZip when an OWR download starts.

Shared hardware probe state

Layer / File(s) Summary
Probe query foundation
src/lib/hooks/useHardwareProbe.ts, src/components/features/__tests__/testUtils.tsx
Adds cached and refreshable hardware probe hooks. Adds a provider-aware test renderer with disabled retries.
Panel probe integration
src/components/features/BatchProcessingPanel.tsx, src/components/features/ExecutionWorkspace.tsx, src/components/features/IHVIntegrationPanel.tsx, src/components/features/recipe-graph/**, src/components/features/*test.tsx
Migrates hardware probing to shared query data. Recommended-provider initialization and manual refresh use the shared hooks.
Environment state integration
src/components/features/InputEnvironmentPanel.tsx, src/components/features/VramEstimateBanner.tsx
Migrates probe consumers and Hugging Face token operations to React Query queries and mutations. VRAM refreshes use cached and forced probe results.

Knowledge-base synchronization

Layer / File(s) Summary
Status and sync query flow
src/lib/hooks/useKbSync.ts
Replaces local synchronization state with React Query status and mutation handling, including polling, invalidation, timeout handling, normalized errors, and stale-status auto-sync.

Runtime loading and UI cleanup

Layer / File(s) Summary
Optional SDK loading
src/lib/codex/codexAgent.ts, package.json
Loads the optional Codex SDK with descriptive failure handling and installation guidance.
Logo image dimensions
src/App.tsx, src/components/TitleBar.tsx
Adds explicit intrinsic dimensions to both logo images and reformats the title-bar documentation comment.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: greptile-apps

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two primary changes: reducing distribution size and deduplicating hardware-probe requests.
Description check ✅ Passed The description directly explains the distribution-size reduction, React Query migrations, caching changes, and validation results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Pipeline Stage Enum Ordering ✅ Passed The PR diff and checked-out solution contain no SessionWorkflowStage enum, member references, or related inequality comparisons; the ordering and legacy-mapping checks are not applicable.
Gpu/Cpu Runtime Boundary ✅ Passed The PR changes no files under inference/ and does not modify any managed CPU/GPU requirements file, so the GPU/CPU boundary checks are not applicable.
Managed Host Restart Safety ✅ Passed The PR changes frontend hooks, panels, server static serving, and build metadata; repository searches found no target host managers, lease trackers, health fields, or stop/restart paths.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/vite-bundle-size
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch perf/vite-bundle-size

Warning

Review ran into problems

🔥 Problems

Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. Analyzed tonythethompson/QuickShell, tonythethompson/numan, tonythethompson/dependency-chain-substrate, skipped Trackdubllc/Trackdub.


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.

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Perf: shrink dist/install and dedupe hardware probe fetches via React Query

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Strip unused ort WASM and remove recharts/codex dependency to shrink dist/install.
• Serve pre-gzipped static assets with immutable caching; disable caching for index.html.
• Use React Query for shared hardware probe, KB sync polling, and HF token mutations.
Diagram

graph TD
  A["Vite build"] --> B["strip ort WASM"] --> C[("dist assets")]
  D["Express server"] --> E["static gzip middleware"] --> C
  F["Dashboard panels"] --> G("React Query cache") --> H["Hardware probe API"] --> D

  subgraph Legend
    direction LR
    _proc["Component/Step"] ~~~ _cache("Cache") ~~~ _art[("Artifact")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Lift hardware probe to a parent and pass via props/context
  • ➕ Avoids adding/expanding React Query usage for this concern
  • ➕ Makes the dataflow explicit (single fetch in one place)
  • ➖ Requires threading probe state through many component boundaries
  • ➖ Harder to reuse across routes/navigation vs query cache
  • ➖ Doesn't generalize to other polling/mutation cases (KB sync, token status)
2. Centralize probe/KB state in a global store (e.g., Zustand)
  • ➕ One source of truth with explicit actions/selectors
  • ➕ No dependency on React Query semantics (staleTime, invalidation)
  • ➖ Reimplements caching/deduping/error states already provided by React Query
  • ➖ More bespoke code paths and test surface area
  • ➖ Requires manual polling/focus refresh behaviors
3. Move asset compression/caching to an edge/proxy (nginx/CDN)
  • ➕ Best-in-class caching/compression at the edge
  • ➕ Reduces Node server responsibilities
  • ➖ Not always available for local/desktop/server-embedded deployments
  • ➖ Adds deployment complexity and environment-specific behavior

Recommendation: Current approach is strong: React Query provides low-boilerplate request deduping/caching for the many concurrently-mounted panels, and the Express static-gzip middleware aligns with the existing Vite gzip output. The alternatives mainly trade off simplicity in one area for complexity elsewhere (prop drilling or reimplementing cache/polling).

Files changed (20) +600 / -782

Enhancement (4) +49 / -14
App.tsxAdd explicit dimensions to dashboard logo image +2/-0

Add explicit dimensions to dashboard logo image

• Adds width/height attributes to the logo <img> to reduce layout shift and improve rendering stability.

src/App.tsx

TitleBar.tsxAdd explicit dimensions to title bar logo image +11/-5

Add explicit dimensions to title bar logo image

• Adds width/height attributes to the title bar logo <img> for predictable sizing and layout stability.

src/components/TitleBar.tsx

ExecutionWorkspace.tsxLazy-load JSZip and reuse shared hardware probe query +3/-9

Lazy-load JSZip and reuse shared hardware probe query

• Replaces eager JSZip import with a dynamic import inside the export handler to reduce entry-chunk size. Switches hardware probe loading to useHardwareProbe instead of per-mount fetches.

src/components/features/ExecutionWorkspace.tsx

useHardwareProbe.tsIntroduce shared hardware-probe React Query hooks +33/-0

Introduce shared hardware-probe React Query hooks

• Adds a shared query key and a useHardwareProbe hook with a 5-minute staleTime for dashboard-wide reuse. Adds useRefreshHardwareProbe to force a refresh and publish results into the query cache.

src/lib/hooks/useHardwareProbe.ts

Refactor (7) +208 / -231
BatchProcessingPanel.tsxUse shared useHardwareProbe() query and one-time provider seeding +11/-11

Use shared useHardwareProbe() query and one-time provider seeding

• Replaces per-mount hardware probe fetching with the shared React Query-backed useHardwareProbe hook. Ensures the recommended provider is applied only once when probe data first becomes available.

src/components/features/BatchProcessingPanel.tsx

IHVIntegrationPanel.tsxRefactor probe flow to React Query with explicit refresh semantics +33/-34

Refactor probe flow to React Query with explicit refresh semantics

• Replaces local probe state management with useHardwareProbe and useRefreshHardwareProbe, including a manual refresh path that bypasses server cache. Preserves auto-applying recommended provider on first probe result arrival.

src/components/features/IHVIntegrationPanel.tsx

InputEnvironmentPanel.tsxMigrate HF token status and hardware probe to React Query +41/-53

Migrate HF token status and hardware probe to React Query

• Replaces manual fetch+state for HF token status with a useQuery and converts save/delete to useMutation with cache updates. Switches hardware probe to the shared useHardwareProbe query for consistent behavior across panels.

src/components/features/InputEnvironmentPanel.tsx

VramEstimateBanner.tsxIntegrate shared hardware probe query with forced refresh fallback +23/-19

Integrate shared hardware probe query with forced refresh fallback

• Uses the shared probe query when no prop is provided and adds a refresh path for cases where the prop probe is missing system RAM. Publishes refreshed probe results through the shared cache so other consumers can update too.

src/components/features/VramEstimateBanner.tsx

StepInspector.tsxUse shared useHardwareProbe() instead of per-mount fetch +2/-9

Use shared useHardwareProbe() instead of per-mount fetch

• Removes local useEffect-based probe fetching and reads the hardware probe via the shared React Query hook for deduped loading.

src/components/features/recipe-graph/StepInspector.tsx

ProviderInspector.tsxUse shared hardware probe query for provider availability +4/-18

Use shared hardware probe query for provider availability

• Replaces local probe state/loading management with useHardwareProbe, simplifying the inspector and enabling request deduping.

src/components/features/recipe-graph/inspectors/ProviderInspector.tsx

useKbSync.tsRewrite KB sync polling/mutations using React Query +94/-87

Rewrite KB sync polling/mutations using React Query

• Replaces manual interval/visibility polling and sync state with useQuery refetchInterval plus a useMutation for sync. Centralizes HTTP/error handling and uses cache invalidation/fetchQuery for refresh semantics.

src/lib/hooks/useKbSync.ts

Tests (5) +160 / -138
BatchProcessingPanel.test.tsxUpdate tests to render with React Query provider +2/-2

Update tests to render with React Query provider

• Switches tests to use renderWithProviders so components using React Query hooks have a QueryClient context during tests.

src/components/features/BatchProcessingPanel.test.tsx

ExecutionWorkspace.test.tsxUpdate tests to render with React Query provider +2/-2

Update tests to render with React Query provider

• Uses renderWithProviders to supply a QueryClient for components that now depend on React Query (e.g., shared hardware probe).

src/components/features/ExecutionWorkspace.test.tsx

IHVIntegrationPanel.test.tsxUpdate tests to render with React Query provider +2/-2

Update tests to render with React Query provider

• Converts tests to use renderWithProviders to satisfy React Query usage introduced in the panel.

src/components/features/IHVIntegrationPanel.test.tsx

InputEnvironmentPanel.test.tsxUpdate tests to render with React Query provider +132/-132

Update tests to render with React Query provider

• Switches unit tests to use renderWithProviders while keeping existing mocks for fetch routes and pipeline store.

src/components/features/InputEnvironmentPanel.test.tsx

testUtils.tsxAdd renderWithProviders() with per-test QueryClient +22/-0

Add renderWithProviders() with per-test QueryClient

• Introduces a test helper that wraps renders in a QueryClientProvider, with retries disabled to prevent stalled tests. Ensures the provider survives rerenders by using RTL's wrapper option.

src/components/features/tests/testUtils.tsx

Other (4) +183 / -399
package.jsonMake codex optional; add gzip static middleware; drop recharts +118/-116

Make codex optional; add gzip static middleware; drop recharts

• Moves @openai/codex-sdk from dependencies to optionalDependencies to reduce default install footprint. Adds express-static-gzip for serving precompressed assets and removes recharts from dependencies.

package.json

pnpm-lock.yamlLockfile updates for optional codex, express-static-gzip, and recharts removal +23/-277

Lockfile updates for optional codex, express-static-gzip, and recharts removal

• Updates the lockfile to add express-static-gzip, mark @openai/codex-sdk as optional, and remove recharts and its transitive d3/redux-related packages.

pnpm-lock.yaml

server.tsServe precompressed dist assets with caching headers via express-static-gzip +16/-1

Serve precompressed dist assets with caching headers via express-static-gzip

• Replaces express.static with express-static-gzip in production mode to preferentially serve .gz assets. Adds Cache-Control behavior: immutable caching for hashed assets and no-cache for index.html.

server.ts

vite.config.tsStrip unused onnxruntime-web WASM post-build and remove recharts chunking +26/-5

Strip unused onnxruntime-web WASM post-build and remove recharts chunking

• Adds a Vite build plugin that deletes copied onnxruntime-web wasm assets from dist/assets after bundling. Removes recharts-specific chunking and keeps gzip compression enabled for production assets.

vite.config.ts

@greptile-apps

greptile-apps Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR substantially reduces distribution and installation size, adds compressed static serving with differentiated cache policies, and consolidates repeated hardware and status requests through React Query.

  • Removes unused runtime assets and dependencies, lazy-loads JSZip, and makes the Codex SDK optional.
  • Serves gzip-compressed production assets while reserving immutable caching for Vite-hashed JavaScript and CSS.
  • Shares hardware-probe results and migrates knowledge-base synchronization and Hugging Face token operations to React Query.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
server.ts Adds precompressed static serving and correctly distinguishes index, hashed build outputs, and stable public assets for caching.
src/components/features/VramEstimateBanner.tsx The follow-up fix ensures a complete parent probe supersedes an older forced probe.
src/lib/hooks/useHardwareProbe.ts Centralizes hardware probing under a shared React Query key with refresh support.
src/lib/hooks/useKbSync.ts Migrates synchronization polling to React Query.
src/components/features/InputEnvironmentPanel.tsx Migrates Hugging Face token status and update operations to queries and mutations.
vite.config.ts Adds compressed output generation and strips unused ONNX Runtime WebAssembly assets.
package.json Adds compressed-static serving, makes the Codex SDK optional, and removes the unused charting dependency.

Reviews (6): Last reviewed commit: "Merge branch 'main' into perf/vite-bundl..." | Re-trigger Greptile

Comment thread src/components/features/VramEstimateBanner.tsx Outdated
Comment thread server.ts
@qodo-code-review

qodo-code-review Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. index.html Cache-Control too weak ✓ Resolved 📜 Skill insight ➹ Performance
Description
server.ts sets Cache-Control for index.html to only no-cache (missing no-store and
must-revalidate), and the SPA fallback serves index.html via res.sendFile() without explicitly
setting the required header, risking stale UI after deploys. This violates the requirement that
index.html must not use long-lived cache behavior and must be served with strict no-cache headers.
Code

server.ts[R215-218]

+            res.setHeader(
+              "Cache-Control",
+              filePath.endsWith("index.html") ? "no-cache" : "public, max-age=31536000, immutable",
+            ),
Relevance

●●● Strong

Repo accepts server.ts middleware correctness fixes; stricter index.html no-cache headers likely
enforced.

PR-#14

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2450414 requires index.html to be served with strict no-cache headers. The new
setHeaders logic only sets no-cache for index.html and the SPA fallback path serves
index.html without explicitly setting the required Cache-Control header.

server.ts[207-227]
Skill: vite-react-best-practices

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`index.html` is currently served with `Cache-Control: no-cache` (missing `no-store, must-revalidate`), and the SPA fallback `res.sendFile(indexHtml)` does not explicitly set any Cache-Control header. This can cause browsers/intermediaries to reuse a stale HTML shell after a deployment.

## Issue Context
The compliance rule requires `index.html` to be served with `no-cache, no-store, must-revalidate` (or at most `public, max-age=0, must-revalidate`).

## Fix Focus Areas
- server.ts[207-227]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Forced probe stays stale ✓ Resolved 🐞 Bug ≡ Correctness
Description
Once an incomplete prop triggers a forced probe, VramEstimateBanner always prefers that local
result over every later hardwareProbeProp. A subsequent hardware rescan can therefore update the
parent and shared cache while VRAM estimates and fit warnings continue using older hardware data.
Code

src/components/features/VramEstimateBanner.tsx[R57-60]

+  const hardwareProbe =
+    hardwareProbeProp !== undefined
+      ? (forcedProbe ?? hardwareProbeProp)
+      : (sharedProbeQuery.data ?? null);
Relevance

●●● Strong

Stale-state precedence issues are commonly fixed; forcedProbe should not permanently override
refreshed probe props.

PR-#97

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The effect can set forcedProbe but never clears it when the prop changes, and the selection
expression gives it unconditional precedence; the IHV panel later refreshes the shared query and
passes its updated value as the prop.

src/components/features/VramEstimateBanner.tsx[44-60]
src/components/features/IHVIntegrationPanel.tsx[125-138]
src/components/features/IHVIntegrationPanel.tsx[467-479]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A forced probe result permanently shadows later parent-provided probe updates, leaving VRAM calculations stale after subsequent rescans.

## Issue Context
The local override is only needed while replacing an incomplete prop. Clear it when the prop changes, compare probe timestamps, or use the shared query result directly after refresh.

## Fix Focus Areas
- src/components/features/VramEstimateBanner.tsx[44-60]
- src/components/features/IHVIntegrationPanel.tsx[125-138]
- src/components/features/IHVIntegrationPanel.tsx[467-479]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Codex optional dependency has no missing-module handling ✓ Resolved 🐞 Bug ☼ Reliability
Description
Moving @openai/codex-sdk to optionalDependencies means pnpm may skip or fail to install it, yet the
codex provider is unconditionally registered at import time (src/server/services/ai/index.ts imports
./codex.ts) and codexAgent.ts's loadCodexSdk() dynamically imports '@openai/codex-sdk' with no
try/catch. If the optional package is absent, the first Codex request throws a raw module-resolution
error that surfaces as an unfriendly generic error message via /api/codex/ask instead of a clear
'optional dependency not installed' message.
Code

package.json[R75-77]

+  "optionalDependencies": {
+    "@openai/codex-sdk": "^0.145.0"
+  },
Relevance

●●● Strong

Team often adds explicit error handling/validation; optional dep needs friendly failure instead of
raw module error.

PR-#32

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
src/lib/codex/codexAgent.ts's loadCodexSdk() does return dynamicImport("@openai/codex-sdk") with
no error handling, and src/server/services/ai/codex.ts calls registerProvider(...) unconditionally
at module load, so the codex provider always appears available in the registry regardless of whether
the optional package was actually installed.

package.json[75-77]
src/lib/codex/codexAgent.ts[43-49]
src/server/services/ai/codex.ts[21-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With `@openai/codex-sdk` now in `optionalDependencies`, it may not be installed in some environments (npm/pnpm can skip failed optional installs). The Codex provider is registered unconditionally and the SDK is dynamically imported without error handling, so a missing package surfaces as a raw module-resolution error to the end user.

## Issue Context
`@openai/codex-sdk` moved from `dependencies` to `optionalDependencies` in this PR to save ~408MB when opted out. The codex provider plugin (`src/server/services/ai/codex.ts`) is always registered via the side-effect import chain (`src/server/services/ai/index.ts` -> `./codex.ts`), and `codexAgent.ts`'s `loadCodexSdk()` performs a bare dynamic `import("@openai/codex-sdk")`.

## Fix Focus Areas
- src/lib/codex/codexAgent.ts[43-60]
- src/server/services/ai/codex.ts[1-28]
- src/server/routes/ai/codexRoutes.ts[73-88]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Stable assets cached immutable ✓ Resolved 🐞 Bug ☼ Reliability
Description
The production server assigns a one-year immutable policy to every file except index.html,
including stable public URLs such as /assets/logo.png. After an upgrade changes one of these
assets without changing its URL, existing clients can keep rendering the old content for up to a
year without revalidation.
Code

server.ts[217]

+              filePath.endsWith("index.html") ? "no-cache" : "public, max-age=31536000, immutable",
Relevance

●● Moderate

Cache policy for non-hashed assets is a tradeoff; no close precedent whether they’ll relax
immutable.

PR-#14

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new header callback applies immutable to all non-index files, while both logo call sites
request the unchanged, non-hashed /assets/logo.png URL.

server.ts[207-220]
src/App.tsx[200-205]
src/components/TitleBar.tsx[67-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The static server marks stable-name public assets immutable for one year, so changed assets can remain stale across application upgrades.

## Issue Context
Vite-generated hashed bundles are safe to cache immutably, but public assets such as `/assets/logo.png` retain a stable URL.

## Fix Focus Areas
- server.ts[213-218]
- src/App.tsx[200-205]
- src/components/TitleBar.tsx[67-73]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 86 rules
✅ REVIEW.md
Review mode: 🧠 Deep: This is a broad, bug-dense behavioral PR spanning server caching/build output, dependency/runtime packaging, and multiple independent React Query migrations across several UI paths.

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread server.ts Outdated
Comment thread server.ts Outdated
Comment thread src/components/features/VramEstimateBanner.tsx
Comment thread package.json
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (1) · ☑ Fixed (1)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 1 fixed
  • ☑ Fixed: index.html Cache-Control too weak

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
src/components/features/ExecutionWorkspace.tsx (1)

386-433: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle JSZip chunk-load failures.

await import("jszip") runs before the existing try, so a failed lazy chunk load escapes ZIP Generation failed. Move the dynamic import into that try block before creating the Zip file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/features/ExecutionWorkspace.tsx` around lines 386 - 433, Move
the dynamic import of JSZip inside the existing try block in the
bundle-generation flow, before constructing the Zip instance. Ensure failures
from await import("jszip") are caught by the existing “ZIP Generation failed”
handler, while preserving the current archive creation and download behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server.ts`:
- Around line 214-218: Update the SPA fallback flow around
res.sendFile(indexHtml) to set the response Cache-Control header to no-cache
before sending index.html. Keep the existing setHeaders callback for
expressStaticGzip responses unchanged.
- Around line 207-221: Update the production server listener configuration near
the Express startup call to bind to 127.0.0.1 instead of 0.0.0.0 by default.
Preserve the existing port and startup behavior, and do not add remote binding
unless an authenticated explicit mode already exists.

In `@src/components/features/InputEnvironmentPanel.tsx`:
- Around line 136-181: Update the hfTokenStatusQuery queryFn and
clearTokenMutation mutationFn to throw when their fetch responses are not ok,
checking r.ok before parsing status data or completing deletion. Ensure the
component renders a distinct query-error state rather than treating failed
status requests as “none,” while preserving cache updates only after successful
operations; add tests covering failed status and delete responses.

In `@src/components/features/VramEstimateBanner.tsx`:
- Around line 39-60: Update useHardwareProbe to accept an enabled option, and
call it from VramEstimateBanner with the query disabled whenever
hardwareProbeProp !== undefined. Preserve the existing shared-query behavior
when no prop is supplied, while retaining the explicit refreshHardwareProbe flow
for props with missing system RAM.

In `@src/lib/hooks/useHardwareProbe.ts`:
- Around line 14-19: Update useHardwareProbe’s useQuery configuration to disable
automatic retries by adding retry: false, preserving the existing query key,
fetcher, and stale-time behavior.

In `@src/lib/hooks/useKbSync.ts`:
- Around line 55-68: Update postKbSync to require data.ok === true before
returning, rejecting 2xx responses with ok false, a missing ok field, or
malformed JSON so React Query receives the mutation error. Preserve the existing
HTTP-status error and rate-limit messaging, and add hook tests covering failed
2xx bodies and malformed 2xx responses.
- Around line 125-129: Update the useQuery configuration in the statusQuery
definition to set retry: false, preserving single-attempt polling when
fetchKbStatus receives any non-2xx response. Add a useKbSync hook test covering
a 429 response and assert that the status endpoint is requested exactly once.

In `@vite.config.ts`:
- Around line 22-30: The closeBundle cleanup currently races with compression
and can delete WASM files before their .gz artifacts are written. Update the
stripUnusedOrtWasm cleanup flow to run as an explicit post-build step after
compression and final asset writes complete, then assert that assetsDir contains
no .wasm or .wasm.gz files.

---

Outside diff comments:
In `@src/components/features/ExecutionWorkspace.tsx`:
- Around line 386-433: Move the dynamic import of JSZip inside the existing try
block in the bundle-generation flow, before constructing the Zip instance.
Ensure failures from await import("jszip") are caught by the existing “ZIP
Generation failed” handler, while preserving the current archive creation and
download behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6acd2e1-7fba-4b7a-bee5-43f7c3ac65f3

📥 Commits

Reviewing files that changed from the base of the PR and between 64d2434 and 84228a2.

⛔ Files ignored due to path filters (3)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/assets/app.ico is excluded by !**/*.ico
  • public/assets/logo.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • package.json
  • server.ts
  • src/App.tsx
  • src/components/TitleBar.tsx
  • src/components/features/BatchComparisonChart.tsx
  • src/components/features/BatchProcessingPanel.test.tsx
  • src/components/features/BatchProcessingPanel.tsx
  • src/components/features/ExecutionWorkspace.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/IHVIntegrationPanel.test.tsx
  • src/components/features/IHVIntegrationPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/VramEstimateBanner.tsx
  • src/components/features/__tests__/testUtils.tsx
  • src/components/features/recipe-graph/StepInspector.tsx
  • src/components/features/recipe-graph/inspectors/ProviderInspector.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/lib/hooks/useKbSync.ts
  • vite.config.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
💤 Files with no reviewable changes (1)
  • src/components/features/BatchComparisonChart.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Greptile Review
  • GitHub Check: python-tests
  • GitHub Check: validate
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

src/**/*.{ts,tsx}: Match existing naming, file layout, and TypeScript patterns in src/.
Put shared recipe logic in src/lib/, especially pipelineValidation.ts, oliveRecipeBuilder.ts, and recipePipeline.ts.

src/**/*.{ts,tsx}: Keep validation logic in shared libraries rather than duplicating it in UI cell helpers or inspectors.
Split the InputEnvironmentPanel, IHVIntegrationPanel, and ExecutionWorkspace mega-panels into feature folders with colocated hooks and tests.
Keep server and UI AI provider catalogs synchronized, preferably through a shared provider ID list or synchronization test; register new providers in both catalogs.
Add test coverage for recipe-graph/, passCatalog, oliveRecipeHub, jobHistoryStore, and vramEstimate, and strengthen component tests for the large panels.

src/**/*.{ts,tsx}: All UI state mutations must go through commitUiStateUpdate in src/lib/pipelineValidation.ts so invariants are enforced; use usePipelineState() for state access and replaceState for recipe imports or preset loads.
Avoid export * barrel imports; import directly from the actual module file to preserve Vite tree-shaking and component-test isolation.

src/**/*.{ts,tsx}: In React/TypeScript source, avoid barrel imports; import from the specific module/file instead of re-export index files.
In React/TypeScript source, eliminate waterfalls in data loading and rendering flows.
In React/TypeScript source, defer non-critical third-party libraries instead of loading them eagerly.

Files:

  • src/App.tsx
  • src/components/TitleBar.tsx
  • src/components/features/ExecutionWorkspace.test.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/VramEstimateBanner.tsx
  • src/components/features/IHVIntegrationPanel.test.tsx
  • src/components/features/recipe-graph/inspectors/ProviderInspector.tsx
  • src/components/features/BatchProcessingPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/__tests__/testUtils.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/BatchProcessingPanel.tsx
  • src/components/features/recipe-graph/StepInspector.tsx
  • src/components/features/IHVIntegrationPanel.tsx
  • src/lib/hooks/useKbSync.ts
  • src/components/features/InputEnvironmentPanel.test.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx,js,jsx}: Place imports at the top of modules; use inline imports only for a documented circular dependency.
Run linting and ensure typecheck-related CI checks pass before submitting changes.
For UI or server changes, manually smoke-test development startup, recipe loading/building, validation banners, and live execution when execution behavior is touched.

Files:

  • src/App.tsx
  • src/components/TitleBar.tsx
  • src/components/features/ExecutionWorkspace.test.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/VramEstimateBanner.tsx
  • server.ts
  • src/components/features/IHVIntegrationPanel.test.tsx
  • src/components/features/recipe-graph/inspectors/ProviderInspector.tsx
  • src/components/features/BatchProcessingPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/__tests__/testUtils.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • vite.config.ts
  • src/components/features/BatchProcessingPanel.tsx
  • src/components/features/recipe-graph/StepInspector.tsx
  • src/components/features/IHVIntegrationPanel.tsx
  • src/lib/hooks/useKbSync.ts
  • src/components/features/InputEnvironmentPanel.test.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When working with React 19 or Vite 8 APIs, consult current Context7 documentation instead of assuming conventions from earlier major versions.

Files:

  • src/App.tsx
  • src/components/TitleBar.tsx
  • src/components/features/ExecutionWorkspace.test.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/VramEstimateBanner.tsx
  • server.ts
  • src/components/features/IHVIntegrationPanel.test.tsx
  • src/components/features/recipe-graph/inspectors/ProviderInspector.tsx
  • src/components/features/BatchProcessingPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/__tests__/testUtils.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • vite.config.ts
  • src/components/features/BatchProcessingPanel.tsx
  • src/components/features/recipe-graph/StepInspector.tsx
  • src/components/features/IHVIntegrationPanel.tsx
  • src/lib/hooks/useKbSync.ts
  • src/components/features/InputEnvironmentPanel.test.tsx
server.ts

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep Olive spawning, dependency installation, and PATH handling in server.ts.

server.ts: Default the Express API bind address to 127.0.0.1; do not expose it to a LAN or public internet without authentication and binding fixes.
Add global Express error middleware so unhandled errors are consistently handled without leaking stack traces.

Files:

  • server.ts
🧠 Learnings (1)
📚 Learning: 2026-08-04T12:36:02.655Z
Learnt from: tonythethompson
Repo: tonythethompson/Olive-Studio PR: 97
File: src/components/features/BatchProcessingPanel.tsx:0-0
Timestamp: 2026-08-04T12:36:02.655Z
Learning: When updating pipeline state through usePipelineState().setState in React components, do not wrap the update in another commitUiStateUpdate call. PipelineStore.setState already invokes commitUiStateUpdate(store.state, partial) to enforce UI state invariants; a second commit can duplicate the operation and merge against a stale component state snapshot.

Applied to files:

  • src/components/TitleBar.tsx
  • src/components/features/ExecutionWorkspace.test.tsx
  • src/components/features/VramEstimateBanner.tsx
  • src/components/features/IHVIntegrationPanel.test.tsx
  • src/components/features/recipe-graph/inspectors/ProviderInspector.tsx
  • src/components/features/BatchProcessingPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/__tests__/testUtils.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/BatchProcessingPanel.tsx
  • src/components/features/recipe-graph/StepInspector.tsx
  • src/components/features/IHVIntegrationPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
🔍 Remote MCP Context7, DeepWiki, GitHub Copilot

Relevant review context

  • The actual change is Olive-Studio PR #161, not mta1124-1629472/Babel-Player; DeepWiki could not index the referenced repositories, so no architectural context was available there.
  • App creates one QueryClient per app instance and wraps the dashboard in QueryClientProvider, so the new hardware and KB hooks have a shared cache in production.
  • useHardwareProbe uses one query key with a five-minute staleTime; forced probes update that same cache with setQueryData. TanStack Query documents these as the standard caching and cache-update mechanisms.
  • Both useHardwareProbe and the new KB status query omit retry; TanStack Query’s documented client default is three retries, whereas the previous manual fetch paths attempted once. The shared test utility explicitly disables retries only in tests.
  • VramEstimateBanner calls useHardwareProbe() unconditionally even when a probe prop is supplied. When the prop lacks RAM, this can trigger both the shared normal query and the explicit forced refresh; previously only the forced refresh occurred.
  • useKbSync now invalidates the KB-status query after successful sync, matching TanStack Query’s documented mutation/invalidation pattern. Existing tests cover freshness helpers but do not directly exercise the hook’s query/mutation behavior.
  • The Vite cleanup plugin uses closeBundle; Vite documents that this runs after output writing, but its closeBundle hooks are executed in parallel, so interaction with the compression plugin should be verified.
  • PR checks currently show CodeQL, CodeFactor, security, and Vercel passing; validation, Docker, Python tests, Olive availability, and Greptile review were still in progress when queried.
🔇 Additional comments (20)
src/App.tsx (1)

203-204: LGTM!

src/components/TitleBar.tsx (1)

5-8: LGTM!

Also applies to: 67-73

src/lib/hooks/useHardwareProbe.ts (1)

8-12: LGTM!

Also applies to: 26-32

src/components/features/__tests__/testUtils.tsx (1)

8-30: LGTM!

src/components/features/BatchProcessingPanel.tsx (1)

17-18: LGTM!

Also applies to: 667-694

src/components/features/BatchProcessingPanel.test.tsx (1)

2-4: LGTM!

src/components/features/IHVIntegrationPanel.tsx (1)

20-21: LGTM!

Also applies to: 85-138

src/components/features/IHVIntegrationPanel.test.tsx (1)

2-3: LGTM!

src/components/features/InputEnvironmentPanel.tsx (1)

2-2: LGTM!

Also applies to: 44-44, 122-122, 218-218, 1608-1611

src/components/features/InputEnvironmentPanel.test.tsx (1)

1-132: LGTM!

src/components/features/VramEstimateBanner.tsx (1)

4-5: LGTM!

src/components/features/recipe-graph/StepInspector.tsx (1)

3-3: LGTM!

Also applies to: 28-28

src/components/features/recipe-graph/inspectors/ProviderInspector.tsx (1)

1-4: LGTM!

Also applies to: 24-24

src/components/features/ExecutionWorkspace.test.tsx (1)

2-4: LGTM!

src/lib/hooks/useKbSync.ts (3)

1-2: LGTM!


26-37: LGTM!


139-169: LGTM!

vite.config.ts (1)

108-132: LGTM!

src/components/features/ExecutionWorkspace.tsx (1)

53-53: LGTM!

Also applies to: 351-351

package.json (1)

75-77: 🩺 Stability & Availability

Codex SDK boundary is intact.

Runtime access to @openai/codex-sdk is behind a lazy import, and the server startup path does not load it directly.

Comment thread server.ts
Comment thread server.ts Outdated
Comment thread src/components/features/InputEnvironmentPanel.tsx Outdated
Comment thread src/components/features/VramEstimateBanner.tsx Outdated
Comment thread src/lib/hooks/useHardwareProbe.ts Outdated
Comment thread src/lib/hooks/useKbSync.ts
Comment thread src/lib/hooks/useKbSync.ts
Comment thread vite.config.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Aug 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server.ts`:
- Around line 217-219: Update the cache-control selection around the filePath
check so the one-year immutable policy applies only to fingerprinted build
assets, not every non-index.html file. Keep index.html non-cached, and use a
revalidating cache policy for stable asset URLs such as /assets/logo.png.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 283a2b9c-5f9e-4d24-9a3e-c3521aef7c18

📥 Commits

Reviewing files that changed from the base of the PR and between 84228a2 and 9c2f7cb.

📒 Files selected for processing (1)
  • server.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⚠️ CI failures not shown inline (1)

GitHub Check: Greptile Review: Confidence 3/5 — below your required 4/5

Conclusion: failure

View job details

Greptile reviewed this pull request successfully — this check reflects your team's confidence threshold, not a review failure. The review scored 3/5, below the 4/5 this repository requires for the check to pass.
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx,js,jsx}: Place imports at the top of modules; use inline imports only for a documented circular dependency.
Run linting and ensure typecheck-related CI checks pass before submitting changes.
For UI or server changes, manually smoke-test development startup, recipe loading/building, validation banners, and live execution when execution behavior is touched.

Files:

  • server.ts
server.ts

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep Olive spawning, dependency installation, and PATH handling in server.ts.

server.ts: Default the Express API bind address to 127.0.0.1; do not expose it to a LAN or public internet without authentication and binding fixes.
Add global Express error middleware so unhandled errors are consistently handled without leaking stack traces.

Files:

  • server.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When working with React 19 or Vite 8 APIs, consult current Context7 documentation instead of assuming conventions from earlier major versions.

Files:

  • server.ts
🔍 Remote MCP Context7, DeepWiki, GitHub Copilot

Additional review context

  • Potential stale VRAM data: VramEstimateBanner always prefers forcedProbe over later hardwareProbeProp updates, so a forced result can remain authoritative after a parent rescan.
  • Cache policy scope: server.ts applies one-year immutable caching to every non-index.html file, including stable URLs such as /assets/logo.png; only hashed build assets are generally safe for immutable caching.
  • Optional Codex dependency: The PR makes @openai/codex-sdk optional, while repository code still dynamically imports it without handling module absence. This may expose a raw module-resolution failure when the optional package is unavailable.
  • React Query behavior: TanStack Query v5 defaults client queries to three retries and five-minute garbage collection. staleTime, setQueryData, invalidateQueries, and refetchInterval behave as used by the new hooks; invalidation refetches active queries by default.
  • Build cleanup ordering: Vite documents that closeBundle runs after output files are written, but its closeBundle hooks run in parallel. The cleanup plugin therefore shares a concurrent lifecycle with compression-related plugins.
  • Validation status: CodeQL, validation, Docker, Python tests, security, CodeFactor, Olive availability, and Vercel checks passed. Greptile failed, and an automated review reported the stale-probe, cache-policy, and optional-dependency concerns above.
  • Repository search: useKbSync() has one production consumer, KbSyncIndicator.tsx.
  • DeepWiki was attempted for repository architecture but was rate-limited; no additional architectural facts were obtained.
🔇 Additional comments (2)
server.ts (2)

2-2: LGTM!


224-229: LGTM!

Comment thread server.ts Outdated
… noise, race, error UX)

- VramEstimateBanner: a forced RAM-refresh probe permanently shadowed
  later hardwareProbeProp updates since it was never cleared. Now only
  overrides the prop while the prop itself is still missing RAM info.
- server.ts: only mark Vite's Rollup-emitted, content-hashed JS/CSS as
  immutable-cached. Public assets copied verbatim (logo.png, fonts,
  favicon) keep a stable URL, so a content change without a filename
  change could otherwise be pinned client-side for a year.
- useHardwareProbe/useKbSync status query: disable React Query's
  default 3x retry. fetchHardwareProbe already retries once internally
  (shells out to GPU/TensorRT detection — expensive to triple); the KB
  status endpoint is rate-limited, so retrying failures adds noise for
  no benefit.
- useKbSync's postKbSync now rejects on a 2xx response with
  `{ ok: false }`, a missing `ok` field, or malformed JSON instead of
  resolving as if the sync succeeded.
- InputEnvironmentPanel's hf-token-status query now checks res.ok
  before parsing — fetch() doesn't reject on 4xx/5xx.
- ExecutionWorkspace: the jszip dynamic import ran before the
  try/catch that was supposed to guard "ZIP Generation failed" —
  a failed chunk load bypassed it as an unhandled rejection.
- codexAgent: a missing @openai/codex-sdk (now optionalDependencies)
  surfaced as a raw module-resolution error, and the failed import
  promise was cached forever, requiring a server restart even after
  installing the package. Now throws a clear message and resets the
  cached promise so a later install works without a restart.
- vite.config.ts: moved the ORT wasm strip from closeBundle (a
  parallel hook — races with vite-plugin-compression's own closeBundle,
  could delete files mid-write or miss them) to generateBundle, which
  runs before anything is written to disk. Compression's hook now
  never sees the wasm assets to begin with.

Not changed: server.ts's 0.0.0.0 bind. A bot flagged this as a
security issue, but binding to 127.0.0.1 by default would break
legitimate LAN/WSL2-forwarding access patterns for a tool that's
explicitly meant to run locally. No in-repo policy backs the claimed
"coding guideline" this cited. Left as-is pending an explicit call
from the repo owner.
greptile-apps[bot]
greptile-apps Bot previously approved these changes Aug 7, 2026
…e, tests)

Follow-up on the earlier hf-token fix — coderabbit correctly flagged
it as partial:

- clearTokenMutation now checks r.ok before treating a DELETE as
  success; it previously wrote "none" to the cache on any response,
  including a failed delete.
- handleClearToken wraps the mutation call in try/catch, matching
  handleSubmitToken's existing pattern — clearTokenMutation.mutateAsync
  can now reject and an onClick handler can't let that escape as an
  unhandled rejection.
- hfTokenStatus gets a distinct "error" state instead of collapsing a
  failed status query into "none" (which read as "no token configured"
  rather than "couldn't check").
- Added test coverage for both failure paths.
@greptile-apps
greptile-apps Bot dismissed their stale review August 7, 2026 13:52

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

Comment thread server.ts Outdated
… drives it

- server.ts: the immutable-cache check matched any .js/.css by
  extension alone. A future stable-URL script copied verbatim from
  public/ would have been misclassified as content-hashed. Now
  requires the actual Vite hash suffix (name-<8-char-hash>.ext).
- useHardwareProbe gains an `enabled` option. VramEstimateBanner now
  disables the shared query when a hardwareProbe prop is supplied —
  previously it fired an unconditional fetchHardwareProbe(false)
  query in parallel with the forced-refresh effect's
  fetchHardwareProbe(true) whenever the prop was missing RAM info,
  doubling probe invocations.
@codefactor-io

codefactor-io Bot commented Aug 7, 2026

Copy link
Copy Markdown

CodeFactor found an issue: Very Complex Method

It's currently on:
src\components\features\VramEstimateBanner.tsx:29-355
Commit 13ddfd7

@tonythethompson

Copy link
Copy Markdown
Owner Author

Pre-existing — VramEstimateBanner.tsx was already ~348 lines before this PR touched it; this PR's changes add ~10 net lines (the forced-probe staleness fix and the enabled-query fix). A full complexity refactor of the component is real work unrelated to this PR's scope (bundle size + query dedup), and would create merge friction with #162, which already relocated this file. Leaving it out of scope here — happy to take it on as its own PR if wanted.

🤖 Addressed by Claude Code

@tonythethompson
tonythethompson merged commit 5799b41 into main Aug 7, 2026
13 of 14 checks passed
@tonythethompson
tonythethompson deleted the perf/vite-bundle-size branch August 7, 2026 14:40
@linear-code

linear-code Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

OLI-66

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server.ts`:
- Around line 215-226: Update the cache-control check in the static asset header
logic to treat both index.html and its compressed index.html.gz path as
non-cacheable, covering express-static-gzip’s rewritten request path while
preserving the existing hashed JS/CSS caching behavior.

In `@src/components/features/ExecutionWorkspace.tsx`:
- Around line 386-393: Update the ZIP-loading catch in the export flow around
the jszip import and the OwrExportOverlay state to store a downloadable error
for OwrExportOverlayProps instead of returning silently. Render that error with
role="alert" in OwrExportOverlay, and clear the error state before each new
download attempt.

In `@src/components/features/InputEnvironmentPanel.tsx`:
- Around line 187-192: Update handleClearToken and the Clear control in
InputEnvironmentPanel so failed clearTokenMutation requests produce a
clear-specific user-visible error instead of being silently ignored, using
clearTokenMutation.error. Disable the Clear action while
clearTokenMutation.isPending, while preserving the existing successful-clear
behavior.

In `@src/lib/codex/codexAgent.ts`:
- Around line 60-62: Update the error message in the Codex provider loading path
to recommend `pnpm add --save-optional `@openai/codex-sdk`` instead of `pnpm add
`@openai/codex-sdk``, preserving the optional dependency declaration.
- Around line 56-63: Update the catch around the Codex module initialization and
codexModulePromise so only an explicit missing-@openai/codex-sdk resolution
failure produces the optional-dependency installation message. For module
evaluation, export, or other load failures, throw a generic Codex provider load
error while preserving the original error as its cause; retain resetting
codexModulePromise for both paths.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a5cd736b-c0f9-4b67-84f4-632da734fd4a

📥 Commits

Reviewing files that changed from the base of the PR and between 9c2f7cb and ccf91c4.

📒 Files selected for processing (9)
  • server.ts
  • src/components/features/ExecutionWorkspace.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/VramEstimateBanner.tsx
  • src/lib/codex/codexAgent.ts
  • src/lib/hooks/useHardwareProbe.ts
  • src/lib/hooks/useKbSync.ts
  • vite.config.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Greptile Review
  • GitHub Check: python-tests
  • GitHub Check: olive-pass-availability
  • GitHub Check: validate
  • GitHub Check: docker-build
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

src/**/*.{ts,tsx}: Match existing naming, file layout, and TypeScript patterns in src/.
Put shared recipe logic in src/lib/, especially pipelineValidation.ts, oliveRecipeBuilder.ts, and recipePipeline.ts.

src/**/*.{ts,tsx}: Keep validation logic in shared libraries rather than duplicating it in UI cell helpers or inspectors.
Split the InputEnvironmentPanel, IHVIntegrationPanel, and ExecutionWorkspace mega-panels into feature folders with colocated hooks and tests.
Keep server and UI AI provider catalogs synchronized, preferably through a shared provider ID list or synchronization test; register new providers in both catalogs.
Add test coverage for recipe-graph/, passCatalog, oliveRecipeHub, jobHistoryStore, and vramEstimate, and strengthen component tests for the large panels.

src/**/*.{ts,tsx}: All UI state mutations must go through commitUiStateUpdate in src/lib/pipelineValidation.ts so invariants are enforced; use usePipelineState() for state access and replaceState for recipe imports or preset loads.
Avoid export * barrel imports; import directly from the actual module file to preserve Vite tree-shaking and component-test isolation.

src/**/*.{ts,tsx}: In React/TypeScript source, avoid barrel imports; import from the specific module/file instead of re-export index files.
In React/TypeScript source, eliminate waterfalls in data loading and rendering flows.
In React/TypeScript source, defer non-critical third-party libraries instead of loading them eagerly.

Files:

  • src/lib/codex/codexAgent.ts
  • src/components/features/VramEstimateBanner.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/lib/hooks/useKbSync.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx,js,jsx}: Place imports at the top of modules; use inline imports only for a documented circular dependency.
Run linting and ensure typecheck-related CI checks pass before submitting changes.
For UI or server changes, manually smoke-test development startup, recipe loading/building, validation banners, and live execution when execution behavior is touched.

Files:

  • src/lib/codex/codexAgent.ts
  • vite.config.ts
  • src/components/features/VramEstimateBanner.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/lib/hooks/useKbSync.ts
  • server.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When working with React 19 or Vite 8 APIs, consult current Context7 documentation instead of assuming conventions from earlier major versions.

Files:

  • src/lib/codex/codexAgent.ts
  • vite.config.ts
  • src/components/features/VramEstimateBanner.tsx
  • src/lib/hooks/useHardwareProbe.ts
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
  • src/lib/hooks/useKbSync.ts
  • server.ts
server.ts

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep Olive spawning, dependency installation, and PATH handling in server.ts.

server.ts: Default the Express API bind address to 127.0.0.1; do not expose it to a LAN or public internet without authentication and binding fixes.
Add global Express error middleware so unhandled errors are consistently handled without leaking stack traces.

Files:

  • server.ts
🧠 Learnings (1)
📚 Learning: 2026-08-04T12:36:02.655Z
Learnt from: tonythethompson
Repo: tonythethompson/Olive-Studio PR: 97
File: src/components/features/BatchProcessingPanel.tsx:0-0
Timestamp: 2026-08-04T12:36:02.655Z
Learning: When updating pipeline state through usePipelineState().setState in React components, do not wrap the update in another commitUiStateUpdate call. PipelineStore.setState already invokes commitUiStateUpdate(store.state, partial) to enforce UI state invariants; a second commit can duplicate the operation and merge against a stale component state snapshot.

Applied to files:

  • src/components/features/VramEstimateBanner.tsx
  • src/components/features/InputEnvironmentPanel.tsx
  • src/components/features/InputEnvironmentPanel.test.tsx
  • src/components/features/ExecutionWorkspace.tsx
🪛 React Doctor (0.9.3)
src/components/features/InputEnvironmentPanel.tsx

[warning] 1601-1601: ' in JSX text can read as markup & confuse readers.

Replace bare ' / " / > / } characters with HTML entities so literal UI text is encoded consistently.

(no-unescaped-entities)

🔍 Remote MCP Context7, DeepWiki, GitHub Copilot

Additional review context

  • DeepWiki: Repository lookup failed (tonythethompson/Olive-Studio was not indexed), so no architectural context was obtained.
  • KB sync: useKbSync() is consumed by KbSyncIndicator.tsx; the current implementation performs status fetching, a five-minute interval refresh, visibility refreshes, and one stale-status auto-sync. A React Query migration should preserve these behaviors and avoid duplicate syncs.
  • Codex callers: Codex functionality is reached through both codexRoutes.ts and providerRoutes.ts, while the SDK loader is implemented in src/lib/codex/codexAgent.ts. Missing optional-dependency errors therefore affect multiple API paths.
  • Current static serving: The baseline server uses express.static(distPath) followed by an uncached SPA fallback via res.sendFile(indexHtml). Cache-header changes should retain the distinction between actual assets and fallback HTML.
  • Context7: No dedicated express-static-gzip documentation library was found; only Express documentation matches were returned, so no additional package-specific validation was available.
🔇 Additional comments (6)
src/lib/hooks/useHardwareProbe.ts (1)

14-23: LGTM!

src/components/features/InputEnvironmentPanel.tsx (2)

122-122: LGTM!

Also applies to: 136-176, 229-229, 1624-1627


1600-1604: 📐 Maintainability & Code Quality

Verify the JSX entity lint result.

React Doctor reports no-unescaped-entities for Couldn't. If the configured lint rule is enforced, replace it with Couldn&apos;t and confirm the lint check passes.

Sources: Coding guidelines, Linters/SAST tools

src/components/features/InputEnvironmentPanel.test.tsx (1)

1-1: LGTM!

Also applies to: 133-192

src/components/features/VramEstimateBanner.tsx (1)

51-58: 🎯 Functional Correctness

No stale forced-probe response to address.

The component always resolves to the most recent incomplete hardwareProbeProp before applying the pending async result, so a newer prop cannot be shadowed by an older refresh response.

			> Likely an incorrect or invalid review comment.
src/lib/hooks/useKbSync.ts (1)

68-70: LGTM!

Also applies to: 132-132

Comment thread server.ts
Comment on lines +215 to +226
if (filePath.endsWith("index.html")) {
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
return;
}
// Only Vite's Rollup-emitted JS/CSS carry a content hash in the
// filename (name-<8-char-hash>.js) — safe to cache forever.
// Matching on extension alone would also catch any stable-URL
// .js/.css copied verbatim from public/, so require the actual
// hash suffix. Everything else under dist/ (logo.png, fonts,
// favicon) has a stable URL and must be revalidated, not served
// from a 1-year cache untouched.
const isHashedBuildOutput = /-[\w-]{8}\.(js|css)(\.gz)?$/.test(filePath);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact declared middleware version for its compressed-request rewrite.
tarball="$(
  curl -fsSL https://registry.npmjs.org/express-static-gzip/3.0.1 |
    python3 -c 'import json, sys; print(json.load(sys.stdin)["dist"]["tarball"])'
)"
curl -fsSL "$tarball" |
  tar -xzO package/index.js |
  grep -nE 'convertToCompressedRequest|req\.url|serveStatic'

# Confirm that server.ts classifies both index.html and index.html.gz as non-cacheable.
rg -n -C 4 'endsWith\("index\.html"|Cache-Control' server.ts

Repository: tonythethompson/Olive-Studio

Length of output: 2273


Keep compressed index.html non-cacheable.

express-static-gzip rewrites /index.html requests to /index.html.gz, but setHeaders only checks index.html. Compressed SPA entry documents can then get public, max-age=3600, so stale documents may still reference removed hashed assets.

Proposed fix
-            if (filePath.endsWith("index.html")) {
+            if (filePath.endsWith("index.html") || filePath.endsWith("index.html.gz")) {
               res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
               return;
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (filePath.endsWith("index.html")) {
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
return;
}
// Only Vite's Rollup-emitted JS/CSS carry a content hash in the
// filename (name-<8-char-hash>.js) — safe to cache forever.
// Matching on extension alone would also catch any stable-URL
// .js/.css copied verbatim from public/, so require the actual
// hash suffix. Everything else under dist/ (logo.png, fonts,
// favicon) has a stable URL and must be revalidated, not served
// from a 1-year cache untouched.
const isHashedBuildOutput = /-[\w-]{8}\.(js|css)(\.gz)?$/.test(filePath);
if (filePath.endsWith("index.html") || filePath.endsWith("index.html.gz")) {
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
return;
}
// Only Vite's Rollup-emitted JS/CSS carry a content hash in the
// filename (name-<8-char-hash>.js) — safe to cache forever.
// Matching on extension alone would also catch any stable-URL
// .js/.css copied verbatim from public/, so require the actual
// hash suffix. Everything else under dist/ (logo.png, fonts,
// favicon) has a stable URL and must be revalidated, not served
// from a 1-year cache untouched.
const isHashedBuildOutput = /-[\w-]{8}\.(js|css)(\.gz)?$/.test(filePath);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server.ts` around lines 215 - 226, Update the cache-control check in the
static asset header logic to treat both index.html and its compressed
index.html.gz path as non-cacheable, covering express-static-gzip’s rewritten
request path while preserving the existing hashed JS/CSS caching behavior.

Comment on lines +386 to +393
let zip: InstanceType<typeof import("jszip")>;
try {
const { default: JSZip } = await import("jszip");
zip = new JSZip();
} catch (e) {
console.error("Failed to load ZIP module", e);
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the existing OWR overlay error and alert conventions before adding the error prop.
rg -n -C 3 'OwrExportOverlay|role="alert"|set.*Error|Failed to load ZIP module' \
  src/components/features/ExecutionWorkspace.tsx src/components/features

# Locate adjacent component tests for the export flow.
rg -n -C 3 'OWR|DownloadOwr|Export for OWR|jszip' \
  src/components/features/ExecutionWorkspace.test.tsx src/components/features

Repository: tonythethompson/Olive-Studio

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ExecutionWorkspace OWR-related state and overlay call =="
sed -n '200,260p' src/components/features/ExecutionWorkspace.tsx
sed -n '350,410p' src/components/features/ExecutionWorkspace.tsx
sed -n '700,730p' src/components/features/ExecutionWorkspace.tsx

echo "== OwrExportOverlayProps and render =="
sed -n '1,180p' src/components/features/OwrExportOverlay.tsx

echo "== package/jszip dependency =="
rg -n '"jszip"|exportOwrBundle|handleOwrDownload|ondownloadbundle|download' package.json public src/components/features/ExecutionWorkspace.tsx src/components/features/OwrExportOverlay.tsx

Repository: tonythethompson/Olive-Studio

Length of output: 12183


Show the ZIP load failure in the OWR export UI.

The jszip import constructor catch returns silently, leaving OwrExportOverlay open with no download. Pass a downloadable error to OwrExportOverlayProps, render it with role="alert", and clear it before the next download attempt.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/features/ExecutionWorkspace.tsx` around lines 386 - 393,
Update the ZIP-loading catch in the export flow around the jszip import and the
OwrExportOverlay state to store a downloadable error for OwrExportOverlayProps
instead of returning silently. Render that error with role="alert" in
OwrExportOverlay, and clear the error state before each new download attempt.

Comment on lines 187 to +192
const handleClearToken = async () => {
await fetch("/api/env/hf-token", { method: "DELETE" });
setHfTokenStatus("none");
try {
await clearTokenMutation.mutateAsync();
} catch {
/* ignore — clearTokenMutation.error surfaces via mutation state */
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Expose failed token deletion.

Line 190 suppresses the rejected mutation. The component does not render clearTokenMutation.error, so a failed Clear action has no user-visible result. Render a clear-specific error and disable Clear while clearTokenMutation.isPending.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/features/InputEnvironmentPanel.tsx` around lines 187 - 192,
Update handleClearToken and the Clear control in InputEnvironmentPanel so failed
clearTokenMutation requests produce a clear-specific user-visible error instead
of being silently ignored, using clearTokenMutation.error. Disable the Clear
action while clearTokenMutation.isPending, while preserving the existing
successful-clear behavior.

Comment on lines +56 to +63
({ Codex } = await codexModulePromise);
} catch (err) {
// Reset so a later install doesn't require a server restart.
codexModulePromise = null;
throw new Error(
"Codex provider unavailable: @openai/codex-sdk (optionalDependencies) is not installed. Run `pnpm add @openai/codex-sdk` to enable it.",
{ cause: err },
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)codexAgent\.ts$|(^|/)codexRoutes\.ts$|package\.json$' || true

echo "== codexAgent.ts outline =="
ast-grep outline src/lib/codex/codexAgent.ts | head -120 || true

echo "== codexAgent relevant content =="
cat -n src/lib/codex/codexAgent.ts | sed -n '1,130p'

echo "== codexRoutes relevant content =="
cat -n src/server/routes/ai/codexRoutes.ts | sed -n '1,220p'

echo "== package deps "
python3 - <<'PY'
import json
from pathlib import Path
p=Path('package.json')
if p.exists():
    data=json.loads(p.read_text())
    for sec in ('dependencies','devDependencies','optionalDependencies','peerDependencies'):
        deps=data.get(sec,{})
        if '`@openai/codex-sdk`' in deps:
            print(sec, '`@openai/codex-sdk`', deps['`@openai/codex-sdk`'])
PY
rg -n '`@openai/codex-sdk`|codexModulePromise|Codex provider unavailable|codexRoutes' src package.json pnpm-lock.yaml 2>/dev/null || true

Repository: tonythethompson/Olive-Studio

Length of output: 10944


🏁 Script executed (no clone):

#!/bin/bash
set -euo pipefail

echo "== Node error shape probe =="
node - <<'JS'
function makeFetchError() {
  try {
    fetch('doesnt_exist.mjs').catch(() => {});
  } catch (err) {
    return err;
  }
}
const error = makeFetchError();
console.log(JSON.stringify({
  constructor: error?.constructor && error.constructor.name,
  message: error?.message,
  code: error?.code,
  cause: Boolean(error?.cause),
  causeMessage: error?.cause?.message ?? null,
  causeCode: error?.cause?.code ?? null,
  causeConstructor: error?.cause?.constructor && error.cause.constructor.name ?? null,
}, null, 2));
JS

echo "== package availability probe =="
node - <<'JS'
(async () => {
  const pkg = '`@openai/codex-sdk`';
  try {
    const module = await import(pkg);
    console.log(JSON.stringify({
      pkg,
      available: true,
      constructor: module?.Codex?.constructor && module.Codex.constructor.name,
      hasImport: Boolean(module && module.Codex),
    }, null, 2));
  } catch (cause) {
    const err = new Error(`${pkg} unavailable`);
    err.cause = cause;
    console.log(JSON.stringify({
      pkg,
      available: false,
      message: err.message,
      code: err.code ?? null,
      cause: Boolean(err.cause),
      causeMessage: err.cause?.message ?? null,
      causeCode: err.cause?.code ?? null,
      causeConstructor: err.cause?.constructor && err.cause.constructor.name ?? null,
    }, null, 2));
  }
})();
JS

Length of output: 942


🏁 Script executed (no clone):

#!/bin/bash
set -euo pipefail

echo "== Node version =="
node --version

echo "== Node error shape probe =="
node - <<'JS'
function makeFetchError() {
  try {
    fetch('file://' + process.cwd() + '/doesnt_exist_invalid_module.mjs').catch(() => {});
  } catch (err) {
    return err;
  }
}
const error = makeFetchError();
console.log(JSON.stringify({
  message: (error && error.message) || null,
  code: (error && error.code) || null,
  hasCause: Boolean(error && error.cause),
  causeMessage: (error && error.cause && error.cause.message) || null,
  causeCode: (error && error.cause && error.cause.code) || null,
}, null, 2));
JS

echo "== Package availability probe =="
node - <<'JS'
(async () => {
  const pkg = '`@openai/codex-sdk`';
  try {
    const module = await import(pkg);
    console.log(JSON.stringify({
      pkg,
      available: true,
      constructorName: module && module.Codex && module.Codex.name ? module.Codex.name : null,
      hasImport: Boolean(module && module.Codex),
    }, null, 2));
  } catch (cause) {
    const err = new Error(pkg + ' unavailable');
    err.cause = cause;
    console.log(JSON.stringify({
      pkg,
      available: false,
      errorHasCode: Boolean(err.code),
      errorHasCause: Boolean(err.cause),
      causeMessage: (cause && cause.message) || null,
      causeCode: (cause && cause.code) || null,
    }, null, 2));
  }
})();
JS

Length of output: 710


🌐 Web query:

Node.js import ERR_MODULE_NOT_FOUND message package installed module evaluates syntax error error shape

💡 Result:

The ERR_MODULE_NOT_FOUND error is specific to Node.js ECMAScript Modules (ESM) [1]. It occurs when the ESM resolver fails to locate a requested module, package, or file [2][3]. Unlike the traditional CommonJS MODULE_NOT_FOUND error, ESM resolution follows strict rules, and the error shape typically includes the code ERR_MODULE_NOT_FOUND and an indication of the import context [4][1][3]. Common causes and solutions include: 1. Missing File Extensions: In ESM, you must explicitly include file extensions (e.g.,.js) in import specifiers [1][5]. Unlike CommonJS, Node.js will not automatically append.js or resolve directory indexes (like index.js) [1][5][6]. - Incorrect: import { func } from './utils'; - Correct: import { func } from './utils.js'; 2. Directory Import Restrictions: ESM does not support importing directories to resolve an index file by default [1][3]. You must specify the full path to the file [1][5]. 3. Misconfigured package.json Exports: If you are importing from a package, the package's exports field in its package.json may explicitly restrict access to the file you are trying to reach, leading to this error even if the file exists [1][2]. 4. Global vs. Local Installation: If you installed a package globally (npm install -g) but are trying to import it in a project, Node.js will not find it [7]. Node.js looks for packages in the node_modules folder relative to the current project [7][8]. Ensure the dependency is listed in your package.json and installed locally [8]. 5. TypeScript/Path Aliases: If using TypeScript, path aliases defined in tsconfig.json (e.g., @app/utils) are not natively resolved by Node.js at runtime [9]. Node.js only sees the compiled JavaScript and does not understand these custom mappings unless you use a loader or build step that resolves them [9]. 6. Module System Mismatch: If your project is configured with "type": "module" in package.json, Node.js expects ESM imports [6]. If you try to import a package that is CommonJS-only or incorrectly set up, it may fail to resolve properly [6][9]. To debug, verify that the path/specifier exists exactly as written, check that all local dependencies are installed in the project's node_modules, and ensure you are providing full file extensions for all relative imports [1][2][8].

Citations:


🌐 Web query:

Node v24 import error cause ERR_MODULE NOT FOUND syntax error caught ErrorModuleFail cause

💡 Result:

In Node.js v24, the ERR_MODULE_NOT_FOUND error is the standard error code produced by the ES Module (ESM) loader when it fails to resolve a module specifier [1][2][3]. Unlike the classic CommonJS MODULE_NOT_FOUND error, which is often more permissive, ERR_MODULE_NOT_FOUND indicates that the ESM resolver could not locate the exact file requested [1][2]. Common causes for this error include: 1. Missing File Extensions: Unlike CommonJS, ESM requires explicit file extensions in import specifiers (e.g., import './utils.js' instead of import './utils') [1][4][3]. 2. No Directory Index Lookup: ESM does not automatically resolve directory imports to an index file (e.g., importing a folder will not automatically find index.js) [1][3]. 3. TypeScript Integration Issues: When using tools like ts-node or tsx with TypeScript, the error often arises because the code is attempting to import a.ts file as if it were a.js file, or because path aliases defined in tsconfig.json are not being resolved at runtime [5][6][4]. 4. Case Sensitivity: Import paths are case-sensitive on Linux-based filesystems [4][3]. An import path that works in a local development environment (e.g., macOS or Windows) may fail in production (e.g., Linux) if the case does not exactly match the file on disk [4][3]. 5. Misconfigured Exports: If the module exists but is not properly exposed via the exports field in package.json, Node.js may report it as not found, though this is sometimes accompanied by specific ERR_PACKAGE_PATH_NOT_EXPORTED errors [1][3]. Regarding your specific mention of "syntax error caught ErrorModuleFail," this is likely a misinterpretation of stack trace output or an error handling pattern where a surrounding try/catch block intercepts the module resolution failure [7]. When you wrap an ESM import in a try/catch block, you can inspect the err.code property to confirm it is ERR_MODULE_NOT_FOUND [7]. If you are encountering this in a TypeScript context, verify that your loader (e.g., tsx or ts-node) is correctly configured to handle ESM and that you are using the correct module resolution settings in your tsconfig.json [5][6][7].

Citations:


🌐 Web query:

GitHub Node.js lib internal errors ESM load ERR_MODULE_NOT_FOUND ModuleNotFoundError

💡 Result:

The ERR_MODULE_NOT_FOUND error in Node.js ESM (ECMAScript Modules) projects typically occurs because Node.js strictly adheres to the ESM specification, which differs from the legacy CommonJS resolution behavior [1][2][3]. Unlike CommonJS, Node.js ESM does not guess file extensions or automatically resolve directory index files [1][3][4]. Common causes and solutions include: 1. Missing File Extensions: ESM requires explicit file extensions in relative import specifiers [5][3]. For example, importing './utils' will fail; you must use './utils.js' [1][2][3]. This requirement applies even in TypeScript projects, where you must use the extension of the compiled JavaScript file (e.g.,.js) rather than the source file (e.g.,.ts) [2][3]. 2. Directory Import Limitations: ESM does not automatically resolve index files in directories [1][3]. If you have a file at './components/index.js', you must explicitly import it as './components/index.js' rather than just './components' [1][3]. 3. TypeScript Configuration: If you use TypeScript, ensure your tsconfig.json is configured to produce compliant ESM output [2][6]. Using "moduleResolution": "bundler" allows extensionless imports in TypeScript but will cause runtime ERR_MODULE_NOT_FOUND errors in Node.js [6]. Changing this to "node" or "nodenext" enforces stricter resolution that aligns with Node.js requirements [6][4]. 4. Missing Exports Map: If you are encountering this error when importing a dependency that is not properly configured, it may be because that package lacks an "exports" map in its package.json [7][8]. When a package does not define an "exports" map, Node.js may fail to resolve subpath imports (like 'pkg/subpath') if they lack the required file extension [7][8]. To troubleshoot, always verify the exact file path and ensure it exists on the filesystem with the correct case, as Node.js resolution is case-sensitive on most filesystems [1][2]. If you are using a monorepo or build tool, ensure that the build process has correctly outputted the files to the expected destination before execution [9][2].

Citations:


Preserve missing-SDK errors from generic module loads.

This catch treats failed resolution, module evaluation, and export failures the same as a missing optional dependency. src/server/routes/ai/codexRoutes.ts returns only err.message, so API callers can get the false “is not installed” diagnosis when @openai/codex-sdk is present but broken. Detect the missing package explicitly, and use a generic load error for other failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/codex/codexAgent.ts` around lines 56 - 63, Update the catch around
the Codex module initialization and codexModulePromise so only an explicit
missing-@openai/codex-sdk resolution failure produces the optional-dependency
installation message. For module evaluation, export, or other load failures,
throw a generic Codex provider load error while preserving the original error as
its cause; retain resetting codexModulePromise for both paths.

Source: MCP tools

Comment on lines +60 to +62
throw new Error(
"Codex provider unavailable: @openai/codex-sdk (optionalDependencies) is not installed. Run `pnpm add @openai/codex-sdk` to enable it.",
{ cause: err },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Package manager files:\n'
git ls-files | grep -E '(^|/)(package.json|pnpm-lock.yaml|package-lock.json|yarn.lock)$' || true

printf '\npackage.json optionalDependencies and dependencies mentioning codex:\n'
python3 - <<'PY'
import json
from pathlib import Path
p=Path('package.json')
if p.exists():
    data=json.loads(p.read_text())
    for key in ['dependencies','optionalDependencies','devDependencies','peerDependencies']:
        deps=data.get(key,{})
        if '`@openai/codex-sdk`' in deps:
            print(key, deps['`@openai/codex-sdk`'])
PY

printf '\ncodexAgent relevant lines:\n'
fd -a 'codexAgent.ts' . | sed 's#^\./##' | while read -r f; do
  echo "--- $f"
  nl -ba "$f" | sed -n '1,120p'
done

printf '\nSearch for add --save-optional or optionalDependencies install guidance:\n'
rg -n "save-optional|--save-optional|optionalDependencies|`@openai/codex-sdk`" .

Repository: tonythethompson/Olive-Studio

Length of output: 437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'codexAgent.ts relevant lines:\n'
if [ -f src/lib/codex/codexAgent.ts ]; then
  awk 'NR<=120 {printf "%6d\t%s\n", NR, $0}' src/lib/codex/codexAgent.ts
fi

printf '\nSearch for pnpm add guidance:\n'
rg -n "pnpm add|--save-optional|save-optional|optionalDependencies|`@openai/codex-sdk`" .

printf '\nRepository pnpm script snippets:\n'
python3 - <<'PY'
import json
from pathlib import Path
data=json.loads(Path('package.json').read_text())
for name, value in (data.get('scripts') or {}).items():
    if 'pnpm' in str(value):
        print(f"{name}: {value}")
PY

printf '\nCheck local pnpm save-optional behavior from docs/config without installing:\n'
# pnpm 8+ already has --save-optional; output command is only valid if pnpm is installed and supports it.
if command -v pnpm >/dev/null 2>&1; then
  pnpm add --help 2>/dev/null | rg -n "save-optional|--save-optional|optionalDependencies|option" | head -20 || true
else
  echo "pnpm is not installed in the sandbox"
fi

Repository: tonythethompson/Olive-Studio

Length of output: 10736


Keep pnpm optional dependency installs optional.

pnpm add @openai/codex-sdk installs a regular dependency, while `@openai/codex-sdk` is declared under `optionalDependencies`. Use `pnpm add --save-optional `@openai/codex-sdk in this message to preserve the optional-provider boundary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/codex/codexAgent.ts` around lines 60 - 62, Update the error message
in the Codex provider loading path to recommend `pnpm add --save-optional
`@openai/codex-sdk`` instead of `pnpm add `@openai/codex-sdk``, preserving the
optional dependency declaration.

This branch was successfully deployed

1 active deployment
Preview — ccf91c43 Deployed Aug 7, 2026 by vercel[bot]
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.

1 participant