Skip to content

Self-hosting and Oauth (CYPACK-669) - #707

Merged
Connoropolous merged 18 commits into
mainfrom
cypack-669
Dec 28, 2025
Merged

Connoropolous merged 18 commits into
mainfrom
cypack-669

Conversation

@cyrusagent

@cyrusagent cyrusagent commented Dec 28, 2025 •

Copy link
Copy Markdown
Contributor

Summary

This PR addresses all 18 code review change requests from PR #656 (oauth-for-cedar from grandmore/cyrus-self-hosting) to prepare the branch for merging.

Changes Made

Type Safety Improvements:

  • Removed all any types from SelfAddRepoCommand.ts - added proper EdgeConfig and WorkspaceCredentials types
  • Removed all any types from SelfAuthCommand.ts - added proper EdgeConfig type
  • Added WorkspaceCredentials interface for type-safe workspace credential handling

Configuration Constants:

  • Moved shared constants (DEFAULT_WORKTREES_DIR, DEFAULT_BASE_BRANCH, DEFAULT_CONFIG_FILENAME) to cyrus-core package for global reuse
  • These constants are now imported from cyrus-core instead of being defined locally

OAuth Improvements:

  • Trimmed OAuth scopes to just write,app:assignable,app:mentionable (matching hosted version)
  • Added OAuth documentation links as comments

Framework Migration:

  • Replaced Node.js http.createServer with Fastify for the OAuth callback server
  • Updated test mocks to work with Fastify route handler pattern

SDK Usage:

  • Replaced raw fetch call with LinearClient SDK for fetchWorkspaceInfo

Code Clarity:

  • Renamed updateConfig to overwriteRepoConfigTokens for clarity
  • Added documentation comments for url and workspace parameters in app.ts
  • Added explanatory comment for the finally block behavior in LinearIssueTrackerService.ts

Documentation Restructure:

  • Created docs/ folder for documentation
  • Moved selfhosting/README.md to docs/SELF_HOSTING.md
  • Extracted config.json reference to docs/CONFIG_FILE.md
  • Extracted Cloudflare tunnel setup to docs/CLOUDFLARE_TUNNEL.md (now marked as optional)
  • Updated main README.md with documentation links
  • Deleted selfhosting/ folder

Documentation Fixes:

  • Fixed README numbering (2.1/2.2 → 1.1/1.2) in self-hosting guide
  • Removed destructive tmux kill-server command from README
  • Deleted obsolete plan documents

Test Plan

  • All 344 tests passing across 8 packages
  • TypeScript type checking passes
  • Linting passes
  • Manual hands-on testing of self-auth and self-add-repo commands with custom --cyrus-home path
  • Verified OAuth URL generation with correct scopes
  • Verified repository cloning and config.json updates

Rebased

This PR has been rebased on latest main branch with merge conflicts resolved.


🤖 Generated with Claude Code

StuartFenton and others added 7 commits December 28, 2025 11:31
Implements automatic OAuth token refresh with the setHeader pattern:

- LinearIssueTrackerService patches client.request to intercept 401 errors
  and retry with refreshed token using client.setHeader
- EdgeWorker manages token refresh with promise coalescing to prevent
  concurrent refresh attempts
- Adds linearRefreshToken field to RepositoryConfig
- IIssueTrackerService gains setAccessToken method for external token updates
- Tests verify token refresh behavior and mock LinearClient properly

Clean implementation on upstream/main with minimal changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous implementation had a race condition where concurrent 401 errors
would each trigger separate token refresh attempts. This fix:

1. Keeps the refresh promise around after resolution so ALL concurrent 401
   errors share the same refreshed token
2. Only clears the promise on refresh failure, allowing fresh retry
3. Adds isRetry flag to prevent infinite retry loops
4. Adds guards for test mocks that don't have .client property

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactored OAuth token refresh to be fully encapsulated within
LinearIssueTrackerService (packages/linear-event-transport), removing
all refresh logic from EdgeWorker (packages/edge-worker).

LinearIssueTrackerService now handles:
- Token refresh HTTP calls to Linear OAuth endpoint
- Workspace-level coalescing via static `pendingRefreshes` map
- Shared refresh token state via static `workspaceRefreshTokens` map
- Per-instance LinearClient header updates

EdgeWorker's remaining role (minimal, cannot be removed):
- Passes initial credentials via LinearOAuthConfig
- onTokenRefresh callback ONLY persists to config.json

NOTE: EdgeWorker owns config.json, so we cannot completely remove it
from the token flow. The onTokenRefresh callback is the minimum interface
needed - LinearIssueTrackerService emits when tokens change, EdgeWorker
persists them. All actual refresh logic is in LinearIssueTrackerService.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- SelfAuthCommand: OAuth flow for authenticating with Linear directly from CLI
- SelfAddRepoCommand: Clone repository and add it to config with workspace selection
- Comprehensive test coverage for both commands (33 tests total)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Complete setup guide for self-hosted Cyrus deployments
- Steps 6 and 7 use new CLI commands: cyrus self-auth, cyrus self-add-repo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove all 'any' types from SelfAddRepoCommand.ts and SelfAuthCommand.ts
- Add DEFAULT_WORKTREES_DIR and DEFAULT_BASE_BRANCH constants
- Trim OAuth scopes to write,app:assignable,app:mentionable
- Replace http.createServer with Fastify in SelfAuthCommand.ts
- Use LinearClient SDK for fetchWorkspaceInfo instead of raw fetch
- Rename updateConfig to overwriteRepoConfigTokens
- Add url/workspace documentation comments in app.ts
- Add finally block and OAuth doc comments in LinearIssueTrackerService.ts
- Fix selfhosting README numbering (2.1/2.2 → 1.1/1.2)
- Remove destructive tmux kill-server command from README
- Delete obsolete plan docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migrated test mocks from Node http.createServer to Fastify,
matching the implementation changes from PR #656 review.
Also updated LinearClient SDK mock for workspace info tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DEFAULT_WORKTREES_DIR, DEFAULT_BASE_BRANCH, DEFAULT_CONFIG_FILENAME
  constants to cyrus-core package for consistent usage across codebase
- Update SelfAddRepoCommand.ts, SelfAuthCommand.ts, Application.ts, and
  apps/f1/server.ts to use the shared constants
- Improve self-add-repo command description to document URL and workspace
  parameters directly in the CLI help output
- Fix LinearClient mock in SelfAuthCommand tests to preserve other
  @linear/sdk exports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment thread apps/cli/src/commands/SelfAuthCommand.ts
Connoropolous and others added 9 commits December 28, 2025 11:56
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move selfhosting/README.md to docs/SELF_HOSTING.md
- Extract config.json reference to docs/CONFIG_FILE.md
- Extract Cloudflare tunnel setup to docs/CLOUDFLARE_TUNNEL.md (optional)
- Update main README.md with documentation links
- Delete selfhosting/ folder

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The shared constants move doesn't warrant an internal changelog entry.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Connoropolous
Connoropolous merged commit 6051eac into main Dec 28, 2025
6 checks passed
@Connoropolous Connoropolous changed the title Address PR #656 code review feedback (CYPACK-669) Self-hosting and Oauth (CYPACK-669) Dec 28, 2025
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.

3 participants