@opencoop/opencode-plugin 1.0.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/LICENSE +178 -0
- package/README.fa.md +221 -0
- package/README.md +245 -0
- package/dist/auth/auth-manager.d.ts +45 -0
- package/dist/auth/auth-manager.js +157 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/auth/session-manager.d.ts +14 -0
- package/dist/auth/session-manager.js +50 -0
- package/dist/auth/session-manager.js.map +1 -0
- package/dist/filesystem/change-tracker.d.ts +18 -0
- package/dist/filesystem/change-tracker.js +96 -0
- package/dist/filesystem/change-tracker.js.map +1 -0
- package/dist/filesystem/file-manager.d.ts +39 -0
- package/dist/filesystem/file-manager.js +189 -0
- package/dist/filesystem/file-manager.js.map +1 -0
- package/dist/filesystem/lock-manager.d.ts +15 -0
- package/dist/filesystem/lock-manager.js +120 -0
- package/dist/filesystem/lock-manager.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/server/mcp-server.d.ts +20 -0
- package/dist/server/mcp-server.js +392 -0
- package/dist/server/mcp-server.js.map +1 -0
- package/dist/types/index.d.ts +80 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config.d.ts +3 -0
- package/dist/utils/config.js +38 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/database.d.ts +12 -0
- package/dist/utils/database.js +110 -0
- package/dist/utils/database.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/web/public/assets/app.js +446 -0
- package/dist/web/public/assets/style.css +639 -0
- package/dist/web/public/index.html +190 -0
- package/dist/web/public/invite.html +132 -0
- package/dist/web/server.d.ts +3 -0
- package/dist/web/server.js +150 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>OpenCOOP - Team Collaboration</title>
|
|
7
|
+
<link rel="stylesheet" href="/assets/style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="app">
|
|
11
|
+
<nav class="sidebar">
|
|
12
|
+
<div class="logo">
|
|
13
|
+
<h1>OpenCOOP</h1>
|
|
14
|
+
<span>v1.0.0</span>
|
|
15
|
+
</div>
|
|
16
|
+
<ul class="nav-links">
|
|
17
|
+
<li><a href="#" class="active" data-page="config">Config</a></li>
|
|
18
|
+
<li><a href="#" data-page="dashboard">Dashboard</a></li>
|
|
19
|
+
<li><a href="#" data-page="changes">Changes</a></li>
|
|
20
|
+
<li><a href="#" data-page="team">Team</a></li>
|
|
21
|
+
<li><a href="#" data-page="locks">Locks</a></li>
|
|
22
|
+
</ul>
|
|
23
|
+
<div class="nav-status">
|
|
24
|
+
<div class="status-dot" id="statusDot"></div>
|
|
25
|
+
<span id="statusText">Checking...</span>
|
|
26
|
+
</div>
|
|
27
|
+
</nav>
|
|
28
|
+
|
|
29
|
+
<main class="content">
|
|
30
|
+
<!-- CONFIG PAGE -->
|
|
31
|
+
<section id="page-config" class="page active">
|
|
32
|
+
<h2>Configuration</h2>
|
|
33
|
+
|
|
34
|
+
<div class="mode-selection">
|
|
35
|
+
<div class="mode-card" data-mode="host" onclick="selectMode('host')">
|
|
36
|
+
<div class="mode-icon">HOST</div>
|
|
37
|
+
<h3>HOST Mode</h3>
|
|
38
|
+
<p>Share your project folder with team members</p>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="mode-card" data-mode="remote" onclick="selectMode('remote')">
|
|
41
|
+
<div class="mode-icon">REMOTE</div>
|
|
42
|
+
<h3>REMOTE Mode</h3>
|
|
43
|
+
<p>Connect to a host's shared project</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div id="host-config" class="config-section hidden">
|
|
48
|
+
<h3>HOST Settings</h3>
|
|
49
|
+
<div class="form-group">
|
|
50
|
+
<label>Project Folder Path:</label>
|
|
51
|
+
<div class="input-row">
|
|
52
|
+
<input type="text" id="workspace-path" placeholder="/path/to/your/project">
|
|
53
|
+
<button onclick="selectFolder()" class="btn-secondary">Browse</button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="form-group">
|
|
57
|
+
<label>Invite Link:</label>
|
|
58
|
+
<div class="input-row">
|
|
59
|
+
<input type="text" id="invite-link" readonly placeholder="Click Generate to create link">
|
|
60
|
+
<button onclick="copyInviteLink()" class="btn-secondary">Copy</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<button onclick="generateInvite()" class="btn-primary">Generate Invite Link</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div id="remote-config" class="config-section hidden">
|
|
67
|
+
<h3>REMOTE Settings</h3>
|
|
68
|
+
<div class="form-group">
|
|
69
|
+
<label>Host Invite Link:</label>
|
|
70
|
+
<input type="text" id="host-url" placeholder="http://host:31313/invite/TOKEN">
|
|
71
|
+
</div>
|
|
72
|
+
<button onclick="connectToHost()" class="btn-primary">Connect to Host</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<button onclick="saveConfig()" class="btn-primary">Save Configuration</button>
|
|
76
|
+
</section>
|
|
77
|
+
|
|
78
|
+
<!-- DASHBOARD PAGE -->
|
|
79
|
+
<section id="page-dashboard" class="page">
|
|
80
|
+
<h2>Dashboard</h2>
|
|
81
|
+
<div class="stats-grid">
|
|
82
|
+
<div class="stat-card">
|
|
83
|
+
<div class="stat-value" id="stat-total-changes">0</div>
|
|
84
|
+
<div class="stat-label">Total Changes</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="stat-card">
|
|
87
|
+
<div class="stat-value" id="stat-online-users">0</div>
|
|
88
|
+
<div class="stat-label">Online Users</div>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="stat-card">
|
|
91
|
+
<div class="stat-value" id="stat-active-locks">0</div>
|
|
92
|
+
<div class="stat-label">Active Locks</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="stat-card">
|
|
95
|
+
<div class="stat-value" id="stat-team-members">0</div>
|
|
96
|
+
<div class="stat-label">Team Members</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="dashboard-grid">
|
|
101
|
+
<div class="card">
|
|
102
|
+
<h3>Recent Activity</h3>
|
|
103
|
+
<div id="recent-activity" class="activity-list"></div>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="card">
|
|
106
|
+
<h3>Online Users</h3>
|
|
107
|
+
<div id="online-users" class="user-list"></div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="card">
|
|
110
|
+
<h3>Changes by User</h3>
|
|
111
|
+
<div id="changes-by-user" class="chart-container"></div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="card">
|
|
114
|
+
<h3>Active Locks</h3>
|
|
115
|
+
<div id="active-locks" class="lock-list"></div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</section>
|
|
119
|
+
|
|
120
|
+
<!-- CHANGES PAGE -->
|
|
121
|
+
<section id="page-changes" class="page">
|
|
122
|
+
<h2>Change History</h2>
|
|
123
|
+
<div class="filters">
|
|
124
|
+
<input type="text" id="filter-file" placeholder="Filter by file...">
|
|
125
|
+
<input type="text" id="filter-user" placeholder="Filter by user...">
|
|
126
|
+
<select id="filter-action">
|
|
127
|
+
<option value="">All Actions</option>
|
|
128
|
+
<option value="read">Read</option>
|
|
129
|
+
<option value="create">Create</option>
|
|
130
|
+
<option value="update">Update</option>
|
|
131
|
+
<option value="delete">Delete</option>
|
|
132
|
+
<option value="move">Move</option>
|
|
133
|
+
</select>
|
|
134
|
+
<button onclick="loadChanges()" class="btn-secondary">Apply</button>
|
|
135
|
+
</div>
|
|
136
|
+
<div id="changes-list" class="changes-list"></div>
|
|
137
|
+
</section>
|
|
138
|
+
|
|
139
|
+
<!-- TEAM PAGE -->
|
|
140
|
+
<section id="page-team" class="page">
|
|
141
|
+
<h2>Team Management</h2>
|
|
142
|
+
<div class="card">
|
|
143
|
+
<h3>Invite New Member</h3>
|
|
144
|
+
<div class="form-row">
|
|
145
|
+
<input type="email" id="invite-email" placeholder="Email address">
|
|
146
|
+
<select id="invite-permissions">
|
|
147
|
+
<option value="read,write">Read + Write</option>
|
|
148
|
+
<option value="read">Read Only</option>
|
|
149
|
+
<option value="read,write,admin">Admin</option>
|
|
150
|
+
</select>
|
|
151
|
+
<input type="number" id="invite-days" placeholder="Days" value="7" min="1" max="365">
|
|
152
|
+
<button onclick="createInvite()" class="btn-primary">Generate Invite</button>
|
|
153
|
+
</div>
|
|
154
|
+
<div id="invite-result" class="invite-result hidden">
|
|
155
|
+
<label>Invite Link:</label>
|
|
156
|
+
<div class="input-row">
|
|
157
|
+
<input type="text" id="generated-invite" readonly>
|
|
158
|
+
<button onclick="copyGeneratedInvite()" class="btn-secondary">Copy</button>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div class="card">
|
|
164
|
+
<h3>Team Members</h3>
|
|
165
|
+
<div id="team-members" class="member-list"></div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="card">
|
|
169
|
+
<h3>Online Users</h3>
|
|
170
|
+
<div id="team-online" class="user-list"></div>
|
|
171
|
+
</div>
|
|
172
|
+
</section>
|
|
173
|
+
|
|
174
|
+
<!-- LOCKS PAGE -->
|
|
175
|
+
<section id="page-locks" class="page">
|
|
176
|
+
<h2>File Locks</h2>
|
|
177
|
+
<div class="card">
|
|
178
|
+
<div class="card-header">
|
|
179
|
+
<h3>Active Locks</h3>
|
|
180
|
+
<button onclick="loadLocks()" class="btn-secondary">Refresh</button>
|
|
181
|
+
</div>
|
|
182
|
+
<div id="locks-list" class="locks-table"></div>
|
|
183
|
+
</div>
|
|
184
|
+
</section>
|
|
185
|
+
</main>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<script src="/assets/app.js"></script>
|
|
189
|
+
</body>
|
|
190
|
+
</html>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>OpenCOOP - Accept Invite</title>
|
|
7
|
+
<link rel="stylesheet" href="/assets/style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="invite-page">
|
|
11
|
+
<div class="invite-card">
|
|
12
|
+
<h1>OpenCOOP</h1>
|
|
13
|
+
<h2>Accept Team Invite</h2>
|
|
14
|
+
|
|
15
|
+
<div id="invite-status" class="invite-status">
|
|
16
|
+
<div class="spinner"></div>
|
|
17
|
+
<p>Validating invite link...</p>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div id="invite-form" class="invite-form hidden">
|
|
21
|
+
<div class="invite-info">
|
|
22
|
+
<p><strong>Workspace:</strong> <span id="invite-workspace">-</span></p>
|
|
23
|
+
<p><strong>Permissions:</strong> <span id="invite-permissions">-</span></p>
|
|
24
|
+
<p><strong>Expires:</strong> <span id="invite-expires">-</span></p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="form-group">
|
|
28
|
+
<label>Your Name:</label>
|
|
29
|
+
<input type="text" id="user-name" placeholder="Enter your name">
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="form-group">
|
|
33
|
+
<label>Your Email:</label>
|
|
34
|
+
<input type="email" id="user-email" placeholder="Enter your email">
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<button onclick="acceptInvite()" class="btn-primary">Join Team</button>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div id="invite-success" class="invite-success hidden">
|
|
41
|
+
<div class="success-icon">SUCCESS</div>
|
|
42
|
+
<h3>Welcome to the Team!</h3>
|
|
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>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div id="invite-error" class="invite-error hidden">
|
|
53
|
+
<div class="error-icon">ERROR</div>
|
|
54
|
+
<h3>Invalid Invite</h3>
|
|
55
|
+
<p id="error-message">This invite link is invalid or has expired.</p>
|
|
56
|
+
<a href="/" class="btn-primary">Go to Dashboard</a>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<script>
|
|
62
|
+
const token = window.location.pathname.split('/').pop();
|
|
63
|
+
|
|
64
|
+
async function validateInvite() {
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(`/api/invite/validate/${token}`);
|
|
67
|
+
const data = await res.json();
|
|
68
|
+
|
|
69
|
+
if (data.valid) {
|
|
70
|
+
document.getElementById('invite-status').classList.add('hidden');
|
|
71
|
+
document.getElementById('invite-form').classList.remove('hidden');
|
|
72
|
+
document.getElementById('invite-workspace').textContent = data.workspaceId || 'Shared Project';
|
|
73
|
+
document.getElementById('invite-permissions').textContent = data.permissions?.join(', ') || 'read, write';
|
|
74
|
+
} else {
|
|
75
|
+
showError(data.reason || 'Invalid invite link');
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
showError('Failed to validate invite');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function showError(msg) {
|
|
83
|
+
document.getElementById('invite-status').classList.add('hidden');
|
|
84
|
+
document.getElementById('invite-error').classList.remove('hidden');
|
|
85
|
+
document.getElementById('error-message').textContent = msg;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function acceptInvite() {
|
|
89
|
+
const name = document.getElementById('user-name').value;
|
|
90
|
+
const email = document.getElementById('user-email').value;
|
|
91
|
+
|
|
92
|
+
if (!name || !email) {
|
|
93
|
+
alert('Please fill in all fields');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch('/api/config');
|
|
99
|
+
const config = await res.json();
|
|
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!');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
validateInvite();
|
|
130
|
+
</script>
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { saveConfig } from "../utils/config.js";
|
|
5
|
+
import { AuthManager } from "../auth/auth-manager.js";
|
|
6
|
+
import { ChangeTracker } from "../filesystem/change-tracker.js";
|
|
7
|
+
import { SessionManager } from "../auth/session-manager.js";
|
|
8
|
+
import { LockManager } from "../filesystem/lock-manager.js";
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
export async function createWebUI(config) {
|
|
11
|
+
const app = express();
|
|
12
|
+
app.use(express.json());
|
|
13
|
+
app.use(express.static(path.join(__dirname, "public")));
|
|
14
|
+
const authManager = new AuthManager(config.jwtSecret);
|
|
15
|
+
const changeTracker = new ChangeTracker();
|
|
16
|
+
const sessionManager = new SessionManager();
|
|
17
|
+
const lockManager = new LockManager();
|
|
18
|
+
// ==================== CONFIG API ====================
|
|
19
|
+
app.get("/api/config", (req, res) => {
|
|
20
|
+
res.json({
|
|
21
|
+
mode: config.mode,
|
|
22
|
+
workspacePath: config.workspacePath,
|
|
23
|
+
hostUrl: config.hostUrl,
|
|
24
|
+
port: config.port,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
app.post("/api/config", async (req, res) => {
|
|
28
|
+
try {
|
|
29
|
+
const { mode, workspacePath, hostUrl } = req.body;
|
|
30
|
+
if (mode)
|
|
31
|
+
config.mode = mode;
|
|
32
|
+
if (workspacePath)
|
|
33
|
+
config.workspacePath = workspacePath;
|
|
34
|
+
if (hostUrl !== undefined)
|
|
35
|
+
config.hostUrl = hostUrl;
|
|
36
|
+
await saveConfig(config);
|
|
37
|
+
res.json({ success: true, config });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
res.status(500).json({ success: false, error: "Failed to save config" });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// ==================== CHANGES API ====================
|
|
44
|
+
app.get("/api/changes", async (req, res) => {
|
|
45
|
+
try {
|
|
46
|
+
const { file_path, user_id, limit, offset } = req.query;
|
|
47
|
+
const changes = await changeTracker.getChanges({
|
|
48
|
+
workspaceId: config.workspacePath,
|
|
49
|
+
filePath: file_path,
|
|
50
|
+
userId: user_id,
|
|
51
|
+
limit: limit ? parseInt(limit) : 50,
|
|
52
|
+
offset: offset ? parseInt(offset) : 0,
|
|
53
|
+
});
|
|
54
|
+
res.json({ changes, total: changes.length });
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
res.status(500).json({ error: "Failed to fetch changes" });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// ==================== STATS API ====================
|
|
61
|
+
app.get("/api/stats", async (req, res) => {
|
|
62
|
+
try {
|
|
63
|
+
const stats = await changeTracker.getStats(config.workspacePath);
|
|
64
|
+
const locks = await lockManager.getActiveLocks(config.workspacePath);
|
|
65
|
+
const online = await sessionManager.getOnlineUsers(config.workspacePath);
|
|
66
|
+
res.json({
|
|
67
|
+
...stats,
|
|
68
|
+
activeLocks: locks.length,
|
|
69
|
+
onlineUsers: online.length,
|
|
70
|
+
locks,
|
|
71
|
+
online,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
res.status(500).json({ error: "Failed to fetch stats" });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// ==================== TEAM API ====================
|
|
79
|
+
app.get("/api/team", async (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
const members = await authManager.getTeamMembers(config.workspacePath);
|
|
82
|
+
const online = await sessionManager.getOnlineUsers(config.workspacePath);
|
|
83
|
+
res.json({ members, online });
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
res.status(500).json({ error: "Failed to fetch team" });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// ==================== INVITE API ====================
|
|
90
|
+
app.post("/api/invite", async (req, res) => {
|
|
91
|
+
try {
|
|
92
|
+
const { email, permissions, expires_in_days } = req.body;
|
|
93
|
+
const ownerId = "owner-web";
|
|
94
|
+
const link = await authManager.generateInviteLink({
|
|
95
|
+
workspaceId: config.workspacePath,
|
|
96
|
+
email: email || "team@opencoop.local",
|
|
97
|
+
permissions: permissions || ["read", "write"],
|
|
98
|
+
expiresInDays: expires_in_days || 7,
|
|
99
|
+
createdBy: ownerId,
|
|
100
|
+
});
|
|
101
|
+
res.json({ success: true, invite: link });
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
res.status(500).json({ error: "Failed to create invite" });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
app.get("/api/invite/validate/:token", async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const result = await authManager.validateInviteLink(req.params.token);
|
|
110
|
+
res.json(result);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
res.status(500).json({ error: "Failed to validate invite" });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// ==================== LOCKS API ====================
|
|
117
|
+
app.get("/api/locks", async (req, res) => {
|
|
118
|
+
try {
|
|
119
|
+
const locks = await lockManager.getActiveLocks(config.workspacePath);
|
|
120
|
+
res.json({ locks });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
res.status(500).json({ error: "Failed to fetch locks" });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// ==================== STATUS API ====================
|
|
127
|
+
app.get("/api/status", (req, res) => {
|
|
128
|
+
res.json({
|
|
129
|
+
status: "running",
|
|
130
|
+
mode: config.mode,
|
|
131
|
+
workspace: config.workspacePath,
|
|
132
|
+
port: config.port,
|
|
133
|
+
version: "1.0.0",
|
|
134
|
+
uptime: process.uptime(),
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
app.get("/api/health", (req, res) => {
|
|
138
|
+
res.json({ status: "ok", timestamp: new Date().toISOString() });
|
|
139
|
+
});
|
|
140
|
+
// ==================== INVITE ACCEPT PAGE ====================
|
|
141
|
+
app.get("/invite/:token", (req, res) => {
|
|
142
|
+
res.sendFile(path.join(__dirname, "public", "invite.html"));
|
|
143
|
+
});
|
|
144
|
+
// ==================== SPA FALLBACK ====================
|
|
145
|
+
app.get("*", (req, res) => {
|
|
146
|
+
res.sendFile(path.join(__dirname, "public", "index.html"));
|
|
147
|
+
});
|
|
148
|
+
return app;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAG5D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAoB;IACpD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAC1C,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC5C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IAEtC,uDAAuD;IACvD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAClD,IAAI,IAAI;gBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;YAC7B,IAAI,aAAa;gBAAE,MAAM,CAAC,aAAa,GAAG,aAAa,CAAC;YACxD,IAAI,OAAO,KAAK,SAAS;gBAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YACpD,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,wDAAwD;IACxD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC;YACxD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC;gBAC7C,WAAW,EAAE,MAAM,CAAC,aAAa;gBACjC,QAAQ,EAAE,SAA+B;gBACzC,MAAM,EAAE,OAA6B;gBACrC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAe,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC7C,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;aAChD,CAAC,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sDAAsD;IACtD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACjE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACrE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAEzE,GAAG,CAAC,IAAI,CAAC;gBACP,GAAG,KAAK;gBACR,WAAW,EAAE,KAAK,CAAC,MAAM;gBACzB,WAAW,EAAE,MAAM,CAAC,MAAM;gBAC1B,KAAK;gBACL,MAAM;aACP,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,qDAAqD;IACrD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACvE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACzE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YACzD,MAAM,OAAO,GAAG,WAAW,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,kBAAkB,CAAC;gBAChD,WAAW,EAAE,MAAM,CAAC,aAAa;gBACjC,KAAK,EAAE,KAAK,IAAI,qBAAqB;gBACrC,WAAW,EAAE,WAAW,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;gBAC7C,aAAa,EAAE,eAAe,IAAI,CAAC;gBACnC,SAAS,EAAE,OAAO;aACnB,CAAC,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,6BAA6B,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACxD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sDAAsD;IACtD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACrE,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClC,GAAG,CAAC,IAAI,CAAC;YACP,MAAM,EAAE,SAAS;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,MAAM,CAAC,aAAa;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,+DAA+D;IAC/D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACrC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,yDAAyD;IACzD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACxB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opencoop/opencode-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenCode plugin for team collaboration via shared MCP server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx watch src/index.ts",
|
|
11
|
+
"build": "tsc --skipLibCheck && cp -r src/web/public dist/web/",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": ["mcp", "opencode", "plugin", "collaboration", "shared", "team"],
|
|
15
|
+
"author": "Hamid Pajand",
|
|
16
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@opencode-ai/plugin": "^1.17.20",
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
20
|
+
"zod": "^3.24.0",
|
|
21
|
+
"express": "^4.21.0",
|
|
22
|
+
"cors": "^2.8.5",
|
|
23
|
+
"helmet": "^8.0.0",
|
|
24
|
+
"sql.js": "^1.11.0",
|
|
25
|
+
"bcryptjs": "^2.4.3",
|
|
26
|
+
"jsonwebtoken": "^9.0.2",
|
|
27
|
+
"nanoid": "^5.0.9",
|
|
28
|
+
"uuid": "^11.1.0",
|
|
29
|
+
"pino": "^9.6.0",
|
|
30
|
+
"pino-pretty": "^13.0.0",
|
|
31
|
+
"dotenv": "^16.4.7",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
33
|
+
"diff": "^7.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.13.0",
|
|
37
|
+
"@types/express": "^5.0.0",
|
|
38
|
+
"@types/cors": "^2.8.17",
|
|
39
|
+
"@types/bcryptjs": "^2.4.6",
|
|
40
|
+
"@types/jsonwebtoken": "^9.0.7",
|
|
41
|
+
"@types/uuid": "^10.0.0",
|
|
42
|
+
"@types/diff": "^6.0.0",
|
|
43
|
+
"typescript": "^5.7.0",
|
|
44
|
+
"tsx": "^4.19.0",
|
|
45
|
+
"vitest": "^3.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|