@opencoop/opencode-plugin 1.0.0 → 1.1.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/dist/auth/auth-manager.js +11 -0
- package/dist/auth/auth-manager.js.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/server/mcp-server.js +28 -13
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/utils/config.js +2 -4
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/logger.js +9 -8
- package/dist/utils/logger.js.map +1 -1
- package/dist/web/public/assets/app.js +460 -296
- package/dist/web/public/assets/style.css +1175 -339
- package/dist/web/public/index.html +397 -144
- package/dist/web/public/invite.html +258 -87
- package/package.json +1 -1
|
@@ -1,129 +1,300 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>OpenCOOP -
|
|
6
|
+
<title>OpenCOOP - Join Team</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
7
10
|
<link rel="stylesheet" href="/assets/style.css">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
<style>
|
|
12
|
+
body {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
overflow: auto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.invite-container {
|
|
21
|
+
width: 100%;
|
|
22
|
+
max-width: 440px;
|
|
23
|
+
padding: 40px;
|
|
24
|
+
background: var(--bg-secondary);
|
|
25
|
+
border: 1px solid var(--border-color);
|
|
26
|
+
border-radius: var(--radius-xl);
|
|
27
|
+
box-shadow: var(--shadow-lg);
|
|
28
|
+
animation: slideUp 0.6s ease forwards;
|
|
29
|
+
opacity: 0;
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.invite-logo {
|
|
34
|
+
width: 72px;
|
|
35
|
+
height: 72px;
|
|
36
|
+
margin: 0 auto 24px;
|
|
37
|
+
border-radius: var(--radius-lg);
|
|
38
|
+
background: var(--gradient-primary);
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
color: white;
|
|
43
|
+
font-size: 1.5rem;
|
|
44
|
+
font-weight: 800;
|
|
45
|
+
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.3);
|
|
46
|
+
animation: bounce 2s ease-in-out infinite;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.invite-title {
|
|
50
|
+
font-size: 1.6rem;
|
|
51
|
+
font-weight: 700;
|
|
52
|
+
margin-bottom: 8px;
|
|
53
|
+
background: var(--gradient-primary);
|
|
54
|
+
-webkit-background-clip: text;
|
|
55
|
+
-webkit-text-fill-color: transparent;
|
|
56
|
+
background-clip: text;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.invite-subtitle {
|
|
60
|
+
color: var(--text-secondary);
|
|
61
|
+
margin-bottom: 32px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.invite-status {
|
|
65
|
+
padding: 16px;
|
|
66
|
+
border-radius: var(--radius-md);
|
|
67
|
+
margin-bottom: 24px;
|
|
68
|
+
animation: fadeIn 0.4s ease forwards;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.invite-status.valid {
|
|
72
|
+
background: rgba(16, 185, 129, 0.1);
|
|
73
|
+
border: 1px solid rgba(16, 185, 129, 0.3);
|
|
74
|
+
color: var(--accent-success);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.invite-status.invalid {
|
|
78
|
+
background: rgba(239, 68, 68, 0.1);
|
|
79
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
80
|
+
color: var(--accent-danger);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.invite-status.loading {
|
|
84
|
+
background: rgba(139, 92, 246, 0.1);
|
|
85
|
+
border: 1px solid rgba(139, 92, 246, 0.3);
|
|
86
|
+
color: var(--accent-primary);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.invite-form {
|
|
90
|
+
text-align: left;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.invite-form .form-group {
|
|
94
|
+
margin-bottom: 20px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.invite-form label {
|
|
98
|
+
display: block;
|
|
99
|
+
margin-bottom: 8px;
|
|
100
|
+
font-weight: 500;
|
|
101
|
+
color: var(--text-secondary);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.invite-form input {
|
|
105
|
+
width: 100%;
|
|
106
|
+
padding: 14px 16px;
|
|
107
|
+
background: var(--bg-tertiary);
|
|
108
|
+
border: 1px solid var(--border-color);
|
|
109
|
+
border-radius: var(--radius-md);
|
|
110
|
+
color: var(--text-primary);
|
|
111
|
+
outline: none;
|
|
112
|
+
transition: all var(--transition-fast);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.invite-form input:focus {
|
|
116
|
+
border-color: var(--border-focus);
|
|
117
|
+
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.invite-form input::placeholder {
|
|
121
|
+
color: var(--text-tertiary);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.invite-btn {
|
|
125
|
+
width: 100%;
|
|
126
|
+
padding: 14px;
|
|
127
|
+
background: var(--gradient-primary);
|
|
128
|
+
color: white;
|
|
129
|
+
border: none;
|
|
130
|
+
border-radius: var(--radius-md);
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
font-size: 1rem;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
transition: all var(--transition-fast);
|
|
135
|
+
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
|
|
136
|
+
}
|
|
19
137
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
138
|
+
.invite-btn:hover {
|
|
139
|
+
transform: translateY(-2px);
|
|
140
|
+
box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.invite-btn:disabled {
|
|
144
|
+
opacity: 0.6;
|
|
145
|
+
cursor: not-allowed;
|
|
146
|
+
transform: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.invite-permissions {
|
|
150
|
+
display: flex;
|
|
151
|
+
gap: 8px;
|
|
152
|
+
margin-bottom: 20px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.perm-option {
|
|
156
|
+
flex: 1;
|
|
157
|
+
padding: 12px;
|
|
158
|
+
background: var(--bg-tertiary);
|
|
159
|
+
border: 1px solid var(--border-color);
|
|
160
|
+
border-radius: var(--radius-md);
|
|
161
|
+
text-align: center;
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
transition: all var(--transition-fast);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.perm-option:hover {
|
|
167
|
+
border-color: var(--accent-primary);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.perm-option.selected {
|
|
171
|
+
background: rgba(139, 92, 246, 0.15);
|
|
172
|
+
border-color: var(--accent-primary);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.perm-option input {
|
|
176
|
+
display: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.perm-label {
|
|
180
|
+
font-weight: 500;
|
|
181
|
+
font-size: 0.9rem;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.perm-desc {
|
|
185
|
+
font-size: 0.75rem;
|
|
186
|
+
color: var(--text-tertiary);
|
|
187
|
+
margin-top: 4px;
|
|
188
|
+
}
|
|
26
189
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
190
|
+
@keyframes fadeIn {
|
|
191
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
192
|
+
to { opacity: 1; transform: translateY(0); }
|
|
193
|
+
}
|
|
31
194
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
195
|
+
.spinner {
|
|
196
|
+
display: inline-block;
|
|
197
|
+
width: 20px;
|
|
198
|
+
height: 20px;
|
|
199
|
+
border: 2px solid rgba(255,255,255,0.3);
|
|
200
|
+
border-radius: 50%;
|
|
201
|
+
border-top-color: white;
|
|
202
|
+
animation: spin 0.8s linear infinite;
|
|
203
|
+
vertical-align: middle;
|
|
204
|
+
margin-right: 8px;
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
207
|
+
</head>
|
|
208
|
+
<body>
|
|
209
|
+
<div class="invite-container">
|
|
210
|
+
<div class="invite-logo">OC</div>
|
|
211
|
+
<h1 class="invite-title">Join the Team</h1>
|
|
212
|
+
<p class="invite-subtitle">You've been invited to collaborate on OpenCOOP</p>
|
|
36
213
|
|
|
37
|
-
|
|
214
|
+
<div id="invite-status" class="invite-status loading">
|
|
215
|
+
<span class="spinner"></span> Validating invite link...
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div id="invite-form" class="invite-form" style="display: none;">
|
|
219
|
+
<div class="form-group">
|
|
220
|
+
<label>Your Name</label>
|
|
221
|
+
<input type="text" id="user-name" placeholder="Enter your name">
|
|
38
222
|
</div>
|
|
39
223
|
|
|
40
|
-
<div
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
<p>You have been added to the workspace.</p>
|
|
44
|
-
<p>Copy this MCP config and add it to your <code>opencode.json</code>:</p>
|
|
45
|
-
<div class="config-code">
|
|
46
|
-
<pre id="mcp-config"></pre>
|
|
47
|
-
<button onclick="copyMcpConfig()" class="btn-secondary">Copy Config</button>
|
|
48
|
-
</div>
|
|
49
|
-
<a href="/" class="btn-primary">Go to Dashboard</a>
|
|
224
|
+
<div class="form-group">
|
|
225
|
+
<label>Your Email</label>
|
|
226
|
+
<input type="email" id="user-email" placeholder="Enter your email">
|
|
50
227
|
</div>
|
|
51
228
|
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
229
|
+
<label style="display: block; margin-bottom: 12px; font-weight: 500; color: var(--text-secondary);">Permission Level</label>
|
|
230
|
+
<div class="invite-permissions">
|
|
231
|
+
<label class="perm-option selected" onclick="selectPerm(this, 'read')">
|
|
232
|
+
<input type="radio" name="perm" value="read" checked>
|
|
233
|
+
<div class="perm-label">Read Only</div>
|
|
234
|
+
<div class="perm-desc">View files</div>
|
|
235
|
+
</label>
|
|
236
|
+
<label class="perm-option" onclick="selectPerm(this, 'read,write')">
|
|
237
|
+
<input type="radio" name="perm" value="read,write">
|
|
238
|
+
<div class="perm-label">Read + Write</div>
|
|
239
|
+
<div class="perm-desc">Edit files</div>
|
|
240
|
+
</label>
|
|
57
241
|
</div>
|
|
242
|
+
|
|
243
|
+
<button class="invite-btn" onclick="acceptInvite()">Join Team</button>
|
|
58
244
|
</div>
|
|
245
|
+
|
|
246
|
+
<div id="invite-error" class="invite-status invalid" style="display: none;"></div>
|
|
59
247
|
</div>
|
|
60
248
|
|
|
61
249
|
<script>
|
|
62
|
-
const token = window.location.pathname.split('/')
|
|
250
|
+
const token = window.location.pathname.split('/invite/')[1];
|
|
251
|
+
let selectedPerms = ['read'];
|
|
252
|
+
|
|
253
|
+
function selectPerm(el, perms) {
|
|
254
|
+
document.querySelectorAll('.perm-option').forEach(p => p.classList.remove('selected'));
|
|
255
|
+
el.classList.add('selected');
|
|
256
|
+
selectedPerms = perms.split(',');
|
|
257
|
+
}
|
|
63
258
|
|
|
64
259
|
async function validateInvite() {
|
|
260
|
+
if (!token) {
|
|
261
|
+
showInvalid('No invite token provided');
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
65
265
|
try {
|
|
66
266
|
const res = await fetch(`/api/invite/validate/${token}`);
|
|
67
267
|
const data = await res.json();
|
|
68
268
|
|
|
69
269
|
if (data.valid) {
|
|
70
|
-
document.getElementById('invite-status').
|
|
71
|
-
document.getElementById('invite-form').
|
|
72
|
-
document.getElementById('invite-workspace').textContent = data.workspaceId || 'Shared Project';
|
|
73
|
-
document.getElementById('invite-permissions').textContent = data.permissions?.join(', ') || 'read, write';
|
|
270
|
+
document.getElementById('invite-status').style.display = 'none';
|
|
271
|
+
document.getElementById('invite-form').style.display = 'block';
|
|
74
272
|
} else {
|
|
75
|
-
|
|
273
|
+
showInvalid(data.reason || 'This invite link is invalid');
|
|
76
274
|
}
|
|
77
|
-
} catch
|
|
78
|
-
|
|
275
|
+
} catch {
|
|
276
|
+
showInvalid('Failed to validate invite link');
|
|
79
277
|
}
|
|
80
278
|
}
|
|
81
279
|
|
|
82
|
-
function
|
|
83
|
-
document.getElementById('invite-status')
|
|
84
|
-
|
|
85
|
-
|
|
280
|
+
function showInvalid(msg) {
|
|
281
|
+
const el = document.getElementById('invite-status');
|
|
282
|
+
el.className = 'invite-status invalid';
|
|
283
|
+
el.innerHTML = msg;
|
|
86
284
|
}
|
|
87
285
|
|
|
88
286
|
async function acceptInvite() {
|
|
89
|
-
const name = document.getElementById('user-name').value;
|
|
90
|
-
const email = document.getElementById('user-email').value;
|
|
287
|
+
const name = document.getElementById('user-name').value.trim();
|
|
288
|
+
const email = document.getElementById('user-email').value.trim();
|
|
91
289
|
|
|
92
290
|
if (!name || !email) {
|
|
93
291
|
alert('Please fill in all fields');
|
|
94
292
|
return;
|
|
95
293
|
}
|
|
96
294
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const mcpConfig = {
|
|
102
|
-
mcp: {
|
|
103
|
-
opencoop: {
|
|
104
|
-
type: "remote",
|
|
105
|
-
url: `http://localhost:31313/mcp`,
|
|
106
|
-
enabled: true,
|
|
107
|
-
headers: {
|
|
108
|
-
"Authorization": `Bearer ${token}`
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
document.getElementById('mcp-config').textContent = JSON.stringify(mcpConfig, null, 2);
|
|
115
|
-
|
|
116
|
-
document.getElementById('invite-form').classList.add('hidden');
|
|
117
|
-
document.getElementById('invite-success').classList.remove('hidden');
|
|
118
|
-
} catch (e) {
|
|
119
|
-
alert('Error accepting invite');
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function copyMcpConfig() {
|
|
124
|
-
const config = document.getElementById('mcp-config').textContent;
|
|
125
|
-
navigator.clipboard.writeText(config);
|
|
126
|
-
alert('Config copied!');
|
|
295
|
+
// In a real implementation, this would register the user
|
|
296
|
+
alert(`Welcome ${name}! You've been added to the team with ${selectedPerms.join(', ')} permissions.`);
|
|
297
|
+
window.location.href = '/';
|
|
127
298
|
}
|
|
128
299
|
|
|
129
300
|
validateInvite();
|