@vibescope/mcp-server 0.2.0 → 0.2.2
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 +60 -7
- package/dist/api-client.d.ts +251 -1
- package/dist/api-client.js +82 -3
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +96 -63
- package/dist/handlers/connectors.d.ts +45 -0
- package/dist/handlers/connectors.js +183 -0
- package/dist/handlers/cost.d.ts +10 -0
- package/dist/handlers/cost.js +112 -50
- package/dist/handlers/decisions.js +32 -19
- package/dist/handlers/deployment.js +144 -122
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +96 -7
- package/dist/handlers/fallback.js +29 -23
- package/dist/handlers/file-checkouts.d.ts +20 -0
- package/dist/handlers/file-checkouts.js +133 -0
- package/dist/handlers/findings.d.ts +6 -0
- package/dist/handlers/findings.js +96 -40
- package/dist/handlers/git-issues.js +40 -36
- package/dist/handlers/ideas.js +49 -31
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +9 -0
- package/dist/handlers/milestones.js +39 -32
- package/dist/handlers/organizations.js +99 -91
- package/dist/handlers/progress.js +24 -13
- package/dist/handlers/project.js +68 -28
- package/dist/handlers/requests.js +18 -14
- package/dist/handlers/roles.d.ts +18 -0
- package/dist/handlers/roles.js +130 -0
- package/dist/handlers/session.js +58 -17
- package/dist/handlers/sprints.js +93 -81
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +189 -91
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +21 -17
- package/dist/index.js +7 -2716
- package/dist/token-tracking.d.ts +74 -0
- package/dist/token-tracking.js +122 -0
- package/dist/tools.js +685 -9
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +17 -0
- package/docs/TOOLS.md +2053 -0
- package/package.json +4 -1
- package/scripts/generate-docs.ts +212 -0
- package/src/api-client.test.ts +718 -0
- package/src/api-client.ts +320 -6
- package/src/handlers/__test-setup__.ts +16 -0
- package/src/handlers/blockers.test.ts +31 -19
- package/src/handlers/blockers.ts +9 -8
- package/src/handlers/bodies-of-work.test.ts +55 -32
- package/src/handlers/bodies-of-work.ts +115 -115
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.test.ts +34 -44
- package/src/handlers/cost.ts +136 -85
- package/src/handlers/decisions.test.ts +37 -27
- package/src/handlers/decisions.ts +35 -30
- package/src/handlers/deployment.ts +180 -208
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +98 -8
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +36 -33
- package/src/handlers/file-checkouts.test.ts +670 -0
- package/src/handlers/file-checkouts.ts +165 -0
- package/src/handlers/findings.test.ts +178 -19
- package/src/handlers/findings.ts +112 -74
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +44 -84
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +61 -59
- package/src/handlers/index.ts +9 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +52 -50
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +117 -142
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +26 -24
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +95 -63
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +21 -17
- package/src/handlers/roles.test.ts +303 -0
- package/src/handlers/roles.ts +208 -0
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +71 -26
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +113 -146
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +231 -156
- package/src/handlers/tool-categories.test.ts +66 -0
- package/src/handlers/types.ts +81 -2
- package/src/handlers/validation.test.ts +78 -45
- package/src/handlers/validation.ts +23 -25
- package/src/index.ts +12 -2732
- package/src/token-tracking.test.ts +453 -0
- package/src/token-tracking.ts +164 -0
- package/src/tools.ts +685 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
- package/dist/config/tool-categories.d.ts +0 -31
- package/dist/config/tool-categories.js +0 -253
- package/dist/knowledge.d.ts +0 -6
- package/dist/knowledge.js +0 -218
package/dist/handlers/ideas.js
CHANGED
|
@@ -10,54 +10,78 @@
|
|
|
10
10
|
*
|
|
11
11
|
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { parseArgs, uuidValidator, priorityValidator, minutesValidator, createEnumValidator, } from '../validators.js';
|
|
14
14
|
import { getApiClient } from '../api-client.js';
|
|
15
|
+
const VALID_IDEA_STATUSES = ['raw', 'exploring', 'planned', 'in_development', 'shipped'];
|
|
16
|
+
// Argument schemas for type-safe parsing
|
|
17
|
+
const addIdeaSchema = {
|
|
18
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
19
|
+
title: { type: 'string', required: true },
|
|
20
|
+
description: { type: 'string' },
|
|
21
|
+
status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
|
|
22
|
+
};
|
|
23
|
+
const updateIdeaSchema = {
|
|
24
|
+
idea_id: { type: 'string', required: true, validate: uuidValidator },
|
|
25
|
+
title: { type: 'string' },
|
|
26
|
+
description: { type: 'string' },
|
|
27
|
+
status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
|
|
28
|
+
doc_url: { type: 'string' },
|
|
29
|
+
};
|
|
30
|
+
const getIdeasSchema = {
|
|
31
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
32
|
+
status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
|
|
33
|
+
limit: { type: 'number', default: 50 },
|
|
34
|
+
offset: { type: 'number', default: 0 },
|
|
35
|
+
search_query: { type: 'string' },
|
|
36
|
+
};
|
|
37
|
+
const deleteIdeaSchema = {
|
|
38
|
+
idea_id: { type: 'string', required: true, validate: uuidValidator },
|
|
39
|
+
};
|
|
40
|
+
const convertIdeaToTaskSchema = {
|
|
41
|
+
idea_id: { type: 'string', required: true, validate: uuidValidator },
|
|
42
|
+
priority: { type: 'number', default: 3, validate: priorityValidator },
|
|
43
|
+
estimated_minutes: { type: 'number', validate: minutesValidator },
|
|
44
|
+
update_status: { type: 'boolean', default: true },
|
|
45
|
+
};
|
|
15
46
|
export const addIdea = async (args, ctx) => {
|
|
16
|
-
const { project_id, title, description, status } = args;
|
|
17
|
-
validateRequired(project_id, 'project_id');
|
|
18
|
-
validateUUID(project_id, 'project_id');
|
|
19
|
-
validateRequired(title, 'title');
|
|
47
|
+
const { project_id, title, description, status } = parseArgs(args, addIdeaSchema);
|
|
20
48
|
const { session } = ctx;
|
|
21
49
|
const apiClient = getApiClient();
|
|
22
50
|
const response = await apiClient.addIdea(project_id, {
|
|
23
51
|
title,
|
|
24
52
|
description,
|
|
25
|
-
status
|
|
53
|
+
status: status
|
|
26
54
|
}, session.currentSessionId || undefined);
|
|
27
55
|
if (!response.ok) {
|
|
28
|
-
|
|
56
|
+
return { result: { error: response.error || 'Failed to add idea' }, isError: true };
|
|
29
57
|
}
|
|
30
58
|
return { result: { success: true, idea_id: response.data?.idea_id, title } };
|
|
31
59
|
};
|
|
32
|
-
export const updateIdea = async (args,
|
|
33
|
-
const { idea_id, title, description, status, doc_url } = args;
|
|
34
|
-
validateRequired(idea_id, 'idea_id');
|
|
35
|
-
validateUUID(idea_id, 'idea_id');
|
|
60
|
+
export const updateIdea = async (args, _ctx) => {
|
|
61
|
+
const { idea_id, title, description, status, doc_url } = parseArgs(args, updateIdeaSchema);
|
|
36
62
|
const apiClient = getApiClient();
|
|
37
63
|
const response = await apiClient.updateIdea(idea_id, {
|
|
38
64
|
title,
|
|
39
65
|
description,
|
|
40
|
-
status,
|
|
66
|
+
status: status,
|
|
41
67
|
doc_url
|
|
42
68
|
});
|
|
43
69
|
if (!response.ok) {
|
|
44
|
-
|
|
70
|
+
return { result: { error: response.error || 'Failed to update idea' }, isError: true };
|
|
45
71
|
}
|
|
46
72
|
return { result: { success: true, idea_id } };
|
|
47
73
|
};
|
|
48
|
-
export const getIdeas = async (args,
|
|
49
|
-
const { project_id, status, limit
|
|
50
|
-
validateRequired(project_id, 'project_id');
|
|
51
|
-
validateUUID(project_id, 'project_id');
|
|
74
|
+
export const getIdeas = async (args, _ctx) => {
|
|
75
|
+
const { project_id, status, limit, offset, search_query } = parseArgs(args, getIdeasSchema);
|
|
52
76
|
const apiClient = getApiClient();
|
|
53
77
|
const response = await apiClient.getIdeas(project_id, {
|
|
54
|
-
status,
|
|
78
|
+
status: status,
|
|
55
79
|
limit,
|
|
56
80
|
offset,
|
|
57
81
|
search_query
|
|
58
82
|
});
|
|
59
83
|
if (!response.ok) {
|
|
60
|
-
|
|
84
|
+
return { result: { error: response.error || 'Failed to fetch ideas' }, isError: true };
|
|
61
85
|
}
|
|
62
86
|
return {
|
|
63
87
|
result: {
|
|
@@ -65,23 +89,17 @@ export const getIdeas = async (args, ctx) => {
|
|
|
65
89
|
},
|
|
66
90
|
};
|
|
67
91
|
};
|
|
68
|
-
export const deleteIdea = async (args,
|
|
69
|
-
const { idea_id } = args;
|
|
70
|
-
validateRequired(idea_id, 'idea_id');
|
|
71
|
-
validateUUID(idea_id, 'idea_id');
|
|
92
|
+
export const deleteIdea = async (args, _ctx) => {
|
|
93
|
+
const { idea_id } = parseArgs(args, deleteIdeaSchema);
|
|
72
94
|
const apiClient = getApiClient();
|
|
73
95
|
const response = await apiClient.deleteIdea(idea_id);
|
|
74
96
|
if (!response.ok) {
|
|
75
|
-
|
|
97
|
+
return { result: { error: response.error || 'Failed to delete idea' }, isError: true };
|
|
76
98
|
}
|
|
77
99
|
return { result: { success: true } };
|
|
78
100
|
};
|
|
79
|
-
export const convertIdeaToTask = async (args,
|
|
80
|
-
const { idea_id, priority
|
|
81
|
-
validateRequired(idea_id, 'idea_id');
|
|
82
|
-
validateUUID(idea_id, 'idea_id');
|
|
83
|
-
validatePriority(priority);
|
|
84
|
-
validateEstimatedMinutes(estimated_minutes);
|
|
101
|
+
export const convertIdeaToTask = async (args, _ctx) => {
|
|
102
|
+
const { idea_id, priority, estimated_minutes, update_status } = parseArgs(args, convertIdeaToTaskSchema);
|
|
85
103
|
const apiClient = getApiClient();
|
|
86
104
|
// Use proxy for convert_idea_to_task operation
|
|
87
105
|
const response = await apiClient.proxy('convert_idea_to_task', {
|
|
@@ -91,7 +109,7 @@ export const convertIdeaToTask = async (args, ctx) => {
|
|
|
91
109
|
update_status
|
|
92
110
|
});
|
|
93
111
|
if (!response.ok) {
|
|
94
|
-
|
|
112
|
+
return { result: { error: response.error || 'Failed to convert idea' }, isError: true };
|
|
95
113
|
}
|
|
96
114
|
return { result: response.data };
|
|
97
115
|
};
|
package/dist/handlers/index.d.ts
CHANGED
|
@@ -24,6 +24,9 @@ export * from './organizations.js';
|
|
|
24
24
|
export * from './cost.js';
|
|
25
25
|
export * from './git-issues.js';
|
|
26
26
|
export * from './sprints.js';
|
|
27
|
+
export * from './file-checkouts.js';
|
|
28
|
+
export * from './roles.js';
|
|
29
|
+
export * from './connectors.js';
|
|
27
30
|
import type { HandlerRegistry } from './types.js';
|
|
28
31
|
/**
|
|
29
32
|
* Build the complete handler registry from all modules
|
package/dist/handlers/index.js
CHANGED
|
@@ -24,6 +24,9 @@ export * from './organizations.js';
|
|
|
24
24
|
export * from './cost.js';
|
|
25
25
|
export * from './git-issues.js';
|
|
26
26
|
export * from './sprints.js';
|
|
27
|
+
export * from './file-checkouts.js';
|
|
28
|
+
export * from './roles.js';
|
|
29
|
+
export * from './connectors.js';
|
|
27
30
|
import { milestoneHandlers } from './milestones.js';
|
|
28
31
|
import { sessionHandlers } from './session.js';
|
|
29
32
|
import { ideaHandlers } from './ideas.js';
|
|
@@ -43,6 +46,9 @@ import { organizationHandlers } from './organizations.js';
|
|
|
43
46
|
import { costHandlers } from './cost.js';
|
|
44
47
|
import { gitIssueHandlers } from './git-issues.js';
|
|
45
48
|
import { sprintHandlers } from './sprints.js';
|
|
49
|
+
import { fileCheckoutHandlers } from './file-checkouts.js';
|
|
50
|
+
import { roleHandlers } from './roles.js';
|
|
51
|
+
import { connectorHandlers } from './connectors.js';
|
|
46
52
|
/**
|
|
47
53
|
* Build the complete handler registry from all modules
|
|
48
54
|
*/
|
|
@@ -67,5 +73,8 @@ export function buildHandlerRegistry() {
|
|
|
67
73
|
...costHandlers,
|
|
68
74
|
...gitIssueHandlers,
|
|
69
75
|
...sprintHandlers,
|
|
76
|
+
...fileCheckoutHandlers,
|
|
77
|
+
...roleHandlers,
|
|
78
|
+
...connectorHandlers,
|
|
70
79
|
};
|
|
71
80
|
}
|
|
@@ -10,13 +10,34 @@
|
|
|
10
10
|
*
|
|
11
11
|
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { parseArgs, uuidValidator, createEnumValidator, ValidationError, } from '../validators.js';
|
|
14
14
|
import { getApiClient } from '../api-client.js';
|
|
15
|
+
const VALID_MILESTONE_STATUSES = ['pending', 'in_progress', 'completed'];
|
|
16
|
+
// Argument schemas for type-safe parsing
|
|
17
|
+
const addMilestoneSchema = {
|
|
18
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
19
|
+
title: { type: 'string', required: true },
|
|
20
|
+
description: { type: 'string' },
|
|
21
|
+
order_index: { type: 'number' },
|
|
22
|
+
};
|
|
23
|
+
const updateMilestoneSchema = {
|
|
24
|
+
milestone_id: { type: 'string', required: true, validate: uuidValidator },
|
|
25
|
+
title: { type: 'string' },
|
|
26
|
+
description: { type: 'string' },
|
|
27
|
+
status: { type: 'string', validate: createEnumValidator(VALID_MILESTONE_STATUSES) },
|
|
28
|
+
order_index: { type: 'number' },
|
|
29
|
+
};
|
|
30
|
+
const completeMilestoneSchema = {
|
|
31
|
+
milestone_id: { type: 'string', required: true, validate: uuidValidator },
|
|
32
|
+
};
|
|
33
|
+
const deleteMilestoneSchema = {
|
|
34
|
+
milestone_id: { type: 'string', required: true, validate: uuidValidator },
|
|
35
|
+
};
|
|
36
|
+
const getMilestonesSchema = {
|
|
37
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
38
|
+
};
|
|
15
39
|
export const addMilestone = async (args, ctx) => {
|
|
16
|
-
const { task_id, title, description, order_index } = args;
|
|
17
|
-
validateRequired(task_id, 'task_id');
|
|
18
|
-
validateUUID(task_id, 'task_id');
|
|
19
|
-
validateRequired(title, 'title');
|
|
40
|
+
const { task_id, title, description, order_index } = parseArgs(args, addMilestoneSchema);
|
|
20
41
|
const { session } = ctx;
|
|
21
42
|
const apiClient = getApiClient();
|
|
22
43
|
const response = await apiClient.addMilestone(task_id, {
|
|
@@ -25,7 +46,7 @@ export const addMilestone = async (args, ctx) => {
|
|
|
25
46
|
order_index
|
|
26
47
|
}, session.currentSessionId || undefined);
|
|
27
48
|
if (!response.ok) {
|
|
28
|
-
|
|
49
|
+
return { result: { error: response.error || 'Failed to add milestone' }, isError: true };
|
|
29
50
|
}
|
|
30
51
|
return {
|
|
31
52
|
result: {
|
|
@@ -34,16 +55,8 @@ export const addMilestone = async (args, ctx) => {
|
|
|
34
55
|
},
|
|
35
56
|
};
|
|
36
57
|
};
|
|
37
|
-
export const updateMilestone = async (args,
|
|
38
|
-
const { milestone_id, title, description, status, order_index } = args;
|
|
39
|
-
validateRequired(milestone_id, 'milestone_id');
|
|
40
|
-
validateUUID(milestone_id, 'milestone_id');
|
|
41
|
-
// Validate status if provided
|
|
42
|
-
if (status !== undefined) {
|
|
43
|
-
if (!['pending', 'in_progress', 'completed'].includes(status)) {
|
|
44
|
-
throw new ValidationError('status must be pending, in_progress, or completed');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
58
|
+
export const updateMilestone = async (args, _ctx) => {
|
|
59
|
+
const { milestone_id, title, description, status, order_index } = parseArgs(args, updateMilestoneSchema);
|
|
47
60
|
// Check that at least one field is provided
|
|
48
61
|
if (title === undefined && description === undefined && status === undefined && order_index === undefined) {
|
|
49
62
|
throw new ValidationError('At least one field to update is required');
|
|
@@ -56,7 +69,7 @@ export const updateMilestone = async (args, ctx) => {
|
|
|
56
69
|
order_index
|
|
57
70
|
});
|
|
58
71
|
if (!response.ok) {
|
|
59
|
-
|
|
72
|
+
return { result: { error: response.error || 'Failed to update milestone' }, isError: true };
|
|
60
73
|
}
|
|
61
74
|
return {
|
|
62
75
|
result: {
|
|
@@ -65,14 +78,12 @@ export const updateMilestone = async (args, ctx) => {
|
|
|
65
78
|
},
|
|
66
79
|
};
|
|
67
80
|
};
|
|
68
|
-
export const completeMilestone = async (args,
|
|
69
|
-
const { milestone_id } = args;
|
|
70
|
-
validateRequired(milestone_id, 'milestone_id');
|
|
71
|
-
validateUUID(milestone_id, 'milestone_id');
|
|
81
|
+
export const completeMilestone = async (args, _ctx) => {
|
|
82
|
+
const { milestone_id } = parseArgs(args, completeMilestoneSchema);
|
|
72
83
|
const apiClient = getApiClient();
|
|
73
84
|
const response = await apiClient.completeMilestone(milestone_id);
|
|
74
85
|
if (!response.ok) {
|
|
75
|
-
|
|
86
|
+
return { result: { error: response.error || 'Failed to complete milestone' }, isError: true };
|
|
76
87
|
}
|
|
77
88
|
return {
|
|
78
89
|
result: {
|
|
@@ -81,14 +92,12 @@ export const completeMilestone = async (args, ctx) => {
|
|
|
81
92
|
},
|
|
82
93
|
};
|
|
83
94
|
};
|
|
84
|
-
export const deleteMilestone = async (args,
|
|
85
|
-
const { milestone_id } = args;
|
|
86
|
-
validateRequired(milestone_id, 'milestone_id');
|
|
87
|
-
validateUUID(milestone_id, 'milestone_id');
|
|
95
|
+
export const deleteMilestone = async (args, _ctx) => {
|
|
96
|
+
const { milestone_id } = parseArgs(args, deleteMilestoneSchema);
|
|
88
97
|
const apiClient = getApiClient();
|
|
89
98
|
const response = await apiClient.deleteMilestone(milestone_id);
|
|
90
99
|
if (!response.ok) {
|
|
91
|
-
|
|
100
|
+
return { result: { error: response.error || 'Failed to delete milestone' }, isError: true };
|
|
92
101
|
}
|
|
93
102
|
return {
|
|
94
103
|
result: {
|
|
@@ -97,14 +106,12 @@ export const deleteMilestone = async (args, ctx) => {
|
|
|
97
106
|
},
|
|
98
107
|
};
|
|
99
108
|
};
|
|
100
|
-
export const getMilestones = async (args,
|
|
101
|
-
const { task_id } = args;
|
|
102
|
-
validateRequired(task_id, 'task_id');
|
|
103
|
-
validateUUID(task_id, 'task_id');
|
|
109
|
+
export const getMilestones = async (args, _ctx) => {
|
|
110
|
+
const { task_id } = parseArgs(args, getMilestonesSchema);
|
|
104
111
|
const apiClient = getApiClient();
|
|
105
112
|
const response = await apiClient.getMilestones(task_id);
|
|
106
113
|
if (!response.ok) {
|
|
107
|
-
|
|
114
|
+
return { result: { error: response.error || 'Failed to get milestones' }, isError: true };
|
|
108
115
|
}
|
|
109
116
|
// Stats are calculated server-side now
|
|
110
117
|
return {
|
|
@@ -16,26 +16,80 @@
|
|
|
16
16
|
* - unshare_project
|
|
17
17
|
* - list_project_shares
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
19
|
+
import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
|
|
20
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
|
+
const ASSIGNABLE_ROLES = ['viewer', 'member', 'admin'];
|
|
23
24
|
// Valid share permissions
|
|
24
25
|
const PERMISSION_ORDER = ['read', 'write', 'admin'];
|
|
25
26
|
// ============================================================================
|
|
27
|
+
// Argument Schemas
|
|
28
|
+
// ============================================================================
|
|
29
|
+
const createOrganizationSchema = {
|
|
30
|
+
name: { type: 'string', required: true },
|
|
31
|
+
description: { type: 'string' },
|
|
32
|
+
slug: { type: 'string' },
|
|
33
|
+
};
|
|
34
|
+
const updateOrganizationSchema = {
|
|
35
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
36
|
+
name: { type: 'string' },
|
|
37
|
+
description: { type: 'string' },
|
|
38
|
+
logo_url: { type: 'string' },
|
|
39
|
+
};
|
|
40
|
+
const deleteOrganizationSchema = {
|
|
41
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
42
|
+
};
|
|
43
|
+
const listOrgMembersSchema = {
|
|
44
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
45
|
+
};
|
|
46
|
+
const inviteMemberSchema = {
|
|
47
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
48
|
+
email: { type: 'string', required: true },
|
|
49
|
+
role: { type: 'string', default: 'member', validate: createEnumValidator(ASSIGNABLE_ROLES) },
|
|
50
|
+
};
|
|
51
|
+
const updateMemberRoleSchema = {
|
|
52
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
53
|
+
user_id: { type: 'string', required: true, validate: uuidValidator },
|
|
54
|
+
role: { type: 'string', required: true, validate: createEnumValidator(ROLE_ORDER) },
|
|
55
|
+
};
|
|
56
|
+
const removeMemberSchema = {
|
|
57
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
58
|
+
user_id: { type: 'string', required: true, validate: uuidValidator },
|
|
59
|
+
};
|
|
60
|
+
const leaveOrganizationSchema = {
|
|
61
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
62
|
+
};
|
|
63
|
+
const shareProjectWithOrgSchema = {
|
|
64
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
65
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
66
|
+
permission: { type: 'string', default: 'read', validate: createEnumValidator(PERMISSION_ORDER) },
|
|
67
|
+
};
|
|
68
|
+
const updateProjectShareSchema = {
|
|
69
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
70
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
71
|
+
permission: { type: 'string', required: true, validate: createEnumValidator(PERMISSION_ORDER) },
|
|
72
|
+
};
|
|
73
|
+
const unshareProjectSchema = {
|
|
74
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
75
|
+
organization_id: { type: 'string', required: true, validate: uuidValidator },
|
|
76
|
+
};
|
|
77
|
+
const listProjectSharesSchema = {
|
|
78
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
79
|
+
};
|
|
80
|
+
// ============================================================================
|
|
26
81
|
// Organization Management
|
|
27
82
|
// ============================================================================
|
|
28
|
-
export const listOrganizations = async (_args,
|
|
83
|
+
export const listOrganizations = async (_args, _ctx) => {
|
|
29
84
|
const apiClient = getApiClient();
|
|
30
85
|
const response = await apiClient.listOrganizations();
|
|
31
86
|
if (!response.ok) {
|
|
32
|
-
|
|
87
|
+
return { result: { error: response.error || 'Failed to list organizations' }, isError: true };
|
|
33
88
|
}
|
|
34
89
|
return { result: response.data };
|
|
35
90
|
};
|
|
36
|
-
export const createOrganization = async (args,
|
|
37
|
-
const { name, description, slug } = args;
|
|
38
|
-
validateRequired(name, 'name');
|
|
91
|
+
export const createOrganization = async (args, _ctx) => {
|
|
92
|
+
const { name, description, slug } = parseArgs(args, createOrganizationSchema);
|
|
39
93
|
const apiClient = getApiClient();
|
|
40
94
|
const response = await apiClient.createOrganization({
|
|
41
95
|
name,
|
|
@@ -43,14 +97,16 @@ export const createOrganization = async (args, ctx) => {
|
|
|
43
97
|
slug
|
|
44
98
|
});
|
|
45
99
|
if (!response.ok) {
|
|
46
|
-
|
|
100
|
+
return { result: { error: response.error || 'Failed to create organization' }, isError: true };
|
|
47
101
|
}
|
|
48
102
|
return { result: response.data };
|
|
49
103
|
};
|
|
50
|
-
export const updateOrganization = async (args,
|
|
51
|
-
const { organization_id, name, description, logo_url } = args;
|
|
52
|
-
|
|
53
|
-
|
|
104
|
+
export const updateOrganization = async (args, _ctx) => {
|
|
105
|
+
const { organization_id, name, description, logo_url } = parseArgs(args, updateOrganizationSchema);
|
|
106
|
+
// Check that at least one update is provided
|
|
107
|
+
if (name === undefined && description === undefined && logo_url === undefined) {
|
|
108
|
+
throw new ValidationError('No updates provided');
|
|
109
|
+
}
|
|
54
110
|
const updates = {};
|
|
55
111
|
if (name !== undefined)
|
|
56
112
|
updates.name = name;
|
|
@@ -58,157 +114,109 @@ export const updateOrganization = async (args, ctx) => {
|
|
|
58
114
|
updates.description = description;
|
|
59
115
|
if (logo_url !== undefined)
|
|
60
116
|
updates.logo_url = logo_url;
|
|
61
|
-
if (Object.keys(updates).length === 0) {
|
|
62
|
-
throw new Error('No updates provided');
|
|
63
|
-
}
|
|
64
117
|
const apiClient = getApiClient();
|
|
65
118
|
const response = await apiClient.updateOrganization(organization_id, updates);
|
|
66
119
|
if (!response.ok) {
|
|
67
|
-
|
|
120
|
+
return { result: { error: response.error || 'Failed to update organization' }, isError: true };
|
|
68
121
|
}
|
|
69
122
|
return { result: response.data };
|
|
70
123
|
};
|
|
71
|
-
export const deleteOrganization = async (args,
|
|
72
|
-
const { organization_id } = args;
|
|
73
|
-
validateRequired(organization_id, 'organization_id');
|
|
74
|
-
validateUUID(organization_id, 'organization_id');
|
|
124
|
+
export const deleteOrganization = async (args, _ctx) => {
|
|
125
|
+
const { organization_id } = parseArgs(args, deleteOrganizationSchema);
|
|
75
126
|
const apiClient = getApiClient();
|
|
76
127
|
const response = await apiClient.deleteOrganization(organization_id);
|
|
77
128
|
if (!response.ok) {
|
|
78
|
-
|
|
129
|
+
return { result: { error: response.error || 'Failed to delete organization' }, isError: true };
|
|
79
130
|
}
|
|
80
131
|
return { result: response.data };
|
|
81
132
|
};
|
|
82
133
|
// ============================================================================
|
|
83
134
|
// Member Management
|
|
84
135
|
// ============================================================================
|
|
85
|
-
export const listOrgMembers = async (args,
|
|
86
|
-
const { organization_id } = args;
|
|
87
|
-
validateRequired(organization_id, 'organization_id');
|
|
88
|
-
validateUUID(organization_id, 'organization_id');
|
|
136
|
+
export const listOrgMembers = async (args, _ctx) => {
|
|
137
|
+
const { organization_id } = parseArgs(args, listOrgMembersSchema);
|
|
89
138
|
const apiClient = getApiClient();
|
|
90
139
|
const response = await apiClient.listOrgMembers(organization_id);
|
|
91
140
|
if (!response.ok) {
|
|
92
|
-
|
|
141
|
+
return { result: { error: response.error || 'Failed to list members' }, isError: true };
|
|
93
142
|
}
|
|
94
143
|
return { result: response.data };
|
|
95
144
|
};
|
|
96
|
-
export const inviteMember = async (args,
|
|
97
|
-
const { organization_id, email, role
|
|
98
|
-
validateRequired(organization_id, 'organization_id');
|
|
99
|
-
validateRequired(email, 'email');
|
|
100
|
-
validateUUID(organization_id, 'organization_id');
|
|
101
|
-
if (!['admin', 'member', 'viewer'].includes(role)) {
|
|
102
|
-
throw new Error('Invalid role. Must be admin, member, or viewer.');
|
|
103
|
-
}
|
|
145
|
+
export const inviteMember = async (args, _ctx) => {
|
|
146
|
+
const { organization_id, email, role } = parseArgs(args, inviteMemberSchema);
|
|
104
147
|
const apiClient = getApiClient();
|
|
105
148
|
const response = await apiClient.inviteMember(organization_id, email, role);
|
|
106
149
|
if (!response.ok) {
|
|
107
|
-
|
|
150
|
+
return { result: { error: response.error || 'Failed to create invite' }, isError: true };
|
|
108
151
|
}
|
|
109
152
|
return { result: response.data };
|
|
110
153
|
};
|
|
111
|
-
export const updateMemberRole = async (args,
|
|
112
|
-
const { organization_id, user_id, role } = args;
|
|
113
|
-
validateRequired(organization_id, 'organization_id');
|
|
114
|
-
validateRequired(user_id, 'user_id');
|
|
115
|
-
validateRequired(role, 'role');
|
|
116
|
-
validateUUID(organization_id, 'organization_id');
|
|
117
|
-
validateUUID(user_id, 'user_id');
|
|
118
|
-
if (!ROLE_ORDER.includes(role)) {
|
|
119
|
-
throw new Error(`Invalid role. Must be one of: ${ROLE_ORDER.join(', ')}`);
|
|
120
|
-
}
|
|
154
|
+
export const updateMemberRole = async (args, _ctx) => {
|
|
155
|
+
const { organization_id, user_id, role } = parseArgs(args, updateMemberRoleSchema);
|
|
121
156
|
if (role === 'owner') {
|
|
122
|
-
throw new
|
|
157
|
+
throw new ValidationError('Cannot assign owner role. Use transfer ownership instead.');
|
|
123
158
|
}
|
|
124
159
|
const apiClient = getApiClient();
|
|
125
160
|
const response = await apiClient.updateMemberRole(organization_id, user_id, role);
|
|
126
161
|
if (!response.ok) {
|
|
127
|
-
|
|
162
|
+
return { result: { error: response.error || 'Failed to update member role' }, isError: true };
|
|
128
163
|
}
|
|
129
164
|
return { result: response.data };
|
|
130
165
|
};
|
|
131
|
-
export const removeMember = async (args,
|
|
132
|
-
const { organization_id, user_id } = args;
|
|
133
|
-
validateRequired(organization_id, 'organization_id');
|
|
134
|
-
validateRequired(user_id, 'user_id');
|
|
135
|
-
validateUUID(organization_id, 'organization_id');
|
|
136
|
-
validateUUID(user_id, 'user_id');
|
|
166
|
+
export const removeMember = async (args, _ctx) => {
|
|
167
|
+
const { organization_id, user_id } = parseArgs(args, removeMemberSchema);
|
|
137
168
|
const apiClient = getApiClient();
|
|
138
169
|
const response = await apiClient.removeMember(organization_id, user_id);
|
|
139
170
|
if (!response.ok) {
|
|
140
|
-
|
|
171
|
+
return { result: { error: response.error || 'Failed to remove member' }, isError: true };
|
|
141
172
|
}
|
|
142
173
|
return { result: response.data };
|
|
143
174
|
};
|
|
144
|
-
export const leaveOrganization = async (args,
|
|
145
|
-
const { organization_id } = args;
|
|
146
|
-
validateRequired(organization_id, 'organization_id');
|
|
147
|
-
validateUUID(organization_id, 'organization_id');
|
|
175
|
+
export const leaveOrganization = async (args, _ctx) => {
|
|
176
|
+
const { organization_id } = parseArgs(args, leaveOrganizationSchema);
|
|
148
177
|
const apiClient = getApiClient();
|
|
149
178
|
const response = await apiClient.leaveOrganization(organization_id);
|
|
150
179
|
if (!response.ok) {
|
|
151
|
-
|
|
180
|
+
return { result: { error: response.error || 'Failed to leave organization' }, isError: true };
|
|
152
181
|
}
|
|
153
182
|
return { result: response.data };
|
|
154
183
|
};
|
|
155
184
|
// ============================================================================
|
|
156
185
|
// Project Sharing
|
|
157
186
|
// ============================================================================
|
|
158
|
-
export const shareProjectWithOrg = async (args,
|
|
159
|
-
const { project_id, organization_id, permission
|
|
160
|
-
validateRequired(project_id, 'project_id');
|
|
161
|
-
validateRequired(organization_id, 'organization_id');
|
|
162
|
-
validateUUID(project_id, 'project_id');
|
|
163
|
-
validateUUID(organization_id, 'organization_id');
|
|
164
|
-
if (!PERMISSION_ORDER.includes(permission)) {
|
|
165
|
-
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
166
|
-
}
|
|
187
|
+
export const shareProjectWithOrg = async (args, _ctx) => {
|
|
188
|
+
const { project_id, organization_id, permission } = parseArgs(args, shareProjectWithOrgSchema);
|
|
167
189
|
const apiClient = getApiClient();
|
|
168
190
|
const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
|
|
169
191
|
if (!response.ok) {
|
|
170
|
-
|
|
192
|
+
return { result: { error: response.error || 'Failed to share project' }, isError: true };
|
|
171
193
|
}
|
|
172
194
|
return { result: response.data };
|
|
173
195
|
};
|
|
174
|
-
export const updateProjectShare = async (args,
|
|
175
|
-
const { project_id, organization_id, permission } = args;
|
|
176
|
-
validateRequired(project_id, 'project_id');
|
|
177
|
-
validateRequired(organization_id, 'organization_id');
|
|
178
|
-
validateRequired(permission, 'permission');
|
|
179
|
-
validateUUID(project_id, 'project_id');
|
|
180
|
-
validateUUID(organization_id, 'organization_id');
|
|
181
|
-
if (!PERMISSION_ORDER.includes(permission)) {
|
|
182
|
-
throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
|
|
183
|
-
}
|
|
196
|
+
export const updateProjectShare = async (args, _ctx) => {
|
|
197
|
+
const { project_id, organization_id, permission } = parseArgs(args, updateProjectShareSchema);
|
|
184
198
|
const apiClient = getApiClient();
|
|
185
199
|
const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
|
|
186
200
|
if (!response.ok) {
|
|
187
|
-
|
|
201
|
+
return { result: { error: response.error || 'Failed to update share' }, isError: true };
|
|
188
202
|
}
|
|
189
203
|
return { result: response.data };
|
|
190
204
|
};
|
|
191
|
-
export const unshareProject = async (args,
|
|
192
|
-
const { project_id, organization_id } = args;
|
|
193
|
-
validateRequired(project_id, 'project_id');
|
|
194
|
-
validateRequired(organization_id, 'organization_id');
|
|
195
|
-
validateUUID(project_id, 'project_id');
|
|
196
|
-
validateUUID(organization_id, 'organization_id');
|
|
205
|
+
export const unshareProject = async (args, _ctx) => {
|
|
206
|
+
const { project_id, organization_id } = parseArgs(args, unshareProjectSchema);
|
|
197
207
|
const apiClient = getApiClient();
|
|
198
208
|
const response = await apiClient.unshareProject(project_id, organization_id);
|
|
199
209
|
if (!response.ok) {
|
|
200
|
-
|
|
210
|
+
return { result: { error: response.error || 'Failed to unshare project' }, isError: true };
|
|
201
211
|
}
|
|
202
212
|
return { result: response.data };
|
|
203
213
|
};
|
|
204
|
-
export const listProjectShares = async (args,
|
|
205
|
-
const { project_id } = args;
|
|
206
|
-
validateRequired(project_id, 'project_id');
|
|
207
|
-
validateUUID(project_id, 'project_id');
|
|
214
|
+
export const listProjectShares = async (args, _ctx) => {
|
|
215
|
+
const { project_id } = parseArgs(args, listProjectSharesSchema);
|
|
208
216
|
const apiClient = getApiClient();
|
|
209
217
|
const response = await apiClient.listProjectShares(project_id);
|
|
210
218
|
if (!response.ok) {
|
|
211
|
-
|
|
219
|
+
return { result: { error: response.error || 'Failed to list shares' }, isError: true };
|
|
212
220
|
}
|
|
213
221
|
return { result: response.data };
|
|
214
222
|
};
|