@we-scrum/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@we-scrum/cli",
3
+ "version": "1.0.0",
4
+ "description": "Cli tool for we-scrum application",
5
+ "main": "dist/cli.js",
6
+ "bin": {
7
+ "we-scrum": "dist/cli.js"
8
+ },
9
+ "tsup": {
10
+ "entry": [
11
+ "src/cli.ts"
12
+ ],
13
+ "format": [
14
+ "cjs"
15
+ ],
16
+ "clean": true,
17
+ "minify": true,
18
+ "bundle": true,
19
+ "keepNames": true,
20
+ "noExternal": [
21
+ "@we-scrum/*",
22
+ "@my-devkit/*"
23
+ ]
24
+ },
25
+ "dependencies": {
26
+ "@inquirer/prompts": "^8.3.0",
27
+ "@modelcontextprotocol/sdk": "^1.26.0",
28
+ "commander": "^11.1.0",
29
+ "firebase": "12.10.0",
30
+ "google-auth-library": "^10.6.1",
31
+ "open": "^11.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "22.18.6",
35
+ "tsup": "8.5.1",
36
+ "typescript": "5.9.3",
37
+ "@my-devkit/cli": "2.1.0",
38
+ "@my-devkit/core": "1.0.0",
39
+ "@we-scrum/enums": "1.0.0",
40
+ "@we-scrum/models": "1.0.0",
41
+ "@we-scrum/commands": "1.0.0"
42
+ },
43
+ "scripts": {
44
+ "build": "mdk build",
45
+ "watch": "mdk watch",
46
+ "build:prod": "tsup",
47
+ "publish:public": "pnpm publish --access public",
48
+ "preinstall": "npx only-allow pnpm",
49
+ "global:link": "pnpm link --global",
50
+ "global:unlink": "pnpm remove --global @we-scrum/cli"
51
+ }
52
+ }
@@ -0,0 +1,107 @@
1
+ import { Logger } from '@my-devkit/core';
2
+ import { GoogleAuthProvider, signInWithCredential, User } from 'firebase/auth';
3
+ import { CodeChallengeMethod, OAuth2Client } from 'google-auth-library';
4
+ import { createHash, randomBytes } from 'node:crypto';
5
+ import { createServer } from 'node:http';
6
+ import open from 'open';
7
+
8
+ import { FirebaseHelper } from './firebase-helper';
9
+ import { SettingsHelper } from './settings-helper';
10
+
11
+ export class AuthHelper {
12
+ // 2. Google OAuth2 Config
13
+ private static cid = '847201088225-vi6fq1rjoouoe24ac9oalst4nis0r597.apps.googleusercontent.com';
14
+ private static csec = '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'; // Note: This is a Public Client Secret for a CLI tool, safe to expose per RFC 8252.
15
+ private static PORT = 3000;
16
+ private static REDIRECT_URI = `http://localhost:${this.PORT}`;
17
+ public static get userId(): string {
18
+ return FirebaseHelper.auth.currentUser?.uid;
19
+ }
20
+
21
+ public static async loginWithGoogle(): Promise<User> {
22
+ const auth = FirebaseHelper.auth;
23
+
24
+ const { verifier, challenge } = this.generatePKCE();
25
+ const oAuth2Client = new OAuth2Client(this.cid, this.csec, this.REDIRECT_URI);
26
+
27
+ return new Promise((resolve, reject) => {
28
+ // Create a temporary local server to catch the redirect
29
+ const server = createServer(async (req, res) => {
30
+ try {
31
+ const url = new URL(req.url, this.REDIRECT_URI);
32
+ const code = url.searchParams.get('code');
33
+
34
+ if (code) {
35
+ // Send a friendly message to the browser
36
+ res.writeHead(200, { 'Content-Type': 'text/html' });
37
+ res.end('<h1>Login Successful!</h1><p>You can close this tab and return to the terminal.</p>');
38
+
39
+ // Stop listening
40
+ server.close();
41
+
42
+ // Exchange the Authorization Code for tokens using the PKCE Verifier
43
+ const { tokens } = await oAuth2Client.getToken({
44
+ code: code,
45
+ codeVerifier: verifier,
46
+ });
47
+
48
+ // Persist credentials in settings
49
+ SettingsHelper.set({ credentials: tokens });
50
+
51
+ // Build Firebase credential and sign in
52
+ const credential = GoogleAuthProvider.credential(tokens.id_token);
53
+
54
+ const userCredential = await signInWithCredential(auth, credential);
55
+
56
+ resolve(userCredential.user);
57
+ }
58
+ } catch (err) {
59
+ res.end('Authentication failed.');
60
+ server.close();
61
+ reject(err);
62
+ }
63
+ });
64
+
65
+ server.listen(this.PORT, () => {
66
+ // 4. Generate the Auth URL with the PKCE Challenge
67
+ const authUrl = oAuth2Client.generateAuthUrl({
68
+ access_type: 'offline', // Required to get a refresh_token
69
+ scope: ['openid', 'email', 'profile'],
70
+ code_challenge: challenge,
71
+ code_challenge_method: CodeChallengeMethod.S256,
72
+ });
73
+
74
+ Logger.info('Opening your browser for authentication...');
75
+ open(authUrl);
76
+ });
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Generates PKCE Verifier and Challenge
82
+ */
83
+ private static generatePKCE() {
84
+ const verifier = randomBytes(32).toString('base64url');
85
+ const challenge = createHash('sha256').update(verifier).digest('base64url');
86
+ return { verifier, challenge };
87
+ }
88
+
89
+ public static logout() {
90
+ SettingsHelper.set({ credentials: undefined });
91
+ }
92
+
93
+ public static async initializeFromStoredCredentials(): Promise<boolean> {
94
+ const credentials = SettingsHelper.get().credentials ?? null;
95
+ if (!credentials?.id_token) {
96
+ return false;
97
+ }
98
+ try {
99
+ const auth = FirebaseHelper.auth;
100
+ const credential = GoogleAuthProvider.credential(credentials.id_token);
101
+ await signInWithCredential(auth, credential);
102
+ return true;
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ import { select } from '@inquirer/prompts';
3
+ import { Logger } from '@my-devkit/core';
4
+ import { UserProjectModel } from '@we-scrum/models';
5
+ import { Command } from 'commander';
6
+
7
+ import { AuthHelper } from './auth-helper';
8
+ import { FirebaseHelper } from './firebase-helper';
9
+ import { startMcpServer } from './mcp-server';
10
+ import { SettingsHelper } from './settings-helper';
11
+
12
+ Logger.registerLogger(new Logger.ConsoleImplementation());
13
+
14
+ const program = new Command();
15
+
16
+ program.name('we-scrum').description('CLI for we-scrum application').version('1.0.0');
17
+
18
+ program
19
+ .command('login')
20
+ .description('Authenticate with your Google account')
21
+ .action(async () => {
22
+ try {
23
+ await AuthHelper.loginWithGoogle();
24
+ process.exit(0);
25
+ } catch (error) {
26
+ Logger.error(error?.message ?? 'Login failed');
27
+ process.exit(1);
28
+ }
29
+ });
30
+
31
+ program
32
+ .command('logout')
33
+ .description('Log out and remove stored credentials')
34
+ .action(() => {
35
+ AuthHelper.logout();
36
+ Logger.info('Logged out successfully.');
37
+ process.exit(0);
38
+ });
39
+
40
+ program
41
+ .command('use-project')
42
+ .description('Select the active project')
43
+ .option('--projectId <id>', 'Set the active project directly without the interactive prompt')
44
+ .action(async (options: { projectId?: string }) => {
45
+ try {
46
+ const initialized = await AuthHelper.initializeFromStoredCredentials();
47
+ if (!initialized) {
48
+ Logger.error('Not authenticated. Please run "we-scrum login" first.');
49
+ process.exit(1);
50
+ }
51
+
52
+ const projects = await FirebaseHelper.getCollection<UserProjectModel>(`users/${AuthHelper.userId}/projects`);
53
+ if (projects.length === 0) {
54
+ Logger.error('No projects found for your account.');
55
+ process.exit(1);
56
+ }
57
+
58
+ let selectedProjectId: string;
59
+
60
+ if (options.projectId) {
61
+ const match = projects.find((p) => p.projectId === options.projectId);
62
+ if (!match) {
63
+ Logger.error(`Project "${options.projectId}" not found in your account.`);
64
+ process.exit(1);
65
+ }
66
+ selectedProjectId = match.projectId;
67
+ } else {
68
+ selectedProjectId = await select({
69
+ message: 'Select a project',
70
+ default: SettingsHelper.get().selectedProjectId,
71
+ choices: projects.map((p) => ({
72
+ name: p.name,
73
+ value: p.projectId,
74
+ description: p.projectId,
75
+ })),
76
+ });
77
+ }
78
+
79
+ SettingsHelper.set({ selectedProjectId });
80
+ Logger.info(`Active project set to: ${projects.find((p) => p.projectId === selectedProjectId)?.name}`);
81
+ process.exit(0);
82
+ } catch (error) {
83
+ if (error?.name === 'ExitPromptError') {
84
+ process.exit(0);
85
+ }
86
+ Logger.error(error?.message ?? 'Failed to select project');
87
+ process.exit(1);
88
+ }
89
+ });
90
+
91
+ program
92
+ .command('mcp')
93
+ .description('Start the we-scrum MCP server (stdio transport)')
94
+ .option('--projectId <id>', 'Override the active project for this session')
95
+ .action(async (options: { projectId?: string }) => {
96
+ if (options.projectId) {
97
+ SettingsHelper.set({ selectedProjectId: options.projectId });
98
+ }
99
+ await startMcpServer();
100
+ });
101
+
102
+ program.parse(process.argv);
@@ -0,0 +1,129 @@
1
+ import { deserialize } from '@my-devkit/core';
2
+ import * as models from '@we-scrum/models';
3
+ import { FirebaseApp, initializeApp } from 'firebase/app';
4
+ import { Auth, getAuth } from 'firebase/auth';
5
+ import {
6
+ DocumentData,
7
+ FieldPath,
8
+ Firestore,
9
+ OrderByDirection,
10
+ Query,
11
+ QueryCompositeFilterConstraint,
12
+ QueryNonFilterConstraint,
13
+ WhereFilterOp,
14
+ and,
15
+ collection,
16
+ doc,
17
+ getDoc,
18
+ getDocs,
19
+ getFirestore,
20
+ limit,
21
+ orderBy,
22
+ query,
23
+ where,
24
+ } from 'firebase/firestore';
25
+
26
+ for (const i in models) {
27
+ new models[i]();
28
+ }
29
+
30
+ export class FirebaseHelper {
31
+ private static _app: FirebaseApp;
32
+ private static _auth: Auth;
33
+ private static _firestore: Firestore;
34
+
35
+ public static get auth(): Auth {
36
+ if (!this._auth) {
37
+ this._auth = getAuth(FirebaseHelper.app);
38
+ }
39
+
40
+ return this._auth;
41
+ }
42
+
43
+ public static async getCollection<T>(path: string, options?: FirebaseHelper.Query): Promise<T[]> {
44
+ const q = this.query(path, options);
45
+
46
+ const documents = await getDocs(q);
47
+
48
+ return documents.docs.map((doc) => deserialize<T>(doc.data()));
49
+ }
50
+
51
+ public static async findDocument<T>(path: string, where: FirebaseHelper.Where[]): Promise<T> {
52
+ const documents = await this.getCollection<T>(path, { where });
53
+
54
+ if (documents.length === 0) {
55
+ return null;
56
+ }
57
+
58
+ if (documents.length > 1) {
59
+ throw new Error(`More than one document found at ${path} where ${where.map((w) => JSON.stringify(w)).join(' AND ')}`);
60
+ }
61
+
62
+ return documents[0];
63
+ }
64
+
65
+ public static async getDocument<T>(path: string): Promise<T | null> {
66
+ const docRef = doc(this.firestore, path);
67
+ const docSnapshot = await getDoc(docRef);
68
+
69
+ if (!docSnapshot.exists()) {
70
+ throw new Error(`Document ${path} not found`);
71
+ }
72
+
73
+ return deserialize<T>(docSnapshot.data());
74
+ }
75
+
76
+ private static get app() {
77
+ if (!this._app) {
78
+ // Should depend on targeted environment
79
+ const firebaseConfig = {
80
+ apiKey: 'AIzaSyCodItNw1wtWfx1GAU_x0Pj96mCAUyrWvU',
81
+ authDomain: 'we-scrum-prod.firebaseapp.com',
82
+ projectId: 'we-scrum-prod',
83
+ };
84
+
85
+ this._app = initializeApp(firebaseConfig);
86
+ }
87
+
88
+ return this._app;
89
+ }
90
+
91
+ private static get firestore() {
92
+ if (!this._firestore) {
93
+ this._firestore = getFirestore(this.app);
94
+ }
95
+
96
+ return this._firestore;
97
+ }
98
+
99
+ private static query(path: string, options?: FirebaseHelper.Query): Query<DocumentData, DocumentData> {
100
+ const ref = collection(this.firestore, path);
101
+
102
+ let filter: QueryCompositeFilterConstraint;
103
+ if (options?.where) {
104
+ filter = and(...options.where.map((w) => where(...w)));
105
+ }
106
+
107
+ const constraints: QueryNonFilterConstraint[] = [];
108
+ if (options?.orderBy) {
109
+ constraints.push(orderBy(...options.orderBy));
110
+ }
111
+
112
+ if (options?.limit) {
113
+ constraints.push(limit(options.limit));
114
+ }
115
+
116
+ return query(ref, filter, ...constraints);
117
+ }
118
+ }
119
+
120
+ export namespace FirebaseHelper {
121
+ export interface Query {
122
+ where?: Where[];
123
+ orderBy?: OrderBy;
124
+ limit?: number;
125
+ }
126
+
127
+ export type Where = [string | FieldPath, opStr: WhereFilterOp, value: unknown];
128
+ type OrderBy = [string | FieldPath, OrderByDirection?];
129
+ }
@@ -0,0 +1,127 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { ProgressStatus } from '@we-scrum/enums';
5
+ import { ProjectStoryModel, ProjectStorySectionModel } from '@we-scrum/models';
6
+
7
+ import { AuthHelper } from './auth-helper';
8
+ import { FirebaseHelper } from './firebase-helper';
9
+ import { SettingsHelper } from './settings-helper';
10
+
11
+ const TOOLS = [
12
+ {
13
+ name: 'get_next_task',
14
+ description: `Get the next pending task (status: ToDo) for a story.
15
+ Returns the task description, the task type template (development guidelines),
16
+ and the full section analysis specifying what must be implemented
17
+ (route changes, object/DTO changes, enumeration changes, relation/handler changes).
18
+ Also returns storyId, sectionId and taskId needed to call start_task and complete_task.`,
19
+ inputSchema: {
20
+ type: 'object',
21
+ properties: {
22
+ identificationNumber: {
23
+ type: 'string',
24
+ description: 'The story identification number, e.g. "144154"',
25
+ },
26
+ },
27
+ required: ['identificationNumber'],
28
+ },
29
+ },
30
+ ];
31
+
32
+ async function handleGetNextTask(identificationNumber: string): Promise<string> {
33
+ const projectId = SettingsHelper.get().selectedProjectId;
34
+ if (!projectId) {
35
+ throw new Error('No active project selected. Please run "we-scrum use-project" first.');
36
+ }
37
+
38
+ // 1. Find story by identificationNumber
39
+ const story = await FirebaseHelper.findDocument<ProjectStoryModel>(`/projects/${projectId}/stories`, [
40
+ ['identificationNumber', '==', identificationNumber],
41
+ ]);
42
+
43
+ if (!story) {
44
+ throw new Error(`Story "${identificationNumber}" not found in the active project.`);
45
+ }
46
+
47
+ // 2. Fetch all section documents at once
48
+ const sections = await FirebaseHelper.getCollection<ProjectStorySectionModel>(
49
+ `projects/${projectId}/stories/${story.storyId}/sections`,
50
+ );
51
+
52
+ // 3. Order sections using the position stored in story.sections[]
53
+ const positionBySection = new Map(story.sections.map((s) => [s.sectionId, s.position ?? 0]));
54
+ const sortedSections = sections.sort((a, b) => (positionBySection.get(a.sectionId) ?? 0) - (positionBySection.get(b.sectionId) ?? 0));
55
+
56
+ // 4. Walk sections in order and find the first ToDo task
57
+ for (const section of sortedSections) {
58
+ const tasks = [...(section.tasks ?? [])].sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
59
+ const nextTask = tasks.find((t) => t.progress?.status === ProgressStatus.ToDo);
60
+
61
+ if (!nextTask) continue;
62
+
63
+ const result = {
64
+ storyId: story.storyId,
65
+ storyName: story.name,
66
+ sectionId: section.sectionId,
67
+ sectionName: section.name,
68
+ task: {
69
+ taskId: nextTask.taskId,
70
+ description: nextTask.description,
71
+ taskTypeName: nextTask.taskTypeName,
72
+ position: nextTask.position,
73
+ status: nextTask.progress?.status,
74
+ },
75
+ sectionAnalysis: {
76
+ ...(section.routeChanges?.length && { routeChanges: section.routeChanges }),
77
+ ...(section.objectChanges?.length && { objectChanges: section.objectChanges }),
78
+ ...(section.enumerationChanges?.length && { enumerationChanges: section.enumerationChanges }),
79
+ ...(section.relationChanges?.length && { relationChanges: section.relationChanges }),
80
+ },
81
+ };
82
+
83
+ return JSON.stringify(result, null, 2);
84
+ }
85
+
86
+ return JSON.stringify({ message: `All tasks for story "${identificationNumber}" are complete.` });
87
+ }
88
+
89
+ export async function startMcpServer(): Promise<void> {
90
+ const initialized = await AuthHelper.initializeFromStoredCredentials();
91
+ if (!initialized) {
92
+ process.stderr.write('Error: Not authenticated. Please run "we-scrum login" first.\n');
93
+ process.exit(1);
94
+ }
95
+
96
+ const server = new Server({ name: 'we-scrum-mcp-server', version: '1.0.0' }, { capabilities: { tools: {} } });
97
+
98
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
99
+
100
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
101
+ const { name, arguments: args } = request.params;
102
+
103
+ try {
104
+ let text: string;
105
+
106
+ switch (name) {
107
+ case 'get_next_task':
108
+ text = await handleGetNextTask(args.identificationNumber as string);
109
+ break;
110
+ default:
111
+ throw new Error(`Unknown tool: ${name}`);
112
+ }
113
+
114
+ return { content: [{ type: 'text', text }] };
115
+ } catch (error) {
116
+ return {
117
+ content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }],
118
+ isError: true,
119
+ };
120
+ }
121
+ });
122
+
123
+ const transport = new StdioServerTransport();
124
+ await server.connect(transport);
125
+
126
+ process.stderr.write('we-scrum MCP server running.\n');
127
+ }
@@ -0,0 +1,35 @@
1
+ import { JsonHelper } from '@my-devkit/core';
2
+ import { Credentials } from 'google-auth-library';
3
+ import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { join as pathJoin } from 'node:path';
6
+
7
+ const SETTINGS_FILE = pathJoin(homedir(), '.we-scrum.json');
8
+
9
+ export interface WeScrumSettings {
10
+ credentials?: Credentials;
11
+ selectedProjectId?: string;
12
+ }
13
+
14
+ export class SettingsHelper {
15
+ public static get(): WeScrumSettings {
16
+ if (!existsSync(SETTINGS_FILE)) {
17
+ return {};
18
+ }
19
+ const content = readFileSync(SETTINGS_FILE, 'utf8');
20
+ return JsonHelper.parse<WeScrumSettings>(content) ?? {};
21
+ }
22
+
23
+ public static set(partial: Partial<WeScrumSettings>): void {
24
+ const updated = { ...this.get(), ...partial };
25
+
26
+ writeFileSync(SETTINGS_FILE, JSON.stringify(updated, null, 2));
27
+ chmodSync(SETTINGS_FILE, 0o600);
28
+ }
29
+
30
+ public static clear(): void {
31
+ if (existsSync(SETTINGS_FILE)) {
32
+ rmSync(SETTINGS_FILE);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,107 @@
1
+ import { Command, serialize } from '@my-devkit/core';
2
+
3
+ export class WeScrumService {
4
+ constructor(
5
+ private backendUrl: string,
6
+ private projectId: string,
7
+ private userId: string,
8
+ ) {}
9
+
10
+ // public async createEnumeration(enumeration: Enumeration): Promise<string> {
11
+ // Logger.info(`Creating enumeration ${enumeration.name}`);
12
+
13
+ // const createCommand = TypeHelper.transform(CreateEnumerationCommand, {
14
+ // projectId: this.projectId,
15
+ // name: enumeration.name,
16
+ // });
17
+
18
+ // const enumerationId = await this.post<CreateEnumerationCommand, string>('enumeration-management/create-enumeration', createCommand);
19
+ // for (const property of enumeration.properties) {
20
+ // const propertyCommand = TypeHelper.transform(CreateEnumerationPropertyCommand, {
21
+ // enumerationId,
22
+ // enumerationPropertyId: guid(),
23
+ // name: property.label,
24
+ // value: property.value,
25
+ // });
26
+ // await this.post<CreateEnumerationPropertyCommand, string>(
27
+ // 'enumeration-management/create-enumeration-property',
28
+ // propertyCommand,
29
+ // );
30
+ // }
31
+
32
+ // return enumerationId;
33
+ // }
34
+
35
+ // public async createObject(object: Object, getEnumerationId: (name: string) => string): Promise<string> {
36
+ // Logger.info(`Creating object ${object.name}`);
37
+
38
+ // const createCommand = TypeHelper.transform(CreateObjectCommand, {
39
+ // projectId: this.projectId,
40
+ // name: object.name,
41
+ // });
42
+ // const objectId = await this.post<CreateObjectCommand, string>('object-management/create-object', createCommand);
43
+
44
+ // const propertyMap = new Map<string, string>();
45
+ // for (const property of object.properties) {
46
+ // const objectPropertyId = guid();
47
+ // const propertyCommand = TypeHelper.transform(CreateObjectPropertyCommand, {
48
+ // objectId,
49
+ // objectPropertyId,
50
+ // objectPropertyParentId: propertyMap.get(property.parent) || null,
51
+ // name: property.name,
52
+ // objectPropertyType: property.type,
53
+ // isArray: property.isArray,
54
+ // enumerationId: property.enumerationName ? getEnumerationId(property.enumerationName) : null,
55
+ // });
56
+ // await this.post<CreateObjectPropertyCommand, string>('object-management/create-object-property', propertyCommand);
57
+
58
+ // propertyMap.set(property.path, objectPropertyId);
59
+ // }
60
+
61
+ // return objectId;
62
+ // }
63
+
64
+ // public async createHandler(handler: Handler, getObjectId: (name: string) => string): Promise<void> {
65
+ // Logger.info(`Creating handler ${handler.name}`);
66
+
67
+ // for (const relation of handler.relations) {
68
+ // const createCommand = TypeHelper.transform(CreateRelationCommand, {
69
+ // projectId: this.projectId,
70
+ // boundedContext: handler.boundedContext,
71
+ // handlerName: handler.name,
72
+ // objectIds: relation.map((o) => getObjectId(o)),
73
+ // subscriptionName: null,
74
+ // });
75
+ // await this.post<CreateRelationCommand, string>('relation-management/create-relation', createCommand);
76
+ // }
77
+ // }
78
+
79
+ // public async createRoute(route: Route, getObjectId: (name: string) => string): Promise<string> {
80
+ // Logger.info(`Creating route ${route.method} ${route.path}`);
81
+
82
+ // const createCommand = TypeHelper.transform(CreateRouteCommand, {
83
+ // projectId: this.projectId,
84
+ // method: route.method,
85
+ // path: route.path,
86
+ // objectId: getObjectId(route.object),
87
+ // permission: null,
88
+ // });
89
+
90
+ // return this.post<CreateRouteCommand, string>('route-management/create-route', createCommand);
91
+ // }
92
+
93
+ private async post<C extends Command, R>(route: string, command: C): Promise<R> {
94
+ const response = await fetch(`${this.backendUrl}/command/${route}`, {
95
+ method: 'POST',
96
+ body: JSON.stringify(serialize(command)),
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ Authorization: 'Basic ' + Buffer.from(`${this.userId}:20V0anV4M!we`).toString('base64'),
100
+ },
101
+ signal: AbortSignal.timeout(60 * 1000),
102
+ });
103
+
104
+ const body = (await response.json()) as { result: R };
105
+ return body.result;
106
+ }
107
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "moduleResolution": "node",
5
+ "target": "es2021",
6
+ "outDir": "./dist",
7
+ "baseUrl": "src",
8
+ "sourceMap": true,
9
+ "declaration": false,
10
+ "emitDecoratorMetadata": true,
11
+ "experimentalDecorators": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "paths": {
15
+ "@modelcontextprotocol/sdk/server": ["../node_modules/@modelcontextprotocol/sdk/dist/esm/server/index"],
16
+ "@modelcontextprotocol/sdk/server/stdio.js": ["../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio"],
17
+ "@modelcontextprotocol/sdk/types.js": ["../node_modules/@modelcontextprotocol/sdk/dist/esm/types"]
18
+ }
19
+ }
20
+ }