Skip to content

relay command and configuration also made menu more clean and simple#4

Merged
AustinKelsay merged 1 commit intomasterfrom
staging
Oct 13, 2025
Merged

relay command and configuration also made menu more clean and simple#4
AustinKelsay merged 1 commit intomasterfrom
staging

Conversation

@AustinKelsay
Copy link
Copy Markdown
Member

@AustinKelsay AustinKelsay commented Oct 13, 2025

Summary by CodeRabbit

  • New Features

    • Added Relays command and UI to list, set, add, remove, and reset default relays with per-user persistence, used by signer/status flows.
    • Contextual help now renders in-app for select commands; Help screen redesigned with core commands, subcommands, and common options.
    • Support overriding the app data path via IGLOO_APPDATA.
  • Documentation

    • README updated with relays commands and notes on persistent defaults under Automation flags.
  • Tests

    • New tests cover relay utilities and persistence; test script updated to run them.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary of changes
Documentation
README.md
Documents new igloo relays commands and persistent defaults note; no code changes.
Tooling & Tests
package.json, tests/relays.test.ts
Test script now runs typecheck plus relays tests; adds tests for normalize, resolve precedence, disk-backed config, and IGLOO_APPDATA pathing.
App Routing & Relays UI
src/App.tsx, src/components/relays/Relays.tsx
Adds Relays route and new Ink UI component handling list/set/add/remove/reset of configured default relays with persistence.
CLI Help Routing
src/cli.tsx
On --help, conditionally renders App for share/keyset/keys/relays; otherwise shows standard help screen.
Help/Intro Content
src/components/Help.tsx, src/components/Intro.tsx
Redesigns help output structure and command taxonomy; updates core commands and common options display.
Keyset Relay Resolution
src/components/keyset/KeysetSigner.tsx, src/components/keyset/KeysetStatus.tsx, src/components/keyset/useShareEchoListener.ts
Switches from static defaults to resolveRelaysWithFallbackSync with appropriate fallbacks; adds parsing helper and imports for new relay utilities.
Keyset Infrastructure
src/keyset/paths.ts, src/keyset/relays.ts
Adds IGLOO_APPDATA override for app data path; introduces relays config module (types, normalize, read/write/remove, sync/async reads, resolver, defaults).

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I hop through relays, swift and bright,
Tuning defaults by moonlit night.
Set, add, remove—thump-thump, I save!
A warren of websockets, stout and brave.
Help screens tidy, routes aligned—
Carrot-commits, neatly signed. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly highlights the introduction of the relay command and configuration changes along with the menu simplification, which directly reflects the primary updates in the pull request without extraneous detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch staging

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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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.tsx and App.tsx can reference to reduce duplication and keep the command surface in sync.

src/components/relays/Relays.tsx (2)

41-52: Code duplication: consider reusing normalizeRelays.

The uniqueLower function implements case-insensitive deduplication, which partially overlaps with normalizeRelays from ../../keyset/relays.js (which does deduplication, trimming, and URL validation).

Consider whether this helper could be replaced by or integrated with normalizeRelays to reduce duplication and ensure consistent behavior.


85-90: Verify disk-first fallback logic for consistency.

Both handleAdd and handleRemove read from disk first (readConfiguredRelaysSync()), then fall back to the in-memory configured state, then to DEFAULT_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

📥 Commits

Reviewing files that changed from the base of the PR and between fd8941d and 2dbc6c4.

📒 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.ts
  • src/App.tsx
  • src/components/Help.tsx
  • src/cli.tsx
  • src/components/relays/Relays.tsx
  • src/components/Intro.tsx
  • src/components/keyset/useShareEchoListener.ts
  • src/keyset/relays.ts
  • src/components/keyset/KeysetStatus.tsx
  • src/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.ts
  • src/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.ts
  • src/App.tsx
  • src/components/Help.tsx
  • src/cli.tsx
  • src/components/relays/Relays.tsx
  • tests/relays.test.ts
  • src/components/Intro.tsx
  • src/components/keyset/useShareEchoListener.ts
  • src/keyset/relays.ts
  • src/components/keyset/KeysetStatus.tsx
  • src/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.tsx
  • src/components/Help.tsx
  • src/cli.tsx
  • src/components/relays/Relays.tsx
  • src/components/Intro.tsx
  • src/components/keyset/KeysetStatus.tsx
  • src/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 needed

Place 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.tsx
  • src/components/relays/Relays.tsx
  • src/components/Intro.tsx
  • src/components/keyset/useShareEchoListener.ts
  • src/components/keyset/KeysetStatus.tsx
  • src/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.tsx
  • src/components/relays/Relays.tsx
  • src/components/Intro.tsx
  • src/components/keyset/KeysetStatus.tsx
  • src/components/keyset/KeysetSigner.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components using PascalCase

Files:

  • src/components/Help.tsx
  • src/components/relays/Relays.tsx
  • src/components/Intro.tsx
  • src/components/keyset/useShareEchoListener.ts
  • src/components/keyset/KeysetStatus.tsx
  • src/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 relays commands 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 set helps users understand the relationship between the --relays flag 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 relays command 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 .js extension as required by the TypeScript ESM guidelines.


135-136: LGTM!

The new relays route follows the established pattern and correctly passes flags and args to 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 .js extensions and bring in the necessary relay resolution utilities.


93-93: LGTM!

The catch block correctly falls back to the configured relays or DEFAULT_ECHO_RELAYS when decoding fails, ensuring the hook always returns a valid relay set.


86-89: Confirm fallback precedence for empty group relays
When extractRelays(decoded) returns [] or undefined, the hook calls resolveRelaysWithFallbackSync(undefined, DEFAULT_ECHO_RELAYS), which prefers configured relays over DEFAULT_ECHO_RELAYS. Ensure this matches the intended behavior—if an empty group credential should override configured relays, change the check to fromGroup !== undefined.

src/components/keyset/KeysetStatus.tsx (2)

17-17: LGTM!

The import correctly includes the .js extension 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 parseBooleanFlag helper 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_RELAYS to resolveRelaysWithFallbackSync enables 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 relays command 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:

  • RelaysConfig includes an optional updatedAt for audit trails
  • getConfigDirectory uses getAppDataPath() with 'igloo' namespace for proper user-level isolation
  • getRelaysConfigPath correctly composes the full path

22-29: LGTM! Robust URL validation.

The isWsUrl helper correctly validates WebSocket URLs by checking both wss: and ws: protocols, with proper error handling for invalid URLs via try-catch.


31-45: LGTM! Comprehensive normalization.

The normalizeRelays function 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 readConfiguredRelays appropriately:

  • Returns undefined for 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 readConfiguredRelaysSync mirrors 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 writeConfiguredRelays function:

  • 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 removeConfiguredRelays function correctly ignores ENOENT errors (file already absent) while propagating other errors, ensuring idempotent behavior.


98-115: LGTM! Clear precedence logic.

The resolveRelaysWithFallbackSync implements the documented precedence correctly:

  1. Override (if valid after normalization)
  2. Configured defaults (from disk)
  3. Supplied fallback

The fallback to configured/fallback when overrides normalize away (line 108) is a good safety net for invalid overrides.

@AustinKelsay AustinKelsay merged commit b2e3d5b into master Oct 13, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 4, 2026
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