refactor(crosscode): modularize CLI architecture and fix critical runtime bugs - #103
Merged
Merged
Conversation
The data listener was never removed on cleanup, leaking handlers if onKeypress was called more than once. Now stores the reference and removes it in cleanupKeypress. Also adds 'h' key support and drops the unused emitKeypressEvents import.
If opencode crashed before a port was detected, the promise would sit for 15s then silently resolve with a dead port. Now listens for the exit event, clears the polling interval, and rejects immediately so the caller can surface the error.
Both close and error events would fire on failure, scheduling two reconnect timers and opening duplicate connections. Added a guard so only close triggers reconnect. Also adds a 60s heartbeat timeout to detect silently-dead connections, and moves backoff reset to after auth.ok so a rejected handshake doesn't hammer the server at 1s intervals.
…h regex The username was compared with === while the password used timingSafeEqual, leaking timing info. Now both use timingSafeEqual. Also removed the encodeURI re-encoding on the pathname and made the commit hash regex accept uppercase hex digits.
@crosscode/shared is imported at runtime in cli.ts, so it belongs in dependencies not devDependencies. Removed the main field pointing to dist/index.js which was never generated.
The 1120-line cli.ts was doing everything: logging, config, auth, proxying, and three tunnel providers with near-identical spawn logic copy-pasted across each. Now split into: util.ts - debug, censor, dep check, port alloc log.ts - rotation, streams, async flush on shutdown config.ts - types, read/save, project/session identity auth.ts - login flow, API key validation, prompt input proxy.ts - HTTP proxy server, URL sanitization opencode.ts - shared opencode spawn+port-detect helper providers/ - one file per tunnel backend cli.ts is now ~270 lines of arg parsing and orchestration. This also fixes the tunnel-to-cloudflared fallback that never actually started cloudflared (the flag was set async after the if/else chain had already evaluated), and makes shutdown await log buffer flush before calling process.exit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR modularizes the
crosscodeCLI package, breaking down the monolithic 1,120-linecli.tsinto isolated, single-responsibility modules undersrc/. It also improves process lifecycle management, tunnel fallback reliability, connection stability, and timing safety across the package.🛠️ Key Changes & Fixes
1. Architecture & Modularization
Reduced
cli.tsfrom 1,120 lines to ~270 lines by extracting concerns into dedicated modules:src/util.ts— Common helpers for debug logging, credential censoring, dependency checking, and free port allocation.src/log.ts— Log streams, rotation, and async buffer flushing on exit.src/config.ts— Persistent configuration, project identity, and session tokens.src/auth.ts— API key validation, login flows, and interactive user prompts.src/proxy.ts— HTTP proxy server, SSE upstream bridging, and URL sanitization.src/opencode.ts— Unifiedopencode serveprocess spawning and port detection, removing ~300 lines of duplicated code.src/providers/— Dedicated adapters fortunnel.ts,cloudflared.ts, andngrok.ts.2. Runtime & Stability Improvements
waitForOpencodePortnow listens for processexitevents and immediately rejects ifopencodecrashes during startup instead of hanging for 15 seconds.datalistener reference socleanupKeypress()can reliably detach event listeners on exit. Also added support for thehkeypress for help.closeanderrorevents.auth.ok.timingSafeEqualStrfor username authentication ingit-handler.ts.shutdown()now waits for log stream buffers to drain before callingprocess.exit(0).packages/crosscode/tsup.config.tswithnoExternal: [/@crosscode\/shared/]so workspace dependencies are bundled directly for CLI runtime usage."type": "module"topackages/shared/package.json.@crosscode/sharedtodependenciesinpackages/crosscode/package.json.🧪 Verification
pnpm -r buildcompleted successfully across all workspace packages and apps.pnpm -r exec -- tsc --noEmitpassed with 0 errors.node packages/crosscode/dist/cli.js --help.