Skip to content

Commit fb0d597

Browse files
MarkoVcodeclaude
andcommitted
feat: Add GitHub Copilot authentication (Phase 1.1)
Implements OAuth Device Flow authentication for GitHub Copilot access: Backend: - Created encrypted token store using electron-store with SafeStorage - Implemented OAuth Device Flow using @octokit/auth-oauth-device - Added IPC handlers for authentication, token management, and logout - Created authentication service with Copilot access verification Frontend: - Added Redux slice for Copilot state management - Created Copilot UI component with authentication flow - Integrated Copilot tab into DevTools Console - Added IPC utility functions for renderer process Features: - One-time OAuth authentication with browser redirect - Secure token storage with OS-level encryption - Verification code display during auth flow - Authentication status checking on mount - Logout functionality - GitHub Copilot access verification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 654ed02 commit fb0d597

12 files changed

Lines changed: 1361 additions & 98 deletions

File tree

package-lock.json

Lines changed: 220 additions & 90 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/bruno-app/src/components/Devtools/Console/index.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@ import React, { useEffect, useRef, useState } from 'react';
22
import { useSelector, useDispatch } from 'react-redux';
33
import ReactJson from 'react-json-view';
44
import { useTheme } from 'providers/Theme';
5-
import {
6-
IconX,
7-
IconTrash,
5+
import {
6+
IconX,
7+
IconTrash,
88
IconFilter,
9-
IconAlertTriangle,
10-
IconAlertCircle,
9+
IconAlertTriangle,
10+
IconAlertCircle,
1111
IconBug,
1212
IconCode,
1313
IconChevronDown,
1414
IconTerminal2,
1515
IconNetwork,
1616
IconDashboard,
17-
IconRoute
17+
IconRoute,
18+
IconBrandGithubCopilot
1819
} from '@tabler/icons';
1920
import {
2021
closeConsole,
@@ -33,6 +34,7 @@ import RequestDetailsPanel from './RequestDetailsPanel';
3334
import ErrorDetailsPanel from './ErrorDetailsPanel';
3435
import Performance from '../Performance';
3536
import Trace from '../Trace';
37+
import Copilot from '../Copilot';
3638
import StyledWrapper from './StyledWrapper';
3739

3840
const LogIcon = ({ type }) => {
@@ -393,6 +395,8 @@ const Console = () => {
393395
return <Performance />;
394396
case 'trace':
395397
return <Trace />;
398+
case 'copilot':
399+
return <Copilot />;
396400
// case 'debug':
397401
// return <DebugTab />;
398402
default:
@@ -509,6 +513,14 @@ const Console = () => {
509513
<span>Trace</span>
510514
</button>
511515

516+
<button
517+
className={`console-tab ${activeTab === 'copilot' ? 'active' : ''}`}
518+
onClick={() => handleTabChange('copilot')}
519+
>
520+
<IconBrandGithubCopilot size={16} strokeWidth={1.5} />
521+
<span>Copilot</span>
522+
</button>
523+
512524
{/* <button
513525
className={`console-tab ${activeTab === 'debug' ? 'active' : ''}`}
514526
onClick={() => handleTabChange('debug')}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import styled from 'styled-components';
2+
3+
const StyledWrapper = styled.div`
4+
width: 100%;
5+
height: 100%;
6+
background: ${(props) => props.theme.console.bg};
7+
border-top: 1px solid ${(props) => props.theme.console.border};
8+
display: flex;
9+
flex-direction: column;
10+
overflow: hidden;
11+
12+
.copilot-header {
13+
display: flex;
14+
align-items: center;
15+
justify-content: space-between;
16+
padding: 12px 16px;
17+
background: ${(props) => props.theme.console.headerBg};
18+
border-bottom: 1px solid ${(props) => props.theme.console.border};
19+
flex-shrink: 0;
20+
}
21+
22+
.copilot-header-left {
23+
display: flex;
24+
align-items: center;
25+
gap: 12px;
26+
}
27+
28+
.copilot-title {
29+
font-size: 14px;
30+
font-weight: 600;
31+
color: ${(props) => props.theme.text};
32+
}
33+
34+
.auth-status {
35+
display: flex;
36+
align-items: center;
37+
gap: 6px;
38+
font-size: 12px;
39+
padding: 4px 8px;
40+
border-radius: 4px;
41+
background: ${(props) => props.theme.console.buttonBg};
42+
color: ${(props) => props.theme.text};
43+
44+
&.authenticated {
45+
background: rgba(76, 175, 80, 0.1);
46+
color: #4caf50;
47+
}
48+
49+
&.error {
50+
background: rgba(244, 67, 54, 0.1);
51+
color: #f44336;
52+
}
53+
}
54+
55+
.status-dot {
56+
width: 6px;
57+
height: 6px;
58+
border-radius: 50%;
59+
background: currentColor;
60+
}
61+
62+
.copilot-content {
63+
flex: 1;
64+
overflow-y: auto;
65+
padding: 16px;
66+
}
67+
68+
.auth-container {
69+
max-width: 600px;
70+
margin: 40px auto;
71+
text-align: center;
72+
}
73+
74+
.auth-icon {
75+
width: 64px;
76+
height: 64px;
77+
margin: 0 auto 24px;
78+
background: ${(props) => props.theme.console.buttonBg};
79+
border-radius: 50%;
80+
display: flex;
81+
align-items: center;
82+
justify-content: center;
83+
color: ${(props) => props.theme.text};
84+
font-size: 32px;
85+
}
86+
87+
.auth-title {
88+
font-size: 24px;
89+
font-weight: 600;
90+
margin-bottom: 12px;
91+
color: ${(props) => props.theme.text};
92+
}
93+
94+
.auth-description {
95+
font-size: 14px;
96+
color: ${(props) => props.theme.textSecondary};
97+
margin-bottom: 24px;
98+
line-height: 1.6;
99+
}
100+
101+
.auth-button {
102+
display: inline-flex;
103+
align-items: center;
104+
gap: 8px;
105+
padding: 12px 24px;
106+
background: ${(props) => props.theme.button.bg};
107+
color: ${(props) => props.theme.button.color};
108+
border: none;
109+
border-radius: 6px;
110+
font-size: 14px;
111+
font-weight: 500;
112+
cursor: pointer;
113+
transition: all 0.2s ease;
114+
115+
&:hover:not(:disabled) {
116+
background: ${(props) => props.theme.button.hoverBg};
117+
transform: translateY(-1px);
118+
}
119+
120+
&:disabled {
121+
opacity: 0.6;
122+
cursor: not-allowed;
123+
}
124+
}
125+
126+
.verification-box {
127+
margin-top: 24px;
128+
padding: 16px;
129+
background: ${(props) => props.theme.console.headerBg};
130+
border: 1px solid ${(props) => props.theme.console.border};
131+
border-radius: 8px;
132+
text-align: left;
133+
}
134+
135+
.verification-title {
136+
font-size: 14px;
137+
font-weight: 600;
138+
margin-bottom: 12px;
139+
color: ${(props) => props.theme.text};
140+
}
141+
142+
.verification-steps {
143+
font-size: 13px;
144+
color: ${(props) => props.theme.textSecondary};
145+
line-height: 1.8;
146+
margin-bottom: 16px;
147+
148+
ol {
149+
padding-left: 20px;
150+
margin: 0;
151+
}
152+
153+
li {
154+
margin-bottom: 8px;
155+
}
156+
}
157+
158+
.user-code {
159+
display: flex;
160+
align-items: center;
161+
gap: 12px;
162+
padding: 12px;
163+
background: ${(props) => props.theme.console.bg};
164+
border: 1px solid ${(props) => props.theme.console.border};
165+
border-radius: 6px;
166+
margin-top: 12px;
167+
}
168+
169+
.user-code-label {
170+
font-size: 12px;
171+
color: ${(props) => props.theme.textSecondary};
172+
font-weight: 500;
173+
}
174+
175+
.user-code-value {
176+
font-size: 18px;
177+
font-weight: 600;
178+
font-family: 'Courier New', monospace;
179+
color: ${(props) => props.theme.text};
180+
letter-spacing: 2px;
181+
}
182+
183+
.error-message {
184+
margin-top: 16px;
185+
padding: 12px;
186+
background: rgba(244, 67, 54, 0.1);
187+
border: 1px solid rgba(244, 67, 54, 0.3);
188+
border-radius: 6px;
189+
color: #f44336;
190+
font-size: 13px;
191+
text-align: left;
192+
}
193+
194+
.logout-button {
195+
padding: 6px 12px;
196+
background: transparent;
197+
color: ${(props) => props.theme.textSecondary};
198+
border: 1px solid ${(props) => props.theme.console.border};
199+
border-radius: 4px;
200+
font-size: 12px;
201+
cursor: pointer;
202+
transition: all 0.2s ease;
203+
204+
&:hover {
205+
background: ${(props) => props.theme.console.buttonBg};
206+
color: ${(props) => props.theme.text};
207+
}
208+
}
209+
`;
210+
211+
export default StyledWrapper;

0 commit comments

Comments
 (0)