@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
|
@@ -7,53 +7,30 @@
|
|
|
7
7
|
* - complete_milestone
|
|
8
8
|
* - delete_milestone
|
|
9
9
|
* - get_milestones
|
|
10
|
+
*
|
|
11
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
10
12
|
*/
|
|
11
13
|
import { ValidationError, validateRequired, validateUUID } from '../validators.js';
|
|
14
|
+
import { getApiClient } from '../api-client.js';
|
|
12
15
|
export const addMilestone = async (args, ctx) => {
|
|
13
16
|
const { task_id, title, description, order_index } = args;
|
|
14
17
|
validateRequired(task_id, 'task_id');
|
|
15
18
|
validateUUID(task_id, 'task_id');
|
|
16
19
|
validateRequired(title, 'title');
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
.from('tasks')
|
|
21
|
-
.select('id, project_id')
|
|
22
|
-
.eq('id', task_id)
|
|
23
|
-
.single();
|
|
24
|
-
if (taskError || !task) {
|
|
25
|
-
throw new Error('Task not found');
|
|
26
|
-
}
|
|
27
|
-
// Get the next order_index if not provided
|
|
28
|
-
let orderIdx = order_index;
|
|
29
|
-
if (orderIdx === undefined) {
|
|
30
|
-
const { data: maxOrder } = await supabase
|
|
31
|
-
.from('task_milestones')
|
|
32
|
-
.select('order_index')
|
|
33
|
-
.eq('task_id', task_id)
|
|
34
|
-
.order('order_index', { ascending: false })
|
|
35
|
-
.limit(1)
|
|
36
|
-
.single();
|
|
37
|
-
orderIdx = maxOrder ? maxOrder.order_index + 1 : 0;
|
|
38
|
-
}
|
|
39
|
-
const { data: milestone, error } = await supabase
|
|
40
|
-
.from('task_milestones')
|
|
41
|
-
.insert({
|
|
42
|
-
task_id,
|
|
20
|
+
const { session } = ctx;
|
|
21
|
+
const apiClient = getApiClient();
|
|
22
|
+
const response = await apiClient.addMilestone(task_id, {
|
|
43
23
|
title,
|
|
44
|
-
description
|
|
45
|
-
order_index
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.single();
|
|
51
|
-
if (error)
|
|
52
|
-
throw error;
|
|
24
|
+
description,
|
|
25
|
+
order_index
|
|
26
|
+
}, session.currentSessionId || undefined);
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`Failed to add milestone: ${response.error}`);
|
|
29
|
+
}
|
|
53
30
|
return {
|
|
54
31
|
result: {
|
|
55
32
|
success: true,
|
|
56
|
-
|
|
33
|
+
milestone_id: response.data?.milestone_id,
|
|
57
34
|
},
|
|
58
35
|
};
|
|
59
36
|
};
|
|
@@ -61,40 +38,30 @@ export const updateMilestone = async (args, ctx) => {
|
|
|
61
38
|
const { milestone_id, title, description, status, order_index } = args;
|
|
62
39
|
validateRequired(milestone_id, 'milestone_id');
|
|
63
40
|
validateUUID(milestone_id, 'milestone_id');
|
|
64
|
-
|
|
65
|
-
if (title !== undefined)
|
|
66
|
-
updates.title = title;
|
|
67
|
-
if (description !== undefined)
|
|
68
|
-
updates.description = description;
|
|
69
|
-
if (order_index !== undefined)
|
|
70
|
-
updates.order_index = order_index;
|
|
41
|
+
// Validate status if provided
|
|
71
42
|
if (status !== undefined) {
|
|
72
43
|
if (!['pending', 'in_progress', 'completed'].includes(status)) {
|
|
73
44
|
throw new ValidationError('status must be pending, in_progress, or completed');
|
|
74
45
|
}
|
|
75
|
-
updates.status = status;
|
|
76
|
-
if (status === 'completed') {
|
|
77
|
-
updates.completed_at = new Date().toISOString();
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
updates.completed_at = null;
|
|
81
|
-
}
|
|
82
46
|
}
|
|
83
|
-
|
|
47
|
+
// Check that at least one field is provided
|
|
48
|
+
if (title === undefined && description === undefined && status === undefined && order_index === undefined) {
|
|
84
49
|
throw new ValidationError('At least one field to update is required');
|
|
85
50
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
51
|
+
const apiClient = getApiClient();
|
|
52
|
+
const response = await apiClient.updateMilestone(milestone_id, {
|
|
53
|
+
title,
|
|
54
|
+
description,
|
|
55
|
+
status: status,
|
|
56
|
+
order_index
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`Failed to update milestone: ${response.error}`);
|
|
60
|
+
}
|
|
94
61
|
return {
|
|
95
62
|
result: {
|
|
96
63
|
success: true,
|
|
97
|
-
milestone,
|
|
64
|
+
milestone: response.data?.milestone,
|
|
98
65
|
},
|
|
99
66
|
};
|
|
100
67
|
};
|
|
@@ -102,21 +69,15 @@ export const completeMilestone = async (args, ctx) => {
|
|
|
102
69
|
const { milestone_id } = args;
|
|
103
70
|
validateRequired(milestone_id, 'milestone_id');
|
|
104
71
|
validateUUID(milestone_id, 'milestone_id');
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
})
|
|
111
|
-
.eq('id', milestone_id)
|
|
112
|
-
.select()
|
|
113
|
-
.single();
|
|
114
|
-
if (error)
|
|
115
|
-
throw error;
|
|
72
|
+
const apiClient = getApiClient();
|
|
73
|
+
const response = await apiClient.completeMilestone(milestone_id);
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
throw new Error(`Failed to complete milestone: ${response.error}`);
|
|
76
|
+
}
|
|
116
77
|
return {
|
|
117
78
|
result: {
|
|
118
79
|
success: true,
|
|
119
|
-
milestone,
|
|
80
|
+
milestone: response.data?.milestone,
|
|
120
81
|
},
|
|
121
82
|
};
|
|
122
83
|
};
|
|
@@ -124,12 +85,11 @@ export const deleteMilestone = async (args, ctx) => {
|
|
|
124
85
|
const { milestone_id } = args;
|
|
125
86
|
validateRequired(milestone_id, 'milestone_id');
|
|
126
87
|
validateUUID(milestone_id, 'milestone_id');
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
throw error;
|
|
88
|
+
const apiClient = getApiClient();
|
|
89
|
+
const response = await apiClient.deleteMilestone(milestone_id);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`Failed to delete milestone: ${response.error}`);
|
|
92
|
+
}
|
|
133
93
|
return {
|
|
134
94
|
result: {
|
|
135
95
|
success: true,
|
|
@@ -141,28 +101,21 @@ export const getMilestones = async (args, ctx) => {
|
|
|
141
101
|
const { task_id } = args;
|
|
142
102
|
validateRequired(task_id, 'task_id');
|
|
143
103
|
validateUUID(task_id, 'task_id');
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
throw error;
|
|
151
|
-
// Calculate progress stats
|
|
152
|
-
const total = milestones?.length || 0;
|
|
153
|
-
const completed = milestones?.filter(m => m.status === 'completed').length || 0;
|
|
154
|
-
const in_progress = milestones?.filter(m => m.status === 'in_progress').length || 0;
|
|
155
|
-
const pending = total - completed - in_progress;
|
|
156
|
-
const progress_percentage = total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
104
|
+
const apiClient = getApiClient();
|
|
105
|
+
const response = await apiClient.getMilestones(task_id);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error(`Failed to get milestones: ${response.error}`);
|
|
108
|
+
}
|
|
109
|
+
// Stats are calculated server-side now
|
|
157
110
|
return {
|
|
158
111
|
result: {
|
|
159
|
-
milestones: milestones || [],
|
|
160
|
-
stats: {
|
|
161
|
-
total,
|
|
162
|
-
completed,
|
|
163
|
-
in_progress,
|
|
164
|
-
pending,
|
|
165
|
-
progress_percentage,
|
|
112
|
+
milestones: response.data?.milestones || [],
|
|
113
|
+
stats: response.data?.stats || {
|
|
114
|
+
total: 0,
|
|
115
|
+
completed: 0,
|
|
116
|
+
in_progress: 0,
|
|
117
|
+
pending: 0,
|
|
118
|
+
progress_percentage: 0,
|
|
166
119
|
},
|
|
167
120
|
},
|
|
168
121
|
};
|
|
@@ -17,97 +17,40 @@
|
|
|
17
17
|
* - list_project_shares
|
|
18
18
|
*/
|
|
19
19
|
import { validateRequired, validateUUID } from '../validators.js';
|
|
20
|
-
import {
|
|
20
|
+
import { getApiClient } from '../api-client.js';
|
|
21
21
|
// Valid roles in order of permission level
|
|
22
22
|
const ROLE_ORDER = ['viewer', 'member', 'admin', 'owner'];
|
|
23
23
|
// Valid share permissions
|
|
24
24
|
const PERMISSION_ORDER = ['read', 'write', 'admin'];
|
|
25
|
-
/**
|
|
26
|
-
* Generate a URL-friendly slug from a name
|
|
27
|
-
*/
|
|
28
|
-
function generateSlug(name) {
|
|
29
|
-
return name
|
|
30
|
-
.toLowerCase()
|
|
31
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
32
|
-
.replace(/^-|-$/g, '')
|
|
33
|
-
.slice(0, 50);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Generate a secure invite token
|
|
37
|
-
*/
|
|
38
|
-
function generateInviteToken() {
|
|
39
|
-
return randomBytes(32).toString('base64url');
|
|
40
|
-
}
|
|
41
25
|
// ============================================================================
|
|
42
26
|
// Organization Management
|
|
43
27
|
// ============================================================================
|
|
44
28
|
export const listOrganizations = async (_args, ctx) => {
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
organizations (
|
|
52
|
-
id,
|
|
53
|
-
name,
|
|
54
|
-
slug,
|
|
55
|
-
description,
|
|
56
|
-
logo_url,
|
|
57
|
-
owner_id,
|
|
58
|
-
created_at
|
|
59
|
-
)
|
|
60
|
-
`)
|
|
61
|
-
.eq('user_id', auth.userId)
|
|
62
|
-
.order('joined_at', { ascending: false });
|
|
63
|
-
if (error)
|
|
64
|
-
throw new Error(`Failed to list organizations: ${error.message}`);
|
|
65
|
-
const organizations = (data || []).map((m) => ({
|
|
66
|
-
...m.organizations,
|
|
67
|
-
role: m.role,
|
|
68
|
-
joined_at: m.joined_at,
|
|
69
|
-
}));
|
|
70
|
-
return { result: { organizations, count: organizations.length } };
|
|
29
|
+
const apiClient = getApiClient();
|
|
30
|
+
const response = await apiClient.listOrganizations();
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error(response.error || 'Failed to list organizations');
|
|
33
|
+
}
|
|
34
|
+
return { result: response.data };
|
|
71
35
|
};
|
|
72
36
|
export const createOrganization = async (args, ctx) => {
|
|
73
|
-
const { name, description, slug
|
|
37
|
+
const { name, description, slug } = args;
|
|
74
38
|
validateRequired(name, 'name');
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
// Check if slug is available
|
|
78
|
-
const { data: existing } = await supabase
|
|
79
|
-
.from('organizations')
|
|
80
|
-
.select('id')
|
|
81
|
-
.eq('slug', slug)
|
|
82
|
-
.maybeSingle();
|
|
83
|
-
if (existing) {
|
|
84
|
-
throw new Error(`Organization slug "${slug}" is already taken`);
|
|
85
|
-
}
|
|
86
|
-
const { data, error } = await supabase
|
|
87
|
-
.from('organizations')
|
|
88
|
-
.insert({
|
|
39
|
+
const apiClient = getApiClient();
|
|
40
|
+
const response = await apiClient.createOrganization({
|
|
89
41
|
name,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
throw new Error(`Failed to create organization: ${error.message}`);
|
|
98
|
-
return {
|
|
99
|
-
result: {
|
|
100
|
-
success: true,
|
|
101
|
-
organization: data,
|
|
102
|
-
message: `Organization "${name}" created. You are the owner.`,
|
|
103
|
-
},
|
|
104
|
-
};
|
|
42
|
+
description,
|
|
43
|
+
slug
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw new Error(response.error || 'Failed to create organization');
|
|
47
|
+
}
|
|
48
|
+
return { result: response.data };
|
|
105
49
|
};
|
|
106
50
|
export const updateOrganization = async (args, ctx) => {
|
|
107
51
|
const { organization_id, name, description, logo_url } = args;
|
|
108
52
|
validateRequired(organization_id, 'organization_id');
|
|
109
53
|
validateUUID(organization_id, 'organization_id');
|
|
110
|
-
const { supabase } = ctx;
|
|
111
54
|
const updates = {};
|
|
112
55
|
if (name !== undefined)
|
|
113
56
|
updates.name = name;
|
|
@@ -118,33 +61,23 @@ export const updateOrganization = async (args, ctx) => {
|
|
|
118
61
|
if (Object.keys(updates).length === 0) {
|
|
119
62
|
throw new Error('No updates provided');
|
|
120
63
|
}
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (error)
|
|
128
|
-
throw new Error(`Failed to update organization: ${error.message}`);
|
|
129
|
-
return { result: { success: true, organization: data } };
|
|
64
|
+
const apiClient = getApiClient();
|
|
65
|
+
const response = await apiClient.updateOrganization(organization_id, updates);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
throw new Error(response.error || 'Failed to update organization');
|
|
68
|
+
}
|
|
69
|
+
return { result: response.data };
|
|
130
70
|
};
|
|
131
71
|
export const deleteOrganization = async (args, ctx) => {
|
|
132
72
|
const { organization_id } = args;
|
|
133
73
|
validateRequired(organization_id, 'organization_id');
|
|
134
74
|
validateUUID(organization_id, 'organization_id');
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
.delete
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
throw new Error(`Failed to delete organization: ${error.message}`);
|
|
142
|
-
return {
|
|
143
|
-
result: {
|
|
144
|
-
success: true,
|
|
145
|
-
message: 'Organization deleted. All shares have been removed.',
|
|
146
|
-
},
|
|
147
|
-
};
|
|
75
|
+
const apiClient = getApiClient();
|
|
76
|
+
const response = await apiClient.deleteOrganization(organization_id);
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(response.error || 'Failed to delete organization');
|
|
79
|
+
}
|
|
80
|
+
return { result: response.data };
|
|
148
81
|
};
|
|
149
82
|
// ============================================================================
|
|
150
83
|
// Member Management
|
|
@@ -153,16 +86,12 @@ export const listOrgMembers = async (args, ctx) => {
|
|
|
153
86
|
const { organization_id } = args;
|
|
154
87
|
validateRequired(organization_id, 'organization_id');
|
|
155
88
|
validateUUID(organization_id, 'organization_id');
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
.order('joined_at', { ascending: true });
|
|
163
|
-
if (error)
|
|
164
|
-
throw new Error(`Failed to list members: ${error.message}`);
|
|
165
|
-
return { result: { members: data || [], count: data?.length || 0 } };
|
|
89
|
+
const apiClient = getApiClient();
|
|
90
|
+
const response = await apiClient.listOrgMembers(organization_id);
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(response.error || 'Failed to list members');
|
|
93
|
+
}
|
|
94
|
+
return { result: response.data };
|
|
166
95
|
};
|
|
167
96
|
export const inviteMember = async (args, ctx) => {
|
|
168
97
|
const { organization_id, email, role = 'member' } = args;
|
|
@@ -172,40 +101,12 @@ export const inviteMember = async (args, ctx) => {
|
|
|
172
101
|
if (!['admin', 'member', 'viewer'].includes(role)) {
|
|
173
102
|
throw new Error('Invalid role. Must be admin, member, or viewer.');
|
|
174
103
|
}
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
.from('organization_invites')
|
|
180
|
-
.select('id')
|
|
181
|
-
.eq('organization_id', organization_id)
|
|
182
|
-
.eq('email', email)
|
|
183
|
-
.is('accepted_at', null)
|
|
184
|
-
.gt('expires_at', new Date().toISOString())
|
|
185
|
-
.maybeSingle();
|
|
186
|
-
if (existing) {
|
|
187
|
-
throw new Error(`A pending invite already exists for ${email}`);
|
|
104
|
+
const apiClient = getApiClient();
|
|
105
|
+
const response = await apiClient.inviteMember(organization_id, email, role);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error(response.error || 'Failed to create invite');
|
|
188
108
|
}
|
|
189
|
-
|
|
190
|
-
.from('organization_invites')
|
|
191
|
-
.insert({
|
|
192
|
-
organization_id,
|
|
193
|
-
email,
|
|
194
|
-
role,
|
|
195
|
-
token,
|
|
196
|
-
invited_by: auth.userId,
|
|
197
|
-
})
|
|
198
|
-
.select()
|
|
199
|
-
.single();
|
|
200
|
-
if (error)
|
|
201
|
-
throw new Error(`Failed to create invite: ${error.message}`);
|
|
202
|
-
return {
|
|
203
|
-
result: {
|
|
204
|
-
success: true,
|
|
205
|
-
invite: data,
|
|
206
|
-
message: `Invite sent to ${email} with role "${role}"`,
|
|
207
|
-
},
|
|
208
|
-
};
|
|
109
|
+
return { result: response.data };
|
|
209
110
|
};
|
|
210
111
|
export const updateMemberRole = async (args, ctx) => {
|
|
211
112
|
const { organization_id, user_id, role } = args;
|
|
@@ -220,22 +121,12 @@ export const updateMemberRole = async (args, ctx) => {
|
|
|
220
121
|
if (role === 'owner') {
|
|
221
122
|
throw new Error('Cannot assign owner role. Use transfer ownership instead.');
|
|
222
123
|
}
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
throw new Error('
|
|
124
|
+
const apiClient = getApiClient();
|
|
125
|
+
const response = await apiClient.updateMemberRole(organization_id, user_id, role);
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
throw new Error(response.error || 'Failed to update member role');
|
|
227
128
|
}
|
|
228
|
-
|
|
229
|
-
.from('organization_members')
|
|
230
|
-
.update({ role })
|
|
231
|
-
.eq('organization_id', organization_id)
|
|
232
|
-
.eq('user_id', user_id)
|
|
233
|
-
.neq('role', 'owner') // Cannot change owner's role
|
|
234
|
-
.select()
|
|
235
|
-
.single();
|
|
236
|
-
if (error)
|
|
237
|
-
throw new Error(`Failed to update member role: ${error.message}`);
|
|
238
|
-
return { result: { success: true, member: data } };
|
|
129
|
+
return { result: response.data };
|
|
239
130
|
};
|
|
240
131
|
export const removeMember = async (args, ctx) => {
|
|
241
132
|
const { organization_id, user_id } = args;
|
|
@@ -243,50 +134,23 @@ export const removeMember = async (args, ctx) => {
|
|
|
243
134
|
validateRequired(user_id, 'user_id');
|
|
244
135
|
validateUUID(organization_id, 'organization_id');
|
|
245
136
|
validateUUID(user_id, 'user_id');
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
.neq('role', 'owner'); // Cannot remove owner
|
|
253
|
-
if (error)
|
|
254
|
-
throw new Error(`Failed to remove member: ${error.message}`);
|
|
255
|
-
return {
|
|
256
|
-
result: {
|
|
257
|
-
success: true,
|
|
258
|
-
message: 'Member removed. Their org-scoped API keys have been invalidated.',
|
|
259
|
-
},
|
|
260
|
-
};
|
|
137
|
+
const apiClient = getApiClient();
|
|
138
|
+
const response = await apiClient.removeMember(organization_id, user_id);
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
throw new Error(response.error || 'Failed to remove member');
|
|
141
|
+
}
|
|
142
|
+
return { result: response.data };
|
|
261
143
|
};
|
|
262
144
|
export const leaveOrganization = async (args, ctx) => {
|
|
263
145
|
const { organization_id } = args;
|
|
264
146
|
validateRequired(organization_id, 'organization_id');
|
|
265
147
|
validateUUID(organization_id, 'organization_id');
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
.
|
|
270
|
-
.select('role')
|
|
271
|
-
.eq('organization_id', organization_id)
|
|
272
|
-
.eq('user_id', auth.userId)
|
|
273
|
-
.single();
|
|
274
|
-
if (membership?.role === 'owner') {
|
|
275
|
-
throw new Error('Owner cannot leave. Transfer ownership first or delete the organization.');
|
|
148
|
+
const apiClient = getApiClient();
|
|
149
|
+
const response = await apiClient.leaveOrganization(organization_id);
|
|
150
|
+
if (!response.ok) {
|
|
151
|
+
throw new Error(response.error || 'Failed to leave organization');
|
|
276
152
|
}
|
|
277
|
-
|
|
278
|
-
.from('organization_members')
|
|
279
|
-
.delete()
|
|
280
|
-
.eq('organization_id', organization_id)
|
|
281
|
-
.eq('user_id', auth.userId);
|
|
282
|
-
if (error)
|
|
283
|
-
throw new Error(`Failed to leave organization: ${error.message}`);
|
|
284
|
-
return {
|
|
285
|
-
result: {
|
|
286
|
-
success: true,
|
|
287
|
-
message: 'You have left the organization.',
|
|
288
|
-
},
|
|
289
|
-
};
|
|
153
|
+
return { result: response.data };
|
|
290
154
|
};
|
|
291
155
|
// ============================================================================
|
|
292
156
|
// Project Sharing
|
|
@@ -300,46 +164,12 @@ export const shareProjectWithOrg = async (args, ctx) => {
|
|
|
300
164
|
if (!PERMISSION_ORDER.includes(permission)) {
|
|
301
165
|
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
302
166
|
}
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
.
|
|
307
|
-
.select('id, name')
|
|
308
|
-
.eq('id', project_id)
|
|
309
|
-
.eq('user_id', auth.userId)
|
|
310
|
-
.single();
|
|
311
|
-
if (!project) {
|
|
312
|
-
throw new Error('Project not found or you are not the owner');
|
|
313
|
-
}
|
|
314
|
-
// Check if share already exists
|
|
315
|
-
const { data: existing } = await supabase
|
|
316
|
-
.from('project_shares')
|
|
317
|
-
.select('id')
|
|
318
|
-
.eq('project_id', project_id)
|
|
319
|
-
.eq('organization_id', organization_id)
|
|
320
|
-
.maybeSingle();
|
|
321
|
-
if (existing) {
|
|
322
|
-
throw new Error('Project is already shared with this organization');
|
|
167
|
+
const apiClient = getApiClient();
|
|
168
|
+
const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(response.error || 'Failed to share project');
|
|
323
171
|
}
|
|
324
|
-
|
|
325
|
-
.from('project_shares')
|
|
326
|
-
.insert({
|
|
327
|
-
project_id,
|
|
328
|
-
organization_id,
|
|
329
|
-
permission,
|
|
330
|
-
shared_by: auth.userId,
|
|
331
|
-
})
|
|
332
|
-
.select()
|
|
333
|
-
.single();
|
|
334
|
-
if (error)
|
|
335
|
-
throw new Error(`Failed to share project: ${error.message}`);
|
|
336
|
-
return {
|
|
337
|
-
result: {
|
|
338
|
-
success: true,
|
|
339
|
-
share: data,
|
|
340
|
-
message: `Project "${project.name}" shared with organization (${permission} access)`,
|
|
341
|
-
},
|
|
342
|
-
};
|
|
172
|
+
return { result: response.data };
|
|
343
173
|
};
|
|
344
174
|
export const updateProjectShare = async (args, ctx) => {
|
|
345
175
|
const { project_id, organization_id, permission } = args;
|
|
@@ -351,17 +181,12 @@ export const updateProjectShare = async (args, ctx) => {
|
|
|
351
181
|
if (!PERMISSION_ORDER.includes(permission)) {
|
|
352
182
|
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
353
183
|
}
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
.update
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.select()
|
|
361
|
-
.single();
|
|
362
|
-
if (error)
|
|
363
|
-
throw new Error(`Failed to update share: ${error.message}`);
|
|
364
|
-
return { result: { success: true, share: data } };
|
|
184
|
+
const apiClient = getApiClient();
|
|
185
|
+
const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
throw new Error(response.error || 'Failed to update share');
|
|
188
|
+
}
|
|
189
|
+
return { result: response.data };
|
|
365
190
|
};
|
|
366
191
|
export const unshareProject = async (args, ctx) => {
|
|
367
192
|
const { project_id, organization_id } = args;
|
|
@@ -369,44 +194,23 @@ export const unshareProject = async (args, ctx) => {
|
|
|
369
194
|
validateRequired(organization_id, 'organization_id');
|
|
370
195
|
validateUUID(project_id, 'project_id');
|
|
371
196
|
validateUUID(organization_id, 'organization_id');
|
|
372
|
-
const
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (error)
|
|
379
|
-
throw new Error(`Failed to unshare project: ${error.message}`);
|
|
380
|
-
return {
|
|
381
|
-
result: {
|
|
382
|
-
success: true,
|
|
383
|
-
message: 'Project share removed. Org members can no longer access this project.',
|
|
384
|
-
},
|
|
385
|
-
};
|
|
197
|
+
const apiClient = getApiClient();
|
|
198
|
+
const response = await apiClient.unshareProject(project_id, organization_id);
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(response.error || 'Failed to unshare project');
|
|
201
|
+
}
|
|
202
|
+
return { result: response.data };
|
|
386
203
|
};
|
|
387
204
|
export const listProjectShares = async (args, ctx) => {
|
|
388
205
|
const { project_id } = args;
|
|
389
206
|
validateRequired(project_id, 'project_id');
|
|
390
207
|
validateUUID(project_id, 'project_id');
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
shared_at,
|
|
398
|
-
shared_by,
|
|
399
|
-
organizations (
|
|
400
|
-
id,
|
|
401
|
-
name,
|
|
402
|
-
slug
|
|
403
|
-
)
|
|
404
|
-
`)
|
|
405
|
-
.eq('project_id', project_id)
|
|
406
|
-
.order('shared_at', { ascending: false });
|
|
407
|
-
if (error)
|
|
408
|
-
throw new Error(`Failed to list shares: ${error.message}`);
|
|
409
|
-
return { result: { shares: data || [], count: data?.length || 0 } };
|
|
208
|
+
const apiClient = getApiClient();
|
|
209
|
+
const response = await apiClient.listProjectShares(project_id);
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
throw new Error(response.error || 'Failed to list shares');
|
|
212
|
+
}
|
|
213
|
+
return { result: response.data };
|
|
410
214
|
};
|
|
411
215
|
/**
|
|
412
216
|
* Organizations handlers registry
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Handles progress logging and activity feed:
|
|
5
5
|
* - log_progress
|
|
6
6
|
* - get_activity_feed
|
|
7
|
+
*
|
|
8
|
+
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
7
9
|
*/
|
|
8
10
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
9
11
|
export declare const logProgress: Handler;
|