circuit-mcp 2.0.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -117
- package/package.json +1 -1
- package/preview-error.html +57 -0
- package/preview-success.html +57 -0
- package/src/auth.js +74 -63
- package/src/server.js +348 -263
package/README.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# circuit-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Customer feedback in. Engineering specs out. Solve it with your AI coding tool.
|
|
4
4
|
|
|
5
|
-
Circuit
|
|
5
|
+
Connect [Circuit](https://withcircuit.com) to **Cursor** and **Claude Code** via the Model Context Protocol.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
### Claude Code
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
claude mcp add circuit -- npx circuit-mcp
|
|
15
|
+
```
|
|
8
16
|
|
|
9
17
|
### Cursor
|
|
10
18
|
|
|
@@ -21,166 +29,148 @@ Add to `~/.cursor/mcp.json`:
|
|
|
21
29
|
}
|
|
22
30
|
```
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
claude mcp add circuit -- npx circuit-mcp
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### First Run
|
|
31
|
-
|
|
32
|
-
On first use, Circuit opens your browser to authenticate. Your token is cached at `~/.circuit/token.json`.
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
Circuit MCP
|
|
36
|
-
|
|
37
|
-
First time setup — let's connect your account.
|
|
38
|
-
Opening browser to authenticate...
|
|
39
|
-
|
|
40
|
-
✓ Connected!
|
|
41
|
-
```
|
|
32
|
+
First run opens your browser to authenticate. Token is cached at `~/.circuit/token.json` (30-day expiry).
|
|
42
33
|
|
|
43
34
|
---
|
|
44
35
|
|
|
45
|
-
## Tools
|
|
36
|
+
## 4 Tools
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
### `circuit.priorities`
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
What should I work on? Ranked customer priorities with trends, confidence, and pattern matching.
|
|
50
41
|
|
|
51
|
-
|
|
42
|
+
| Parameter | Type | Description |
|
|
43
|
+
|-----------|------|-------------|
|
|
44
|
+
| `weekly` | boolean | Set to `true` to get the weekly digest instead (movements, new entries, drops, spikes) |
|
|
45
|
+
| `lens` | string | How to rank: `volume`, `urgency`, `revenue`, `retention`, `delight`, `feature` |
|
|
46
|
+
| `segment` | string | Customer segment: `enterprise`, `smb`, `all` |
|
|
47
|
+
| `limit` | number | Number of results (default: 5, max: 20) |
|
|
48
|
+
| `category` | string | Filter: `Bug`, `Feature`, `Friction`, `Complaint`, `Praise` |
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|-----------|-------------|
|
|
55
|
-
| `lens` | How to rank: `volume`, `urgency`, `revenue`, `retention`, `delight`, `feature` |
|
|
56
|
-
| `segment` | Filter by customer segment: `enterprise`, `smb`, `all` |
|
|
57
|
-
| `limit` | Number of priorities to return (default: 5, max: 20) |
|
|
58
|
-
| `category` | Filter: `Bug`, `Feature`, `Friction`, `Complaint`, `Praise` |
|
|
50
|
+
**Also returns:** Session context (last ship, in-progress builds, suggested next action) and instinct confidence (pattern matching from your shipping history).
|
|
59
51
|
|
|
60
|
-
|
|
52
|
+
**Weekly mode** (`weekly: true`): Returns priority movements (rank changes), new entries, dropped items, and volume spikes compared to last week.
|
|
61
53
|
|
|
62
|
-
|
|
54
|
+
### `circuit.spec`
|
|
63
55
|
|
|
64
|
-
|
|
56
|
+
Full engineering spec for a priority. 5 sections: What to Build, Why It Matters, Customer Voice (verbatim quotes), Files to Touch, Done When.
|
|
65
57
|
|
|
66
|
-
|
|
58
|
+
| Parameter | Type | Description |
|
|
59
|
+
|-----------|------|-------------|
|
|
60
|
+
| `priority_id` | string | Priority ID from `circuit.priorities` |
|
|
61
|
+
| `spec_id` | string | Spec ID directly (alternative to priority_id) |
|
|
62
|
+
| `include_history` | boolean | Include version history and ship memory (default: true) |
|
|
63
|
+
| `batch` | boolean | Set to `true` to export multiple specs as markdown |
|
|
64
|
+
| `spec_ids` | string[] | Specific spec IDs to export (batch mode) |
|
|
65
|
+
| `status` | string | Filter specs by status in batch mode: `ready`, `building`, `shipped` |
|
|
66
|
+
| `limit` | number | Number of specs in batch mode (default: 10, max: 50) |
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|-----------|-------------|
|
|
70
|
-
| `priority_id` or `build_id` | Which priority to get the brief for |
|
|
71
|
-
| `include_history` | Include version history and related ship memory (default: true) |
|
|
68
|
+
**Also returns:** Post-ship signal (new feedback since shipping), effort estimate, codebase context (tech stack, related PRs, AI config files like CLAUDE.md), and related memories from previous ships.
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
**Batch mode** (`batch: true`): Exports multiple specs as formatted markdown for sprint planning or documentation.
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
### `circuit.act`
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
One tool, four actions. Move work forward without leaving your editor.
|
|
74
|
+
Take action. Start building, ship it, share back, assign, correct, submit feedback, or submit a transcript.
|
|
80
75
|
|
|
81
76
|
| Action | What it does |
|
|
82
77
|
|--------|-------------|
|
|
83
|
-
| `build` | Mark
|
|
84
|
-
| `ship` | Mark shipped
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
| `build` | Mark spec as "building" |
|
|
79
|
+
| `ship` | Mark as shipped, record memory |
|
|
80
|
+
| `share` | Notify customers who submitted feedback (email, widget, or both) |
|
|
81
|
+
| `assign` | Assign spec to a team member by email or user ID |
|
|
82
|
+
| `correct` | Fix a priority's category classification (Circuit remembers corrections) |
|
|
83
|
+
| `submit` | Add new feedback from your terminal |
|
|
84
|
+
| `transcript` | Submit a customer interview, sales call, or support transcript |
|
|
89
85
|
|
|
90
|
-
|
|
86
|
+
**Share parameters** (when `action: "share"`):
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
| Parameter | Type | Description |
|
|
89
|
+
|-----------|------|-------------|
|
|
90
|
+
| `spec_id` | string | Spec ID (required) |
|
|
91
|
+
| `channel` | string | `email`, `widget`, `both`, `skip` |
|
|
92
|
+
| `message` | string | Custom message to include in notifications (optional) |
|
|
93
93
|
|
|
94
|
-
**
|
|
94
|
+
**Transcript parameters** (when `action: "transcript"`):
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
| Parameter | Type | Description |
|
|
97
|
+
|-----------|------|-------------|
|
|
98
|
+
| `text` | string | Full transcript text (min 50 chars, required) |
|
|
99
|
+
| `title` | string | Title, e.g. "Sales call with Acme Corp" |
|
|
100
|
+
| `type` | string | `interview`, `sales_call`, `support`, `other` |
|
|
101
|
+
| `customer_name` | string | Customer name (optional) |
|
|
102
|
+
| `customer_email` | string | Customer email (optional, for close-the-loop notifications) |
|
|
103
|
+
| `revenue_band` | string | `enterprise`, `paid`, `free` |
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
### `circuit.ask`
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
You: "What should I work on?"
|
|
104
|
-
Circuit: → circuit.priorities (lens: urgency)
|
|
105
|
-
Returns top 5, #1 is "Login timeout on slow connections"
|
|
107
|
+
Semantic search across all your data: feedback, priorities, specs, and help articles. Returns your shipping patterns and behavioral insights. Also searches your connected GitHub repo for code context.
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
| Parameter | Type | Description |
|
|
110
|
+
|-----------|------|-------------|
|
|
111
|
+
| `question` | string | Natural language, e.g. "What are enterprise customers saying about onboarding?" |
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
Circuit: → circuit.act (action: build)
|
|
113
|
-
Marked as building
|
|
113
|
+
---
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
## Workflow
|
|
116
116
|
|
|
117
|
-
You: "Done, ship it"
|
|
118
|
-
Circuit: → circuit.act (action: ship)
|
|
119
|
-
Marked as shipped. 12 customers notified.
|
|
120
117
|
```
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
> circuit.priorities {weekly: true} # What changed since last week?
|
|
119
|
+
> circuit.priorities # See what matters
|
|
120
|
+
> circuit.spec {priority_id: "..."} # Get the spec
|
|
121
|
+
> circuit.act {action: "build", ...} # Start building
|
|
122
|
+
> ... write the code ...
|
|
123
|
+
> circuit.act {action: "ship", ...} # Ship it.
|
|
124
|
+
> circuit.act {action: "share", channel: "both"} # Share back. Customers notified.
|
|
125
|
+
> circuit.spec {batch: true, status: "shipped"} # Export shipped specs
|
|
126
|
+
```
|
|
123
127
|
|
|
124
128
|
---
|
|
125
129
|
|
|
126
|
-
## Commands
|
|
130
|
+
## CLI Commands
|
|
127
131
|
|
|
128
132
|
```bash
|
|
129
|
-
npx circuit-mcp
|
|
130
|
-
npx circuit-mcp setup
|
|
131
|
-
npx circuit-mcp auth
|
|
132
|
-
npx circuit-mcp logout
|
|
133
|
+
npx circuit-mcp # Start MCP server (stdio mode)
|
|
134
|
+
npx circuit-mcp setup # Interactive setup
|
|
135
|
+
npx circuit-mcp auth # Re-authenticate
|
|
136
|
+
npx circuit-mcp logout # Clear stored token
|
|
137
|
+
npx circuit-mcp fix # Troubleshooting guide
|
|
133
138
|
```
|
|
134
139
|
|
|
135
|
-
|
|
140
|
+
---
|
|
136
141
|
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
npx circuit-mcp auth
|
|
140
|
-
```
|
|
141
|
-
Tokens last 30 days. Re-auth opens your browser — takes 5 seconds.
|
|
142
|
+
## Troubleshooting
|
|
142
143
|
|
|
143
|
-
**
|
|
144
|
-
1. Confirm `~/.cursor/mcp.json` has the config above
|
|
145
|
-
2. Restart Cursor
|
|
146
|
-
3. Check the MCP panel shows "circuit" as connected
|
|
144
|
+
**"Token expired"** — Run `npx circuit-mcp auth` to re-authenticate.
|
|
147
145
|
|
|
148
|
-
**
|
|
149
|
-
```bash
|
|
150
|
-
claude mcp list # Verify circuit is registered
|
|
151
|
-
claude mcp remove circuit # Remove and re-add if needed
|
|
152
|
-
claude mcp add circuit -- npx circuit-mcp
|
|
153
|
-
```
|
|
146
|
+
**"Connection refused"** — Check your internet connection. The MCP connects to Circuit's API.
|
|
154
147
|
|
|
155
|
-
**
|
|
148
|
+
**Tool not showing in Cursor/Claude** — Restart your editor after adding the MCP config.
|
|
156
149
|
|
|
157
|
-
|
|
150
|
+
**Custom API URL** — Set `CIRCUIT_API_URL` environment variable to override the default endpoint.
|
|
158
151
|
|
|
159
152
|
---
|
|
160
153
|
|
|
161
|
-
## How
|
|
154
|
+
## How it works
|
|
162
155
|
|
|
163
156
|
```
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
157
|
+
Your editor (Cursor / Claude Code)
|
|
158
|
+
|
|
|
159
|
+
| JSON-RPC over stdio
|
|
160
|
+
|
|
|
161
|
+
circuit-mcp (this package)
|
|
162
|
+
|
|
|
163
|
+
| HTTPS + Bearer token
|
|
164
|
+
|
|
|
165
|
+
Circuit API
|
|
166
|
+
|
|
|
167
|
+
| PostgreSQL + pgvector
|
|
168
|
+
|
|
|
169
|
+
Your feedback data
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
## Links
|
|
172
|
+
The MCP server runs as a local process, communicating with your editor via stdin/stdout. All data stays in your Circuit account, authenticated via OAuth 2.0.
|
|
180
173
|
|
|
181
|
-
|
|
182
|
-
- [MCP Protocol](https://modelcontextprotocol.io) — Model Context Protocol spec
|
|
183
|
-
|
|
184
|
-
## License
|
|
174
|
+
---
|
|
185
175
|
|
|
186
|
-
|
|
176
|
+
Built by [Circuit](https://withcircuit.com). Feedback in. Specs out.
|
package/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Circuit - Connection Failed</title>
|
|
5
|
+
<style>
|
|
6
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Geist', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
9
|
+
background: #F5F3F0;
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
color: #1C1A18;
|
|
15
|
+
padding: 24px;
|
|
16
|
+
}
|
|
17
|
+
.content {
|
|
18
|
+
text-align: center;
|
|
19
|
+
max-width: 400px;
|
|
20
|
+
}
|
|
21
|
+
.icon {
|
|
22
|
+
margin-bottom: 24px;
|
|
23
|
+
}
|
|
24
|
+
.icon svg {
|
|
25
|
+
width: 48px;
|
|
26
|
+
height: 48px;
|
|
27
|
+
color: #D64545;
|
|
28
|
+
}
|
|
29
|
+
h1 {
|
|
30
|
+
font-size: 24px;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
color: #1C1A18;
|
|
33
|
+
margin-bottom: 12px;
|
|
34
|
+
}
|
|
35
|
+
p {
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
color: rgba(28, 26, 24, 0.6);
|
|
38
|
+
line-height: 1.6;
|
|
39
|
+
}
|
|
40
|
+
.hint {
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
color: rgba(28, 26, 24, 0.6);
|
|
43
|
+
margin-top: 16px;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<div class="content">
|
|
49
|
+
<div class="icon">
|
|
50
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
|
|
51
|
+
</div>
|
|
52
|
+
<h1>Connection Failed</h1>
|
|
53
|
+
<p>Authentication timed out</p>
|
|
54
|
+
<p class="hint">Please try connecting again from your AI assistant.</p>
|
|
55
|
+
</div>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Circuit - Connected</title>
|
|
5
|
+
<style>
|
|
6
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
+
body {
|
|
8
|
+
font-family: 'Geist', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
9
|
+
background: #F5F3F0;
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
color: #1C1A18;
|
|
15
|
+
padding: 24px;
|
|
16
|
+
}
|
|
17
|
+
.content {
|
|
18
|
+
text-align: center;
|
|
19
|
+
max-width: 400px;
|
|
20
|
+
}
|
|
21
|
+
.icon {
|
|
22
|
+
margin-bottom: 24px;
|
|
23
|
+
}
|
|
24
|
+
.icon svg {
|
|
25
|
+
width: 48px;
|
|
26
|
+
height: 48px;
|
|
27
|
+
color: #1C1A18;
|
|
28
|
+
}
|
|
29
|
+
h1 {
|
|
30
|
+
font-size: 24px;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
color: #1C1A18;
|
|
33
|
+
margin-bottom: 12px;
|
|
34
|
+
}
|
|
35
|
+
p {
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
color: rgba(28, 26, 24, 0.6);
|
|
38
|
+
line-height: 1.6;
|
|
39
|
+
}
|
|
40
|
+
.hint {
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
color: rgba(28, 26, 24, 0.6);
|
|
43
|
+
margin-top: 16px;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<div class="content">
|
|
49
|
+
<div class="icon">
|
|
50
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
|
51
|
+
</div>
|
|
52
|
+
<h1>Connected.</h1>
|
|
53
|
+
<p>Your AI coding assistant is now connected to Circuit.</p>
|
|
54
|
+
<p class="hint">This window will close automatically...</p>
|
|
55
|
+
</div>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
package/src/auth.js
CHANGED
|
@@ -8,7 +8,7 @@ import open from 'open';
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { showSpinner, showInfo, showPrompt } from './ui.js';
|
|
10
10
|
|
|
11
|
-
const CIRCUIT_URL = 'https://app.withcircuit.com';
|
|
11
|
+
const CIRCUIT_URL = process.env.CIRCUIT_APP_URL || 'https://app.withcircuit.com';
|
|
12
12
|
const TOKEN_FILE = path.join(os.homedir(), '.circuit', 'token.json');
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -136,79 +136,57 @@ function getSuccessPage() {
|
|
|
136
136
|
return `<!DOCTYPE html>
|
|
137
137
|
<html>
|
|
138
138
|
<head>
|
|
139
|
-
<title>Circuit - Connected
|
|
139
|
+
<title>Circuit - Connected</title>
|
|
140
140
|
<style>
|
|
141
141
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
142
142
|
body {
|
|
143
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
144
|
-
background: #
|
|
143
|
+
font-family: 'Geist', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
144
|
+
background: #F5F3F0;
|
|
145
145
|
min-height: 100vh;
|
|
146
146
|
display: flex;
|
|
147
147
|
align-items: center;
|
|
148
148
|
justify-content: center;
|
|
149
149
|
color: #1C1A18;
|
|
150
|
+
padding: 24px;
|
|
150
151
|
}
|
|
151
|
-
.
|
|
152
|
-
background: white;
|
|
153
|
-
border-radius: 16px;
|
|
154
|
-
box-shadow: 0 4px 24px rgba(0,0,0,0.08);
|
|
155
|
-
padding: 48px 64px;
|
|
152
|
+
.content {
|
|
156
153
|
text-align: center;
|
|
157
|
-
max-width:
|
|
154
|
+
max-width: 400px;
|
|
158
155
|
}
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
align-items: center;
|
|
162
|
-
justify-content: center;
|
|
163
|
-
gap: 8px;
|
|
164
|
-
margin-bottom: 32px;
|
|
165
|
-
}
|
|
166
|
-
.logo-icon {
|
|
167
|
-
width: 28px;
|
|
168
|
-
height: 28px;
|
|
169
|
-
background: #1C1A18;
|
|
170
|
-
border-radius: 50%;
|
|
171
|
-
display: flex;
|
|
172
|
-
align-items: center;
|
|
173
|
-
justify-content: center;
|
|
174
|
-
}
|
|
175
|
-
.logo-icon::after {
|
|
176
|
-
content: '';
|
|
177
|
-
width: 12px;
|
|
178
|
-
height: 12px;
|
|
179
|
-
border: 2px solid white;
|
|
180
|
-
border-radius: 50%;
|
|
156
|
+
.icon {
|
|
157
|
+
margin-bottom: 24px;
|
|
181
158
|
}
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
.woo {
|
|
187
|
-
font-size: 14px;
|
|
188
|
-
color: rgba(28,26,24,0.5);
|
|
189
|
-
margin-bottom: 8px;
|
|
159
|
+
.icon svg {
|
|
160
|
+
width: 48px;
|
|
161
|
+
height: 48px;
|
|
162
|
+
color: #1C1A18;
|
|
190
163
|
}
|
|
191
164
|
h1 {
|
|
192
|
-
font-size:
|
|
165
|
+
font-size: 24px;
|
|
193
166
|
font-weight: 600;
|
|
167
|
+
color: #1C1A18;
|
|
194
168
|
margin-bottom: 12px;
|
|
195
169
|
}
|
|
196
170
|
p {
|
|
197
|
-
font-size:
|
|
198
|
-
color: rgba(28,26,24,0.6);
|
|
199
|
-
line-height: 1.
|
|
171
|
+
font-size: 14px;
|
|
172
|
+
color: rgba(28, 26, 24, 0.6);
|
|
173
|
+
line-height: 1.6;
|
|
174
|
+
}
|
|
175
|
+
.hint {
|
|
176
|
+
font-size: 12px;
|
|
177
|
+
color: rgba(28, 26, 24, 0.6);
|
|
178
|
+
margin-top: 16px;
|
|
200
179
|
}
|
|
201
180
|
</style>
|
|
202
181
|
</head>
|
|
203
182
|
<body>
|
|
204
|
-
<div class="
|
|
205
|
-
<div class="
|
|
206
|
-
<
|
|
207
|
-
<span class="logo-text">Circuit</span>
|
|
183
|
+
<div class="content">
|
|
184
|
+
<div class="icon">
|
|
185
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
|
208
186
|
</div>
|
|
209
|
-
<
|
|
210
|
-
<
|
|
211
|
-
<p>
|
|
187
|
+
<h1>Connected.</h1>
|
|
188
|
+
<p>Your AI coding assistant is now connected to Circuit.</p>
|
|
189
|
+
<p class="hint">This window will close automatically...</p>
|
|
212
190
|
</div>
|
|
213
191
|
<script>setTimeout(() => window.close(), 3000);</script>
|
|
214
192
|
</body>
|
|
@@ -216,32 +194,65 @@ function getSuccessPage() {
|
|
|
216
194
|
}
|
|
217
195
|
|
|
218
196
|
function getErrorPage(error) {
|
|
197
|
+
// Sanitize error message to prevent XSS in the static HTML page
|
|
198
|
+
const safeError = String(error).replace(/[<>&"']/g, c => ({
|
|
199
|
+
'<': '<', '>': '>', '&': '&', '"': '"', "'": '''
|
|
200
|
+
})[c]);
|
|
201
|
+
|
|
219
202
|
return `<!DOCTYPE html>
|
|
220
203
|
<html>
|
|
221
204
|
<head>
|
|
222
|
-
<title>Circuit -
|
|
205
|
+
<title>Circuit - Connection Failed</title>
|
|
223
206
|
<style>
|
|
224
207
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
225
208
|
body {
|
|
226
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
227
|
-
background:
|
|
209
|
+
font-family: 'Geist', -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', Roboto, sans-serif;
|
|
210
|
+
background: #F5F3F0;
|
|
228
211
|
min-height: 100vh;
|
|
229
212
|
display: flex;
|
|
230
213
|
align-items: center;
|
|
231
214
|
justify-content: center;
|
|
232
|
-
color:
|
|
215
|
+
color: #1C1A18;
|
|
216
|
+
padding: 24px;
|
|
217
|
+
}
|
|
218
|
+
.content {
|
|
219
|
+
text-align: center;
|
|
220
|
+
max-width: 400px;
|
|
221
|
+
}
|
|
222
|
+
.icon {
|
|
223
|
+
margin-bottom: 24px;
|
|
224
|
+
}
|
|
225
|
+
.icon svg {
|
|
226
|
+
width: 48px;
|
|
227
|
+
height: 48px;
|
|
228
|
+
color: #D64545;
|
|
229
|
+
}
|
|
230
|
+
h1 {
|
|
231
|
+
font-size: 24px;
|
|
232
|
+
font-weight: 600;
|
|
233
|
+
color: #1C1A18;
|
|
234
|
+
margin-bottom: 12px;
|
|
235
|
+
}
|
|
236
|
+
p {
|
|
237
|
+
font-size: 14px;
|
|
238
|
+
color: rgba(28, 26, 24, 0.6);
|
|
239
|
+
line-height: 1.6;
|
|
240
|
+
}
|
|
241
|
+
.hint {
|
|
242
|
+
font-size: 12px;
|
|
243
|
+
color: rgba(28, 26, 24, 0.6);
|
|
244
|
+
margin-top: 16px;
|
|
233
245
|
}
|
|
234
|
-
.container { text-align: center; padding: 48px; }
|
|
235
|
-
.icon { font-size: 64px; margin-bottom: 24px; }
|
|
236
|
-
h1 { font-size: 32px; color: #ef4444; margin-bottom: 16px; }
|
|
237
|
-
p { font-size: 18px; color: rgba(255,255,255,0.7); }
|
|
238
246
|
</style>
|
|
239
247
|
</head>
|
|
240
248
|
<body>
|
|
241
|
-
<div class="
|
|
242
|
-
<div class="icon"
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
<div class="content">
|
|
250
|
+
<div class="icon">
|
|
251
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
|
|
252
|
+
</div>
|
|
253
|
+
<h1>Connection Failed</h1>
|
|
254
|
+
<p>${safeError}</p>
|
|
255
|
+
<p class="hint">Please try connecting again from your AI assistant.</p>
|
|
245
256
|
</div>
|
|
246
257
|
</body>
|
|
247
258
|
</html>`;
|