relay command and configuration also made menu more clean and simple#4
relay command and configuration also made menu more clean and simple#4AustinKelsay merged 1 commit intomasterfrom
Conversation
WalkthroughAdds relay configuration support and a new Relays UI/CLI subcommand. Introduces a relays config module with read/write/normalize and resolution utilities, updates keyset components to use resolver with fallbacks, revises Help/Intro displays, adjusts CLI help routing, adds tests, updates package scripts, and documents new commands. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant CLI as CLI entry (src/cli.tsx)
participant App as App Router (src/App.tsx)
participant Help as Help UI
participant Relays as Relays UI
User->>CLI: igloo --help [command]
alt command in {share,keyset,keys,relays}
CLI->>App: render(command, args, flags, version)
alt command == relays
App->>Relays: <Relays flags,args>
Relays-->>User: Render relays help/usage
else other supported commands
App->>Help: Render help for command
Help-->>User: Command-specific help
end
else other/none
CLI->>Help: showHelpScreen(version)
Help-->>User: General help
end
note over CLI,Relays: New conditional help path renders App for specific commands
sequenceDiagram
autonumber
participant Cmp as Component (Signer/Status/Echo)
participant Resolver as resolveRelaysWithFallbackSync
participant Disk as readConfiguredRelays(Sync)
participant Fallback as Built-in defaults
Cmp->>Resolver: override?, fallback[]
alt override provided and valid
Resolver-->>Cmp: normalized override[]
else no/invalid override
Resolver->>Disk: read configured relays
alt configured present
Resolver-->>Cmp: normalized configured[]
else none/error
Resolver->>Fallback: use fallback[]
Resolver-->>Cmp: normalized fallback[]
end
end
note over Resolver: Precedence: override > configured > fallback
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
package.json (1)
7-7: Consider using a test pattern instead of hardcoding a single test file.The current approach hardcodes
tests/relays.test.ts. If additional test files are added in the future, each would need to be manually added to this script.Consider using a glob pattern to automatically discover test files:
- "test": "npm run typecheck && tsx --test tests/relays.test.ts", + "test": "npm run typecheck && tsx --test 'tests/**/*.test.ts'",This makes the test script more maintainable as the test suite grows.
src/cli.tsx (1)
109-121: LGTM! Consider a central command registry.The dual-path help flow correctly routes helpable commands to the App component while falling back to the standard help screen. However, the hardcoded Set at line 109 could become a maintenance burden if commands are added without updating this list.
Consider extracting the helpable commands to a shared constant or registry that both
cli.tsxandApp.tsxcan reference to reduce duplication and keep the command surface in sync.src/components/relays/Relays.tsx (2)
41-52: Code duplication: consider reusingnormalizeRelays.The
uniqueLowerfunction implements case-insensitive deduplication, which partially overlaps withnormalizeRelaysfrom../../keyset/relays.js(which does deduplication, trimming, and URL validation).Consider whether this helper could be replaced by or integrated with
normalizeRelaysto reduce duplication and ensure consistent behavior.
85-90: Verify disk-first fallback logic for consistency.Both
handleAddandhandleRemoveread from disk first (readConfiguredRelaysSync()), then fall back to the in-memoryconfiguredstate, then toDEFAULT_PING_RELAYS. This ensures fresh data but may behave unexpectedly if the disk state has changed since the component mounted.Verify whether this fallback chain is intentional. If the goal is always using the freshest disk state, consider refactoring to consistently use
readConfiguredRelaysSync()for the current value rather than mixing disk and memory state.Also applies to: 96-101
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
README.md(2 hunks)package.json(1 hunks)src/App.tsx(2 hunks)src/cli.tsx(1 hunks)src/components/Help.tsx(1 hunks)src/components/Intro.tsx(1 hunks)src/components/keyset/KeysetSigner.tsx(3 hunks)src/components/keyset/KeysetStatus.tsx(2 hunks)src/components/keyset/useShareEchoListener.ts(2 hunks)src/components/relays/Relays.tsx(1 hunks)src/keyset/paths.ts(1 hunks)src/keyset/relays.ts(1 hunks)tests/relays.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
All import specifiers must include .js extensions (TypeScript ESM requirement) even though source files are .ts/.tsx
Files:
src/keyset/paths.tssrc/App.tsxsrc/components/Help.tsxsrc/cli.tsxsrc/components/relays/Relays.tsxsrc/components/Intro.tsxsrc/components/keyset/useShareEchoListener.tssrc/keyset/relays.tssrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/keyset/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/keyset/**/*.ts: Keep key exchange and credential helpers under src/keyset/
Document non-obvious flows with concise comments, especially around key lifecycle management
Files:
src/keyset/paths.tssrc/keyset/relays.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript with ESM, 2-space indentation, single quotes, and trailing commas
Order imports from shallow-to-deep
Use camelCase for utility function and variable names
Use SCREAMING_SNAKE_CASE for constants
Files:
src/keyset/paths.tssrc/App.tsxsrc/components/Help.tsxsrc/cli.tsxsrc/components/relays/Relays.tsxtests/relays.test.tssrc/components/Intro.tsxsrc/components/keyset/useShareEchoListener.tssrc/keyset/relays.tssrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/**/*.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Use Ink components (, , etc.) for terminal UI and do not use HTML elements
Files:
src/App.tsxsrc/components/Help.tsxsrc/cli.tsxsrc/components/relays/Relays.tsxsrc/components/Intro.tsxsrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/App.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
src/App.tsx: Route commands via a switch statement in App.tsx based on a normalized command string
When adding a new command, import the new component in App.tsx and add a corresponding case to the switch
Extract flags from props in App.tsx and pass them to command components; add validation/defaults as neededPlace routes and screen wiring in src/App.tsx
Files:
src/App.tsx
src/components/**
📄 CodeRabbit inference engine (CLAUDE.md)
All UI components should live under src/components/
Files:
src/components/Help.tsxsrc/components/relays/Relays.tsxsrc/components/Intro.tsxsrc/components/keyset/useShareEchoListener.tssrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/components/**/*.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
When adding a new command, implement it as a component under src/components/
Store reusable UI components under src/components/
Files:
src/components/Help.tsxsrc/components/relays/Relays.tsxsrc/components/Intro.tsxsrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Name React components using PascalCase
Files:
src/components/Help.tsxsrc/components/relays/Relays.tsxsrc/components/Intro.tsxsrc/components/keyset/useShareEchoListener.tssrc/components/keyset/KeysetStatus.tsxsrc/components/keyset/KeysetSigner.tsx
src/cli.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
src/cli.tsx: Argument parsing is handled manually in cli.tsx via parseArgv() and must support --flag value, --flag=value, and -f value forms
When adding command flags, extend parseArgv() in cli.tsx for any custom parsing logic
src/cli.tsx: Keep the CLI entrypoint in src/cli.tsx and use it to bootstrap Ink rendering
Keep CLI flags lower-case (e.g., --verbose)
Files:
src/cli.tsx
**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
**/*.test.ts: Allow colocated test files named feature.test.ts beside implementations
Test files should exercise both happy paths and failure modes
Files:
tests/relays.test.ts
src/components/Intro.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
When adding a new command, update command examples in Intro.tsx if applicable
Files:
src/components/Intro.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-08T16:24:14.475Z
Learnt from: CR
PR: FROSTR-ORG/igloo-cli#0
File: AGENTS.md:0-0
Timestamp: 2025-10-08T16:24:14.475Z
Learning: Applies to src/App.tsx : Place routes and screen wiring in src/App.tsx
Applied to files:
src/App.tsx
📚 Learning: 2025-10-03T23:45:30.161Z
Learnt from: CR
PR: FROSTR-ORG/igloo-cli#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-03T23:45:30.161Z
Learning: Applies to src/components/Intro.tsx : When adding a new command, update command examples in Intro.tsx if applicable
Applied to files:
src/components/Intro.tsx
🧬 Code graph analysis (8)
src/App.tsx (1)
src/components/relays/Relays.tsx (1)
Relays(54-195)
src/cli.tsx (1)
src/App.tsx (1)
App(125-164)
src/components/relays/Relays.tsx (1)
src/keyset/relays.ts (5)
DEFAULT_SIGNER_RELAYS(12-12)readConfiguredRelays(47-62)writeConfiguredRelays(77-85)readConfiguredRelaysSync(64-75)removeConfiguredRelays(87-96)
tests/relays.test.ts (1)
src/keyset/relays.ts (6)
removeConfiguredRelays(87-96)normalizeRelays(31-45)resolveRelaysWithFallbackSync(99-115)writeConfiguredRelays(77-85)readConfiguredRelaysSync(64-75)getRelaysConfigPath(18-20)
src/components/keyset/useShareEchoListener.ts (1)
src/keyset/relays.ts (1)
resolveRelaysWithFallbackSync(99-115)
src/keyset/relays.ts (1)
src/keyset/paths.ts (1)
getAppDataPath(4-20)
src/components/keyset/KeysetStatus.tsx (1)
src/keyset/relays.ts (1)
resolveRelaysWithFallbackSync(99-115)
src/components/keyset/KeysetSigner.tsx (1)
src/keyset/relays.ts (2)
resolveRelaysWithFallbackSync(99-115)DEFAULT_SIGNER_RELAYS(12-12)
🔇 Additional comments (24)
README.md (2)
72-73: LGTM!The documentation clearly describes the new
igloo relayscommands and aligns with the PR objectives to add relay configuration support.
111-111: Good addition of configuration context.The note about persistent defaults via
igloo relays sethelps users understand the relationship between the--relaysflag and the new configuration command.src/keyset/paths.ts (1)
5-8: LGTM!The environment variable override provides a clean way to customize the app data path for testing and CI environments. The implementation correctly falls back to platform-specific logic when the override is not provided.
src/components/Intro.tsx (1)
20-27: LGTM!The reformatted command list is more concise and scannable. The new
relayscommand is properly integrated into the taxonomy.Based on learnings: This correctly updates command examples in Intro.tsx when adding a new command, as per the project's guidelines.
src/App.tsx (2)
16-16: LGTM!The import correctly includes the
.jsextension as required by the TypeScript ESM guidelines.
135-136: LGTM!The new
relaysroute follows the established pattern and correctly passesflagsandargsto the Relays component.Based on learnings: This correctly places the route in src/App.tsx as per the project's guidelines.
src/components/keyset/useShareEchoListener.ts (3)
2-9: LGTM!The imports correctly include
.jsextensions and bring in the necessary relay resolution utilities.
93-93: LGTM!The catch block correctly falls back to the configured relays or
DEFAULT_ECHO_RELAYSwhen decoding fails, ensuring the hook always returns a valid relay set.
86-89: Confirm fallback precedence for empty group relays
WhenextractRelays(decoded)returns[]orundefined, the hook callsresolveRelaysWithFallbackSync(undefined, DEFAULT_ECHO_RELAYS), which prefers configured relays overDEFAULT_ECHO_RELAYS. Ensure this matches the intended behavior—if an empty group credential should override configured relays, change the check tofromGroup !== undefined.src/components/keyset/KeysetStatus.tsx (2)
17-17: LGTM!The import correctly includes the
.jsextension and brings in the relay resolution utility.
276-276: LGTM!The relay resolution now follows a consistent precedence: override flags → configured defaults → fallback. This aligns with the PR objectives to support persistent relay configuration.
tests/relays.test.ts (1)
1-92: LGTM! Comprehensive test coverage.The test suite provides excellent coverage of the relay utilities:
- Normalization and deduplication (lines 30-39)
- Precedence rules for override/configured/fallback (lines 41-54)
- Invalid input fallback behavior (lines 56-65)
- Disk-first add/remove operations (lines 67-84)
- Path isolation with IGLOO_APPDATA (lines 86-92)
The setup/teardown pattern ensures test isolation, and the use of a temp directory prevents pollution of the user's actual config.
src/components/keyset/KeysetSigner.tsx (2)
69-85: LGTM! Useful helper for normalizing boolean flags.The
parseBooleanFlaghelper correctly handles various boolean representations ('1', 'true', 'yes', 'on' and their negatives), which is valuable for CLI flag parsing where users may use different conventions.
252-252: LGTM! Relay resolution now supports configured defaults.The switch from static
DEFAULT_PING_RELAYStoresolveRelaysWithFallbackSyncenables the signer to respect user-configured relay defaults while maintaining the fallback behavior. This aligns with the PR's goal of introducing relay configuration management.src/components/Help.tsx (1)
11-47: LGTM! Much clearer help content.The restructured help significantly improves discoverability:
- Clear title and branding (lines 11-15)
- Well-organized command categories (Core commands, Subcommands, Common options)
- Includes the new
relayscommand with its subcommands (line 25, 34)The multi-block layout makes it easy to scan for specific information.
src/components/relays/Relays.tsx (1)
156-194: LGTM! Clear and informative UI.The rendering provides:
- Clear separation between effective defaults (signer/status fallbacks) and configured overrides
- Helpful usage examples (lines 186-191)
- Appropriate use of color coding for visual hierarchy
src/keyset/relays.ts (8)
6-20: LGTM! Clean API design with appropriate path isolation.The type definition and path helpers provide a solid foundation:
RelaysConfigincludes an optionalupdatedAtfor audit trailsgetConfigDirectoryusesgetAppDataPath()with 'igloo' namespace for proper user-level isolationgetRelaysConfigPathcorrectly composes the full path
22-29: LGTM! Robust URL validation.The
isWsUrlhelper correctly validates WebSocket URLs by checking bothwss:andws:protocols, with proper error handling for invalid URLs via try-catch.
31-45: LGTM! Comprehensive normalization.The
normalizeRelaysfunction correctly:
- Filters non-string values (defensive programming)
- Trims whitespace
- Validates WebSocket URLs
- Deduplicates case-insensitively while preserving original casing
- Returns a clean array
47-62: LGTM! Graceful error handling.The async
readConfiguredRelaysappropriately:
- Returns
undefinedfor missing files (ENOENT, ENOTDIR)- Treats malformed JSON as no config (line 59-60) rather than crashing
- Normalizes and filters empty results
This defensive approach prevents CLI crashes from corrupted config files.
64-75: LGTM! Synchronous variant for resolver.The synchronous
readConfiguredRelaysSyncmirrors the async version's behavior and is necessary for use in the resolver (which needs to execute synchronously during initialization).
77-85: LGTM! Atomic write with directory creation.The
writeConfiguredRelaysfunction:
- Normalizes input before writing
- Ensures the directory exists (
recursive: true)- Includes a timestamp for audit purposes
- Returns the normalized array for caller convenience
87-96: LGTM! Safe file deletion.The
removeConfiguredRelaysfunction correctly ignores ENOENT errors (file already absent) while propagating other errors, ensuring idempotent behavior.
98-115: LGTM! Clear precedence logic.The
resolveRelaysWithFallbackSyncimplements the documented precedence correctly:
- Override (if valid after normalization)
- Configured defaults (from disk)
- Supplied fallback
The fallback to configured/fallback when overrides normalize away (line 108) is a good safety net for invalid overrides.
Summary by CodeRabbit
New Features
Documentation
Tests