@vibescope/mcp-server 0.0.1 → 0.2.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 +113 -98
- package/dist/api-client.d.ts +1169 -0
- package/dist/api-client.js +713 -0
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +39 -240
- package/dist/config/tool-categories.d.ts +31 -0
- package/dist/config/tool-categories.js +253 -0
- package/dist/handlers/blockers.js +57 -58
- package/dist/handlers/bodies-of-work.d.ts +2 -0
- package/dist/handlers/bodies-of-work.js +108 -477
- package/dist/handlers/cost.d.ts +1 -0
- package/dist/handlers/cost.js +35 -113
- package/dist/handlers/decisions.d.ts +2 -0
- package/dist/handlers/decisions.js +28 -27
- package/dist/handlers/deployment.js +113 -828
- package/dist/handlers/discovery.d.ts +3 -0
- package/dist/handlers/discovery.js +26 -627
- package/dist/handlers/fallback.d.ts +2 -0
- package/dist/handlers/fallback.js +56 -142
- package/dist/handlers/findings.d.ts +8 -1
- package/dist/handlers/findings.js +65 -68
- package/dist/handlers/git-issues.d.ts +9 -13
- package/dist/handlers/git-issues.js +80 -225
- package/dist/handlers/ideas.d.ts +3 -0
- package/dist/handlers/ideas.js +53 -134
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.d.ts +2 -0
- package/dist/handlers/milestones.js +51 -98
- package/dist/handlers/organizations.js +79 -275
- package/dist/handlers/progress.d.ts +2 -0
- package/dist/handlers/progress.js +25 -123
- package/dist/handlers/project.js +42 -221
- package/dist/handlers/requests.d.ts +2 -0
- package/dist/handlers/requests.js +23 -83
- package/dist/handlers/session.js +119 -590
- package/dist/handlers/sprints.d.ts +32 -0
- package/dist/handlers/sprints.js +275 -0
- package/dist/handlers/tasks.d.ts +7 -10
- package/dist/handlers/tasks.js +245 -894
- package/dist/handlers/tool-docs.d.ts +9 -0
- package/dist/handlers/tool-docs.js +904 -0
- package/dist/handlers/types.d.ts +11 -3
- package/dist/handlers/validation.d.ts +1 -1
- package/dist/handlers/validation.js +38 -153
- package/dist/index.js +493 -162
- package/dist/knowledge.js +106 -9
- package/dist/tools.js +34 -4
- package/dist/validators.d.ts +21 -0
- package/dist/validators.js +91 -0
- package/package.json +2 -3
- package/src/api-client.ts +1822 -0
- package/src/cli.test.ts +128 -302
- package/src/cli.ts +41 -285
- package/src/handlers/__test-setup__.ts +215 -0
- package/src/handlers/__test-utils__.ts +4 -134
- package/src/handlers/blockers.test.ts +114 -124
- package/src/handlers/blockers.ts +68 -70
- package/src/handlers/bodies-of-work.test.ts +236 -831
- package/src/handlers/bodies-of-work.ts +210 -525
- package/src/handlers/cost.test.ts +149 -113
- package/src/handlers/cost.ts +44 -132
- package/src/handlers/decisions.test.ts +111 -209
- package/src/handlers/decisions.ts +35 -27
- package/src/handlers/deployment.test.ts +193 -239
- package/src/handlers/deployment.ts +143 -896
- package/src/handlers/discovery.test.ts +20 -67
- package/src/handlers/discovery.ts +29 -714
- package/src/handlers/fallback.test.ts +206 -361
- package/src/handlers/fallback.ts +81 -156
- package/src/handlers/findings.test.ts +229 -320
- package/src/handlers/findings.ts +76 -64
- package/src/handlers/git-issues.test.ts +623 -0
- package/src/handlers/git-issues.ts +174 -0
- package/src/handlers/ideas.test.ts +229 -343
- package/src/handlers/ideas.ts +69 -143
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +167 -281
- package/src/handlers/milestones.ts +54 -93
- package/src/handlers/organizations.test.ts +275 -467
- package/src/handlers/organizations.ts +84 -294
- package/src/handlers/progress.test.ts +112 -218
- package/src/handlers/progress.ts +29 -142
- package/src/handlers/project.test.ts +203 -226
- package/src/handlers/project.ts +48 -238
- package/src/handlers/requests.test.ts +74 -342
- package/src/handlers/requests.ts +25 -83
- package/src/handlers/session.test.ts +276 -206
- package/src/handlers/session.ts +136 -662
- package/src/handlers/sprints.test.ts +711 -0
- package/src/handlers/sprints.ts +510 -0
- package/src/handlers/tasks.test.ts +669 -353
- package/src/handlers/tasks.ts +263 -1015
- package/src/handlers/tool-docs.ts +1024 -0
- package/src/handlers/types.ts +12 -4
- package/src/handlers/validation.test.ts +237 -568
- package/src/handlers/validation.ts +43 -167
- package/src/index.ts +493 -186
- package/src/tools.ts +2532 -0
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +127 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +14 -13
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -367
- package/dist/handlers/__test-utils__.d.ts +0 -72
- package/dist/handlers/__test-utils__.js +0 -176
- package/dist/handlers/checkouts.d.ts +0 -37
- package/dist/handlers/checkouts.js +0 -377
- package/dist/handlers/knowledge-query.d.ts +0 -22
- package/dist/handlers/knowledge-query.js +0 -253
- package/dist/handlers/knowledge.d.ts +0 -12
- package/dist/handlers/knowledge.js +0 -108
- package/dist/handlers/roles.d.ts +0 -30
- package/dist/handlers/roles.js +0 -281
- package/dist/handlers/tasks.test.d.ts +0 -1
- package/dist/handlers/tasks.test.js +0 -431
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -532
- package/dist/validators.test.d.ts +0 -1
- package/dist/validators.test.js +0 -176
- package/src/knowledge.ts +0 -132
- package/src/tmpclaude-0078-cwd +0 -1
- package/src/tmpclaude-0ee1-cwd +0 -1
- package/src/tmpclaude-2dd5-cwd +0 -1
- package/src/tmpclaude-344c-cwd +0 -1
- package/src/tmpclaude-3860-cwd +0 -1
- package/src/tmpclaude-4b63-cwd +0 -1
- package/src/tmpclaude-5c73-cwd +0 -1
- package/src/tmpclaude-5ee3-cwd +0 -1
- package/src/tmpclaude-6795-cwd +0 -1
- package/src/tmpclaude-709e-cwd +0 -1
- package/src/tmpclaude-9839-cwd +0 -1
- package/src/tmpclaude-d829-cwd +0 -1
- package/src/tmpclaude-e072-cwd +0 -1
- package/src/tmpclaude-f6ee-cwd +0 -1
- package/tmpclaude-0439-cwd +0 -1
- package/tmpclaude-132f-cwd +0 -1
- package/tmpclaude-15bb-cwd +0 -1
- package/tmpclaude-165a-cwd +0 -1
- package/tmpclaude-1ba9-cwd +0 -1
- package/tmpclaude-21a3-cwd +0 -1
- package/tmpclaude-2a38-cwd +0 -1
- package/tmpclaude-2adf-cwd +0 -1
- package/tmpclaude-2f56-cwd +0 -1
- package/tmpclaude-3626-cwd +0 -1
- package/tmpclaude-3727-cwd +0 -1
- package/tmpclaude-40bc-cwd +0 -1
- package/tmpclaude-436f-cwd +0 -1
- package/tmpclaude-4783-cwd +0 -1
- package/tmpclaude-4b6d-cwd +0 -1
- package/tmpclaude-4ba4-cwd +0 -1
- package/tmpclaude-51e6-cwd +0 -1
- package/tmpclaude-5ecf-cwd +0 -1
- package/tmpclaude-6f97-cwd +0 -1
- package/tmpclaude-7fb2-cwd +0 -1
- package/tmpclaude-825c-cwd +0 -1
- package/tmpclaude-8baf-cwd +0 -1
- package/tmpclaude-8d9f-cwd +0 -1
- package/tmpclaude-975c-cwd +0 -1
- package/tmpclaude-9983-cwd +0 -1
- package/tmpclaude-a045-cwd +0 -1
- package/tmpclaude-ac4a-cwd +0 -1
- package/tmpclaude-b593-cwd +0 -1
- package/tmpclaude-b891-cwd +0 -1
- package/tmpclaude-c032-cwd +0 -1
- package/tmpclaude-cf43-cwd +0 -1
- package/tmpclaude-d040-cwd +0 -1
- package/tmpclaude-dcdd-cwd +0 -1
- package/tmpclaude-dcee-cwd +0 -1
- package/tmpclaude-e16b-cwd +0 -1
- package/tmpclaude-ecd2-cwd +0 -1
- package/tmpclaude-f48d-cwd +0 -1
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
21
21
|
import { validateRequired, validateUUID } from '../validators.js';
|
|
22
|
-
import {
|
|
22
|
+
import { getApiClient } from '../api-client.js';
|
|
23
23
|
|
|
24
24
|
// Valid roles in order of permission level
|
|
25
25
|
const ROLE_ORDER = ['viewer', 'member', 'admin', 'owner'] as const;
|
|
@@ -29,62 +29,23 @@ type Role = (typeof ROLE_ORDER)[number];
|
|
|
29
29
|
const PERMISSION_ORDER = ['read', 'write', 'admin'] as const;
|
|
30
30
|
type Permission = (typeof PERMISSION_ORDER)[number];
|
|
31
31
|
|
|
32
|
-
/**
|
|
33
|
-
* Generate a URL-friendly slug from a name
|
|
34
|
-
*/
|
|
35
|
-
function generateSlug(name: string): string {
|
|
36
|
-
return name
|
|
37
|
-
.toLowerCase()
|
|
38
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
39
|
-
.replace(/^-|-$/g, '')
|
|
40
|
-
.slice(0, 50);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Generate a secure invite token
|
|
45
|
-
*/
|
|
46
|
-
function generateInviteToken(): string {
|
|
47
|
-
return randomBytes(32).toString('base64url');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
32
|
// ============================================================================
|
|
51
33
|
// Organization Management
|
|
52
34
|
// ============================================================================
|
|
53
35
|
|
|
54
36
|
export const listOrganizations: Handler = async (_args, ctx) => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
id,
|
|
64
|
-
name,
|
|
65
|
-
slug,
|
|
66
|
-
description,
|
|
67
|
-
logo_url,
|
|
68
|
-
owner_id,
|
|
69
|
-
created_at
|
|
70
|
-
)
|
|
71
|
-
`)
|
|
72
|
-
.eq('user_id', auth.userId)
|
|
73
|
-
.order('joined_at', { ascending: false });
|
|
74
|
-
|
|
75
|
-
if (error) throw new Error(`Failed to list organizations: ${error.message}`);
|
|
76
|
-
|
|
77
|
-
const organizations = (data || []).map((m) => ({
|
|
78
|
-
...(m.organizations as unknown as Record<string, unknown>),
|
|
79
|
-
role: m.role,
|
|
80
|
-
joined_at: m.joined_at,
|
|
81
|
-
}));
|
|
82
|
-
|
|
83
|
-
return { result: { organizations, count: organizations.length } };
|
|
37
|
+
const apiClient = getApiClient();
|
|
38
|
+
const response = await apiClient.listOrganizations();
|
|
39
|
+
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(response.error || 'Failed to list organizations');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { result: response.data };
|
|
84
45
|
};
|
|
85
46
|
|
|
86
47
|
export const createOrganization: Handler = async (args, ctx) => {
|
|
87
|
-
const { name, description, slug
|
|
48
|
+
const { name, description, slug } = args as {
|
|
88
49
|
name: string;
|
|
89
50
|
description?: string;
|
|
90
51
|
slug?: string;
|
|
@@ -92,40 +53,18 @@ export const createOrganization: Handler = async (args, ctx) => {
|
|
|
92
53
|
|
|
93
54
|
validateRequired(name, 'name');
|
|
94
55
|
|
|
95
|
-
const
|
|
96
|
-
const
|
|
56
|
+
const apiClient = getApiClient();
|
|
57
|
+
const response = await apiClient.createOrganization({
|
|
58
|
+
name,
|
|
59
|
+
description,
|
|
60
|
+
slug
|
|
61
|
+
});
|
|
97
62
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.from('organizations')
|
|
101
|
-
.select('id')
|
|
102
|
-
.eq('slug', slug)
|
|
103
|
-
.maybeSingle();
|
|
104
|
-
|
|
105
|
-
if (existing) {
|
|
106
|
-
throw new Error(`Organization slug "${slug}" is already taken`);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(response.error || 'Failed to create organization');
|
|
107
65
|
}
|
|
108
66
|
|
|
109
|
-
|
|
110
|
-
.from('organizations')
|
|
111
|
-
.insert({
|
|
112
|
-
name,
|
|
113
|
-
slug,
|
|
114
|
-
description: description || null,
|
|
115
|
-
owner_id: auth.userId,
|
|
116
|
-
})
|
|
117
|
-
.select()
|
|
118
|
-
.single();
|
|
119
|
-
|
|
120
|
-
if (error) throw new Error(`Failed to create organization: ${error.message}`);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
result: {
|
|
124
|
-
success: true,
|
|
125
|
-
organization: data,
|
|
126
|
-
message: `Organization "${name}" created. You are the owner.`,
|
|
127
|
-
},
|
|
128
|
-
};
|
|
67
|
+
return { result: response.data };
|
|
129
68
|
};
|
|
130
69
|
|
|
131
70
|
export const updateOrganization: Handler = async (args, ctx) => {
|
|
@@ -139,8 +78,6 @@ export const updateOrganization: Handler = async (args, ctx) => {
|
|
|
139
78
|
validateRequired(organization_id, 'organization_id');
|
|
140
79
|
validateUUID(organization_id, 'organization_id');
|
|
141
80
|
|
|
142
|
-
const { supabase } = ctx;
|
|
143
|
-
|
|
144
81
|
const updates: Record<string, unknown> = {};
|
|
145
82
|
if (name !== undefined) updates.name = name;
|
|
146
83
|
if (description !== undefined) updates.description = description;
|
|
@@ -150,16 +87,14 @@ export const updateOrganization: Handler = async (args, ctx) => {
|
|
|
150
87
|
throw new Error('No updates provided');
|
|
151
88
|
}
|
|
152
89
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
.update(updates)
|
|
156
|
-
.eq('id', organization_id)
|
|
157
|
-
.select()
|
|
158
|
-
.single();
|
|
90
|
+
const apiClient = getApiClient();
|
|
91
|
+
const response = await apiClient.updateOrganization(organization_id, updates);
|
|
159
92
|
|
|
160
|
-
if (
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(response.error || 'Failed to update organization');
|
|
95
|
+
}
|
|
161
96
|
|
|
162
|
-
return { result:
|
|
97
|
+
return { result: response.data };
|
|
163
98
|
};
|
|
164
99
|
|
|
165
100
|
export const deleteOrganization: Handler = async (args, ctx) => {
|
|
@@ -168,21 +103,14 @@ export const deleteOrganization: Handler = async (args, ctx) => {
|
|
|
168
103
|
validateRequired(organization_id, 'organization_id');
|
|
169
104
|
validateUUID(organization_id, 'organization_id');
|
|
170
105
|
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
const { error } = await supabase
|
|
174
|
-
.from('organizations')
|
|
175
|
-
.delete()
|
|
176
|
-
.eq('id', organization_id);
|
|
106
|
+
const apiClient = getApiClient();
|
|
107
|
+
const response = await apiClient.deleteOrganization(organization_id);
|
|
177
108
|
|
|
178
|
-
if (
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
throw new Error(response.error || 'Failed to delete organization');
|
|
111
|
+
}
|
|
179
112
|
|
|
180
|
-
return {
|
|
181
|
-
result: {
|
|
182
|
-
success: true,
|
|
183
|
-
message: 'Organization deleted. All shares have been removed.',
|
|
184
|
-
},
|
|
185
|
-
};
|
|
113
|
+
return { result: response.data };
|
|
186
114
|
};
|
|
187
115
|
|
|
188
116
|
// ============================================================================
|
|
@@ -195,18 +123,14 @@ export const listOrgMembers: Handler = async (args, ctx) => {
|
|
|
195
123
|
validateRequired(organization_id, 'organization_id');
|
|
196
124
|
validateUUID(organization_id, 'organization_id');
|
|
197
125
|
|
|
198
|
-
const
|
|
126
|
+
const apiClient = getApiClient();
|
|
127
|
+
const response = await apiClient.listOrgMembers(organization_id);
|
|
199
128
|
|
|
200
|
-
|
|
201
|
-
.
|
|
202
|
-
|
|
203
|
-
.eq('organization_id', organization_id)
|
|
204
|
-
.order('role', { ascending: true })
|
|
205
|
-
.order('joined_at', { ascending: true });
|
|
206
|
-
|
|
207
|
-
if (error) throw new Error(`Failed to list members: ${error.message}`);
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
throw new Error(response.error || 'Failed to list members');
|
|
131
|
+
}
|
|
208
132
|
|
|
209
|
-
return { result:
|
|
133
|
+
return { result: response.data };
|
|
210
134
|
};
|
|
211
135
|
|
|
212
136
|
export const inviteMember: Handler = async (args, ctx) => {
|
|
@@ -224,44 +148,14 @@ export const inviteMember: Handler = async (args, ctx) => {
|
|
|
224
148
|
throw new Error('Invalid role. Must be admin, member, or viewer.');
|
|
225
149
|
}
|
|
226
150
|
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.from('organization_invites')
|
|
233
|
-
.select('id')
|
|
234
|
-
.eq('organization_id', organization_id)
|
|
235
|
-
.eq('email', email)
|
|
236
|
-
.is('accepted_at', null)
|
|
237
|
-
.gt('expires_at', new Date().toISOString())
|
|
238
|
-
.maybeSingle();
|
|
239
|
-
|
|
240
|
-
if (existing) {
|
|
241
|
-
throw new Error(`A pending invite already exists for ${email}`);
|
|
151
|
+
const apiClient = getApiClient();
|
|
152
|
+
const response = await apiClient.inviteMember(organization_id, email, role);
|
|
153
|
+
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new Error(response.error || 'Failed to create invite');
|
|
242
156
|
}
|
|
243
157
|
|
|
244
|
-
|
|
245
|
-
.from('organization_invites')
|
|
246
|
-
.insert({
|
|
247
|
-
organization_id,
|
|
248
|
-
email,
|
|
249
|
-
role,
|
|
250
|
-
token,
|
|
251
|
-
invited_by: auth.userId,
|
|
252
|
-
})
|
|
253
|
-
.select()
|
|
254
|
-
.single();
|
|
255
|
-
|
|
256
|
-
if (error) throw new Error(`Failed to create invite: ${error.message}`);
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
result: {
|
|
260
|
-
success: true,
|
|
261
|
-
invite: data,
|
|
262
|
-
message: `Invite sent to ${email} with role "${role}"`,
|
|
263
|
-
},
|
|
264
|
-
};
|
|
158
|
+
return { result: response.data };
|
|
265
159
|
};
|
|
266
160
|
|
|
267
161
|
export const updateMemberRole: Handler = async (args, ctx) => {
|
|
@@ -285,25 +179,14 @@ export const updateMemberRole: Handler = async (args, ctx) => {
|
|
|
285
179
|
throw new Error('Cannot assign owner role. Use transfer ownership instead.');
|
|
286
180
|
}
|
|
287
181
|
|
|
288
|
-
const
|
|
182
|
+
const apiClient = getApiClient();
|
|
183
|
+
const response = await apiClient.updateMemberRole(organization_id, user_id, role);
|
|
289
184
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
throw new Error('Cannot change your own role');
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(response.error || 'Failed to update member role');
|
|
293
187
|
}
|
|
294
188
|
|
|
295
|
-
|
|
296
|
-
.from('organization_members')
|
|
297
|
-
.update({ role })
|
|
298
|
-
.eq('organization_id', organization_id)
|
|
299
|
-
.eq('user_id', user_id)
|
|
300
|
-
.neq('role', 'owner') // Cannot change owner's role
|
|
301
|
-
.select()
|
|
302
|
-
.single();
|
|
303
|
-
|
|
304
|
-
if (error) throw new Error(`Failed to update member role: ${error.message}`);
|
|
305
|
-
|
|
306
|
-
return { result: { success: true, member: data } };
|
|
189
|
+
return { result: response.data };
|
|
307
190
|
};
|
|
308
191
|
|
|
309
192
|
export const removeMember: Handler = async (args, ctx) => {
|
|
@@ -317,23 +200,14 @@ export const removeMember: Handler = async (args, ctx) => {
|
|
|
317
200
|
validateUUID(organization_id, 'organization_id');
|
|
318
201
|
validateUUID(user_id, 'user_id');
|
|
319
202
|
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
const { error } = await supabase
|
|
323
|
-
.from('organization_members')
|
|
324
|
-
.delete()
|
|
325
|
-
.eq('organization_id', organization_id)
|
|
326
|
-
.eq('user_id', user_id)
|
|
327
|
-
.neq('role', 'owner'); // Cannot remove owner
|
|
203
|
+
const apiClient = getApiClient();
|
|
204
|
+
const response = await apiClient.removeMember(organization_id, user_id);
|
|
328
205
|
|
|
329
|
-
if (
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
throw new Error(response.error || 'Failed to remove member');
|
|
208
|
+
}
|
|
330
209
|
|
|
331
|
-
return {
|
|
332
|
-
result: {
|
|
333
|
-
success: true,
|
|
334
|
-
message: 'Member removed. Their org-scoped API keys have been invalidated.',
|
|
335
|
-
},
|
|
336
|
-
};
|
|
210
|
+
return { result: response.data };
|
|
337
211
|
};
|
|
338
212
|
|
|
339
213
|
export const leaveOrganization: Handler = async (args, ctx) => {
|
|
@@ -342,34 +216,14 @@ export const leaveOrganization: Handler = async (args, ctx) => {
|
|
|
342
216
|
validateRequired(organization_id, 'organization_id');
|
|
343
217
|
validateUUID(organization_id, 'organization_id');
|
|
344
218
|
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
// Check if user is owner
|
|
348
|
-
const { data: membership } = await supabase
|
|
349
|
-
.from('organization_members')
|
|
350
|
-
.select('role')
|
|
351
|
-
.eq('organization_id', organization_id)
|
|
352
|
-
.eq('user_id', auth.userId)
|
|
353
|
-
.single();
|
|
219
|
+
const apiClient = getApiClient();
|
|
220
|
+
const response = await apiClient.leaveOrganization(organization_id);
|
|
354
221
|
|
|
355
|
-
if (
|
|
356
|
-
throw new Error(
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
throw new Error(response.error || 'Failed to leave organization');
|
|
357
224
|
}
|
|
358
225
|
|
|
359
|
-
|
|
360
|
-
.from('organization_members')
|
|
361
|
-
.delete()
|
|
362
|
-
.eq('organization_id', organization_id)
|
|
363
|
-
.eq('user_id', auth.userId);
|
|
364
|
-
|
|
365
|
-
if (error) throw new Error(`Failed to leave organization: ${error.message}`);
|
|
366
|
-
|
|
367
|
-
return {
|
|
368
|
-
result: {
|
|
369
|
-
success: true,
|
|
370
|
-
message: 'You have left the organization.',
|
|
371
|
-
},
|
|
372
|
-
};
|
|
226
|
+
return { result: response.data };
|
|
373
227
|
};
|
|
374
228
|
|
|
375
229
|
// ============================================================================
|
|
@@ -392,52 +246,14 @@ export const shareProjectWithOrg: Handler = async (args, ctx) => {
|
|
|
392
246
|
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
393
247
|
}
|
|
394
248
|
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
// Verify user owns the project
|
|
398
|
-
const { data: project } = await supabase
|
|
399
|
-
.from('projects')
|
|
400
|
-
.select('id, name')
|
|
401
|
-
.eq('id', project_id)
|
|
402
|
-
.eq('user_id', auth.userId)
|
|
403
|
-
.single();
|
|
249
|
+
const apiClient = getApiClient();
|
|
250
|
+
const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
|
|
404
251
|
|
|
405
|
-
if (!
|
|
406
|
-
throw new Error(
|
|
252
|
+
if (!response.ok) {
|
|
253
|
+
throw new Error(response.error || 'Failed to share project');
|
|
407
254
|
}
|
|
408
255
|
|
|
409
|
-
|
|
410
|
-
const { data: existing } = await supabase
|
|
411
|
-
.from('project_shares')
|
|
412
|
-
.select('id')
|
|
413
|
-
.eq('project_id', project_id)
|
|
414
|
-
.eq('organization_id', organization_id)
|
|
415
|
-
.maybeSingle();
|
|
416
|
-
|
|
417
|
-
if (existing) {
|
|
418
|
-
throw new Error('Project is already shared with this organization');
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const { data, error } = await supabase
|
|
422
|
-
.from('project_shares')
|
|
423
|
-
.insert({
|
|
424
|
-
project_id,
|
|
425
|
-
organization_id,
|
|
426
|
-
permission,
|
|
427
|
-
shared_by: auth.userId,
|
|
428
|
-
})
|
|
429
|
-
.select()
|
|
430
|
-
.single();
|
|
431
|
-
|
|
432
|
-
if (error) throw new Error(`Failed to share project: ${error.message}`);
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
result: {
|
|
436
|
-
success: true,
|
|
437
|
-
share: data,
|
|
438
|
-
message: `Project "${project.name}" shared with organization (${permission} access)`,
|
|
439
|
-
},
|
|
440
|
-
};
|
|
256
|
+
return { result: response.data };
|
|
441
257
|
};
|
|
442
258
|
|
|
443
259
|
export const updateProjectShare: Handler = async (args, ctx) => {
|
|
@@ -457,19 +273,14 @@ export const updateProjectShare: Handler = async (args, ctx) => {
|
|
|
457
273
|
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
458
274
|
}
|
|
459
275
|
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
const { data, error } = await supabase
|
|
463
|
-
.from('project_shares')
|
|
464
|
-
.update({ permission })
|
|
465
|
-
.eq('project_id', project_id)
|
|
466
|
-
.eq('organization_id', organization_id)
|
|
467
|
-
.select()
|
|
468
|
-
.single();
|
|
276
|
+
const apiClient = getApiClient();
|
|
277
|
+
const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
|
|
469
278
|
|
|
470
|
-
if (
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new Error(response.error || 'Failed to update share');
|
|
281
|
+
}
|
|
471
282
|
|
|
472
|
-
return { result:
|
|
283
|
+
return { result: response.data };
|
|
473
284
|
};
|
|
474
285
|
|
|
475
286
|
export const unshareProject: Handler = async (args, ctx) => {
|
|
@@ -483,22 +294,14 @@ export const unshareProject: Handler = async (args, ctx) => {
|
|
|
483
294
|
validateUUID(project_id, 'project_id');
|
|
484
295
|
validateUUID(organization_id, 'organization_id');
|
|
485
296
|
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
const { error } = await supabase
|
|
489
|
-
.from('project_shares')
|
|
490
|
-
.delete()
|
|
491
|
-
.eq('project_id', project_id)
|
|
492
|
-
.eq('organization_id', organization_id);
|
|
297
|
+
const apiClient = getApiClient();
|
|
298
|
+
const response = await apiClient.unshareProject(project_id, organization_id);
|
|
493
299
|
|
|
494
|
-
if (
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
throw new Error(response.error || 'Failed to unshare project');
|
|
302
|
+
}
|
|
495
303
|
|
|
496
|
-
return {
|
|
497
|
-
result: {
|
|
498
|
-
success: true,
|
|
499
|
-
message: 'Project share removed. Org members can no longer access this project.',
|
|
500
|
-
},
|
|
501
|
-
};
|
|
304
|
+
return { result: response.data };
|
|
502
305
|
};
|
|
503
306
|
|
|
504
307
|
export const listProjectShares: Handler = async (args, ctx) => {
|
|
@@ -507,27 +310,14 @@ export const listProjectShares: Handler = async (args, ctx) => {
|
|
|
507
310
|
validateRequired(project_id, 'project_id');
|
|
508
311
|
validateUUID(project_id, 'project_id');
|
|
509
312
|
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
shared_by,
|
|
519
|
-
organizations (
|
|
520
|
-
id,
|
|
521
|
-
name,
|
|
522
|
-
slug
|
|
523
|
-
)
|
|
524
|
-
`)
|
|
525
|
-
.eq('project_id', project_id)
|
|
526
|
-
.order('shared_at', { ascending: false });
|
|
527
|
-
|
|
528
|
-
if (error) throw new Error(`Failed to list shares: ${error.message}`);
|
|
529
|
-
|
|
530
|
-
return { result: { shares: data || [], count: data?.length || 0 } };
|
|
313
|
+
const apiClient = getApiClient();
|
|
314
|
+
const response = await apiClient.listProjectShares(project_id);
|
|
315
|
+
|
|
316
|
+
if (!response.ok) {
|
|
317
|
+
throw new Error(response.error || 'Failed to list shares');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return { result: response.data };
|
|
531
321
|
};
|
|
532
322
|
|
|
533
323
|
/**
|