thinkncollab-shell 0.0.1 → 0.0.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/bin/thinknsh.js CHANGED
@@ -45,9 +45,10 @@ if (args.includes('--version') || args.includes('-v')) {
45
45
  process.exit(0);
46
46
  }
47
47
 
48
- // Create and start shell
49
48
  const shell = new ThinkNCollabShell({
50
- serverUrl: process.env.THINKNCOLLAB_SERVER || 'https://api.thinkncollab.com',
49
+ serverUrl: process.env.THINKNCOLLAB_SERVER || 'http://localhost:3001', // ← /thinknsh hata
50
+ apiUrl: process.env.THINKNCOLLAB_API_URL || 'http://localhost:3001',
51
+ wsUrl: process.env.THINKNCOLLAB_WS_URL || 'http://localhost:3001',
51
52
  autoConnect: process.env.THINKNCOLLAB_AUTO_CONNECT === 'true'
52
53
  });
53
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkncollab-shell",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "ThinkNCollab Custom Shell with WebSocket collaboration",
5
5
  "main": "src/core/shell.js",
6
6
  "bin": {
@@ -23,13 +23,16 @@
23
23
  "author": "ThinkNCollab",
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
- "socket.io-client": "^4.7.2",
26
+ "@inquirer/prompts": "^2.3.1",
27
+ "blessed": "^0.1.81",
27
28
  "chalk": "^4.1.2",
29
+ "cli-table3": "^0.6.5",
30
+ "figlet": "^1.6.0",
28
31
  "fs-extra": "^11.1.1",
29
32
  "inquirer": "^8.2.6",
30
- "os-locale": "^6.0.2",
31
33
  "node-machine-id": "^1.1.12",
32
- "figlet": "^1.6.0"
34
+ "os-locale": "^6.0.2",
35
+ "socket.io-client": "^4.8.3"
33
36
  },
34
37
  "engines": {
35
38
  "node": ">=14.0.0"
@@ -39,4 +42,4 @@
39
42
  "src/",
40
43
  "README.md"
41
44
  ]
42
- }
45
+ }
@@ -0,0 +1,77 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+
5
+ module.exports = {
6
+ name: 'login',
7
+ description: 'Login to ThinkNCollab',
8
+ aliases: ['signin'],
9
+ requiresAuth: false,
10
+
11
+ async execute(args, shell) {
12
+ let email, password;
13
+
14
+ try {
15
+ if (args.length === 0) {
16
+ const { input, password: pwdFn } = require('@inquirer/prompts');
17
+ email = await input({ message: 'Email:' });
18
+ password = await pwdFn({ message: 'Password:', mask: '*' });
19
+
20
+ } else if (args.length >= 2) {
21
+ email = args[0];
22
+ password = args[1];
23
+ } else {
24
+ console.log(chalk.red('❌ Usage: login <email> <password>'));
25
+ console.log(chalk.dim(' or: login (interactive)'));
26
+ return;
27
+ }
28
+
29
+ if (!email || !password) {
30
+ console.log(chalk.red('❌ Email and password required'));
31
+ return;
32
+ }
33
+
34
+ console.log(chalk.cyan('🔐 Logging in...'));
35
+
36
+ if (!shell.api) { console.log(chalk.red('❌ API not initialized')); return; }
37
+
38
+ const result = await shell.api.login(email, password);
39
+
40
+ await fs.writeJson(path.join(shell.config.configDir, 'session.json'), {
41
+ user: result.user,
42
+ token: result.token,
43
+ timestamp: new Date().toISOString()
44
+ });
45
+
46
+ console.log(chalk.green(`✅ Welcome, ${result.user.name || result.user.email}!`));
47
+
48
+ // Auto-connect to WebSocket + join personal notification room
49
+ try {
50
+ const token = result.token;
51
+ const userId = result.user._id ;
52
+ const name = result.user.name || result.user.email;
53
+
54
+ // Set user in websocket manager
55
+ shell.ws._token = token;
56
+ shell.ws.setUser({ userId, name, userType: 'User' }, token);
57
+
58
+ // Connect to backend
59
+ await shell.ws.connect(token);
60
+
61
+ // Join personal notification room (for task assignments etc.)
62
+ shell.ws.joinUserRoom(userId);
63
+
64
+ console.log(chalk.green('🔌 Connected to ThinkNCollab server'));
65
+ } catch (wsErr) {
66
+ console.log(chalk.yellow(`⚠️ Could not connect to server: ${wsErr.message}`));
67
+ }
68
+
69
+ } catch (err) {
70
+ if (err.name === 'ExitPromptError' || err.message?.includes('force closed')) {
71
+ console.log(chalk.yellow('⚠️ Cancelled'));
72
+ } else {
73
+ console.log(chalk.red(`❌ Login failed: ${err.message}`));
74
+ }
75
+ }
76
+ }
77
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Logout Command
3
+ *
4
+ * Usage: logout
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const fs = require('fs-extra');
9
+
10
+ module.exports = {
11
+ name: 'logout',
12
+ description: 'Logout from ThinkNCollab',
13
+ aliases: ['signout', 'exit'],
14
+ requiresAuth: true,
15
+
16
+ async execute(args, shell) {
17
+ shell.printMessage(chalk.cyan('👋 Logging out...'));
18
+
19
+ try {
20
+ await shell.api.logout();
21
+
22
+ // Remove session file
23
+ const sessionPath = shell.getSessionPath();
24
+ await fs.remove(sessionPath);
25
+
26
+ shell.printMessage(chalk.green('✅ Logged out successfully'));
27
+
28
+ } catch (error) {
29
+ shell.printMessage(chalk.red(`❌ Logout failed: ${error.message}`));
30
+ }
31
+ }
32
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Whoami Command
3
+ *
4
+ * Usage: whoami
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+
9
+ module.exports = {
10
+ name: 'whoami',
11
+ description: 'Show current user information',
12
+ aliases: ['user', 'me'],
13
+ requiresAuth: true,
14
+
15
+ async execute(args, shell) {
16
+ const user = shell.api.getUser();
17
+ const room = shell.api.getCurrentRoom();
18
+
19
+ console.log(chalk.cyan('\n👤 User Information:'));
20
+ console.log(chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
21
+ console.log(` ${chalk.yellow('Email:')} ${user.email}`);
22
+ console.log(` ${chalk.yellow('Name:')} ${user.name || 'Not set'}`);
23
+ console.log(` ${chalk.yellow('User ID:')} ${user.id || 'N/A'}`);
24
+
25
+ if (user.team) {
26
+ console.log(` ${chalk.yellow('Team:')} ${user.team}`);
27
+ }
28
+
29
+ if (user.role) {
30
+ console.log(` ${chalk.yellow('Role:')} ${user.role}`);
31
+ }
32
+
33
+ console.log(` ${chalk.yellow('Status:')} ${shell.api.isConnected() ? chalk.green('Online') : chalk.gray('Offline')}`);
34
+
35
+ if (room) {
36
+ console.log(` ${chalk.yellow('Room:')} ${chalk.cyan(room.name)} (${room.id})`);
37
+ }
38
+
39
+ // Show last login if available
40
+ if (user.lastLogin) {
41
+ const lastLogin = new Date(user.lastLogin).toLocaleString();
42
+ console.log(` ${chalk.yellow('Last Login:')} ${lastLogin}`);
43
+ }
44
+
45
+ console.log();
46
+ }
47
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Command Registry - Loads and manages all commands
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+
7
+ class CommandRegistry {
8
+ constructor(shell) {
9
+ this.shell = shell;
10
+ this.commands = new Map();
11
+ this.aliases = new Map();
12
+ this.categories = new Map();
13
+
14
+ this.loadCommands();
15
+ }
16
+
17
+ loadCommands() {
18
+ try {
19
+ this.register(require('./auth/login'), 'auth');
20
+ this.register(require('./auth/logout'), 'auth');
21
+ this.register(require('./auth/whoami'), 'auth');
22
+ this.register(require('./team/myteam'), 'team');
23
+ this.register(require('./team/teammates'), 'team');
24
+ this.register(require('./team/invite'), 'team');
25
+ this.register(require('./room/join'), 'room');
26
+ this.register(require('./room/leave'), 'room');
27
+ this.register(require('./room/rooms'), 'room');
28
+ this.register(require('./message/say'), 'message');
29
+ this.register(require('./shell/status'), 'shell');
30
+ } catch (error) {
31
+ console.error(chalk.red(`❌ Failed to load command: ${error.message}`));
32
+ console.error(error.stack);
33
+ }
34
+ }
35
+
36
+ register(command, category) {
37
+ this.commands.set(command.name, command);
38
+
39
+ if (!this.categories.has(category)) {
40
+ this.categories.set(category, []);
41
+ }
42
+ this.categories.get(category).push(command.name);
43
+
44
+ if (command.aliases) {
45
+ command.aliases.forEach(alias => this.aliases.set(alias, command.name));
46
+ }
47
+ }
48
+
49
+ addAlias(alias, command) {
50
+ this.aliases.set(alias, command);
51
+ }
52
+
53
+ getCommand(name) {
54
+ if (this.aliases.has(name)) name = this.aliases.get(name);
55
+ return this.commands.get(name);
56
+ }
57
+
58
+ async execute(input, shell) {
59
+ const args = shell.parseCommand(input);
60
+ const cmdName = args[0].toLowerCase();
61
+ const cmdArgs = args.slice(1);
62
+
63
+ const command = this.getCommand(cmdName);
64
+ if (!command) return false;
65
+
66
+ try {
67
+ if (command.requiresAuth && !shell.api?.isAuthenticated()) {
68
+ console.log(chalk.red('❌ Please login first: login <email> <password>'));
69
+ return true;
70
+ }
71
+
72
+ if (command.requiresRoom && !shell.api?.getCurrentRoom()) {
73
+ console.log(chalk.red('❌ Please join a room first: join <room-id>'));
74
+ return true;
75
+ }
76
+
77
+ await command.execute(cmdArgs, shell);
78
+
79
+ } catch (error) {
80
+ console.log(chalk.red(`❌ Command error: ${error.message}`));
81
+ }
82
+
83
+ return true;
84
+ }
85
+
86
+ getHelpText() {
87
+ let help = chalk.cyan('\n📚 Available Commands:\n');
88
+ help += chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n');
89
+
90
+ for (const [category, commands] of this.categories) {
91
+ help += chalk.yellow(`${category.toUpperCase()}:\n`);
92
+ commands.forEach(cmdName => {
93
+ const cmd = this.commands.get(cmdName);
94
+ const aliases = cmd.aliases ? chalk.dim(` (${cmd.aliases.join(', ')})`) : '';
95
+ help += ` ${chalk.green(cmdName.padEnd(12))}${aliases} - ${cmd.description}\n`;
96
+ });
97
+ help += '\n';
98
+ }
99
+
100
+ return help;
101
+ }
102
+ }
103
+
104
+ module.exports = CommandRegistry;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Say Command
3
+ *
4
+ * Usage: say <message>
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+
9
+ module.exports = {
10
+ name: 'say',
11
+ description: 'Send a message to the current room',
12
+ aliases: ['msg', 'm', 'send'],
13
+ requiresAuth: true,
14
+ requiresRoom: true,
15
+
16
+ async execute(args, shell) {
17
+ const message = args.join(' ');
18
+
19
+ if (!message) {
20
+ console.log(chalk.red('❌ Usage: say <message>'));
21
+ return;
22
+ }
23
+
24
+ try {
25
+ await shell.api.sendMessage(message);
26
+
27
+ // Clear typing indicator
28
+ if (shell.isTyping) {
29
+ shell.api.sendTyping(false);
30
+ shell.isTyping = false;
31
+ clearTimeout(shell.typingTimeout);
32
+ }
33
+
34
+ // Echo locally (server will also broadcast)
35
+ const user = shell.api.getUser();
36
+ const time = new Date().toLocaleTimeString();
37
+ console.log(chalk.dim(`[${time}] `) + chalk.green(`You: ${message}`));
38
+
39
+ } catch (error) {
40
+ console.log(chalk.red(`❌ Failed to send: ${error.message}`));
41
+ }
42
+ }
43
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Join Command
3
+ *
4
+ * Usage: join <room-id>
5
+ * join <room-name>
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+
10
+ module.exports = {
11
+ name: 'join',
12
+ description: 'Join a collaboration room',
13
+ aliases: ['enter', 'goto'],
14
+ requiresAuth: true,
15
+
16
+ async execute(args, shell) {
17
+ if (args.length === 0) {
18
+ console.log(chalk.red('❌ Usage: join <room-id> [password]'));
19
+ return;
20
+ }
21
+
22
+ const roomId = args[0];
23
+ const password = args[1];
24
+
25
+ console.log(chalk.cyan(`🔗 Joining room: ${roomId}...`));
26
+
27
+ try {
28
+ const result = await shell.api.joinRoom(roomId, password);
29
+
30
+ console.log(chalk.green(`✅ Joined ${result.room.name}`));
31
+
32
+ // Show room info
33
+ console.log(chalk.dim(` 📝 ${result.room.description || 'No description'}`));
34
+ console.log(chalk.dim(` 👥 ${result.participants.length} participants`));
35
+
36
+ // Show recent messages
37
+ if (result.recentMessages && result.recentMessages.length > 0) {
38
+ console.log(chalk.cyan('\n📜 Recent messages:'));
39
+ result.recentMessages.slice(-3).forEach(msg => {
40
+ const time = new Date(msg.timestamp).toLocaleTimeString();
41
+ console.log(chalk.dim(` [${time}] ${msg.username}: ${msg.message}`));
42
+ });
43
+ }
44
+
45
+ // Show participants
46
+ if (result.participants.length > 0) {
47
+ console.log(chalk.cyan('\n👥 In room:'));
48
+ const online = result.participants.filter(p => p.online);
49
+ online.forEach(p => {
50
+ const you = p.email === shell.api.getUser().email ? chalk.green(' (you)') : '';
51
+ console.log(` ${chalk.green('●')} ${p.name || p.email}${you}`);
52
+ });
53
+ }
54
+
55
+ console.log(chalk.dim('\n💡 Type "say <message>" to chat'));
56
+
57
+ } catch (error) {
58
+ if (error.message.includes('password')) {
59
+ console.log(chalk.red(`❌ Room requires password: join ${roomId} <password>`));
60
+ } else if (error.message.includes('not found')) {
61
+ console.log(chalk.red(`❌ Room not found: ${roomId}`));
62
+ console.log(chalk.dim('💡 Type "rooms" to see available rooms'));
63
+ } else {
64
+ console.log(chalk.red(`❌ Failed to join: ${error.message}`));
65
+ }
66
+ }
67
+ }
68
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Leave Command
3
+ *
4
+ * Usage: leave
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+
9
+ module.exports = {
10
+ name: 'leave',
11
+ description: 'Leave current room',
12
+ aliases: ['exit-room', 'part'],
13
+ requiresAuth: true,
14
+ requiresRoom: true,
15
+
16
+ async execute(args, shell) {
17
+ const room = shell.api.getCurrentRoom();
18
+
19
+ if (!room) {
20
+ console.log(chalk.yellow('⚠️ Not in any room'));
21
+ return;
22
+ }
23
+
24
+ console.log(chalk.cyan(`👋 Leaving room: ${room.name}...`));
25
+
26
+ try {
27
+ await shell.api.leaveRoom();
28
+ console.log(chalk.green(`✅ Left room: ${room.name}`));
29
+ } catch (error) {
30
+ console.log(chalk.red(`❌ Failed to leave: ${error.message}`));
31
+ }
32
+ }
33
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Rooms Command
3
+ *
4
+ * Usage: rooms
5
+ * rooms --all
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+
10
+ module.exports = {
11
+ name: 'rooms',
12
+ description: 'List available rooms',
13
+ aliases: ['channels', 'ls'],
14
+ requiresAuth: true,
15
+
16
+ async execute(args, shell) {
17
+ const showAll = args.includes('--all') || args.includes('-a');
18
+
19
+ console.log(chalk.cyan('\n📋 Available Rooms:'));
20
+ console.log(chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
21
+
22
+ try {
23
+ const rooms = await shell.api.getRooms();
24
+ const currentRoom = shell.api.getCurrentRoom();
25
+
26
+ if (rooms.length === 0) {
27
+ console.log(chalk.yellow(' No rooms available'));
28
+ console.log(chalk.dim('💡 Create a room with: join <new-room-name>'));
29
+ return;
30
+ }
31
+
32
+ // Separate my rooms and other rooms
33
+ const myRooms = rooms.filter(r => r.isMember);
34
+ const otherRooms = rooms.filter(r => !r.isMember);
35
+
36
+ if (myRooms.length > 0) {
37
+ console.log(chalk.yellow('\n YOUR ROOMS:'));
38
+ myRooms.forEach(room => {
39
+ const current = currentRoom && currentRoom.id === room.id ? chalk.green(' (current)') : '';
40
+ const active = room.active ? chalk.green('●') : chalk.gray('○');
41
+ console.log(` ${active} ${chalk.white(room.name)}${current}`);
42
+ console.log(chalk.dim(` ${room.participants} online · ${room.description || ''}`));
43
+ });
44
+ }
45
+
46
+ if (otherRooms.length > 0 && showAll) {
47
+ console.log(chalk.yellow('\n PUBLIC ROOMS:'));
48
+ otherRooms.slice(0, 10).forEach(room => {
49
+ console.log(` ○ ${chalk.white(room.name)}`);
50
+ console.log(chalk.dim(` ${room.participants} online · ${room.description || ''}`));
51
+ });
52
+
53
+ if (otherRooms.length > 10) {
54
+ console.log(chalk.dim(` ... and ${otherRooms.length - 10} more (use --all to see all)`));
55
+ }
56
+ } else if (otherRooms.length > 0) {
57
+ console.log(chalk.dim(`\n 📌 ${otherRooms.length} other rooms available (use --all to see)`));
58
+ }
59
+
60
+ console.log(chalk.dim('\n💡 Type "join <room-name>" to join a room'));
61
+
62
+ } catch (error) {
63
+ console.log(chalk.red(`❌ Failed to fetch rooms: ${error.message}`));
64
+ }
65
+ }
66
+ };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Status Command
3
+ *
4
+ * Usage: status
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const os = require('os');
9
+
10
+ module.exports = {
11
+ name: 'status',
12
+ description: 'Show shell and connection status',
13
+ aliases: ['stats', 'info'],
14
+ requiresAuth: false,
15
+
16
+ async execute(args, shell) {
17
+ const user = shell.api.getUser();
18
+ const room = shell.api.getCurrentRoom();
19
+ const connected = shell.api.isConnected();
20
+
21
+ console.log(chalk.cyan('\n📊 ThinkNCollab Shell Status'));
22
+ console.log(chalk.dim('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
23
+
24
+ // Connection status
25
+ console.log(` ${chalk.yellow('Connection:')} ${connected ? chalk.green('● Online') : chalk.red('○ Offline')}`);
26
+
27
+ if (connected) {
28
+ console.log(` ${chalk.yellow('Server:')} ${shell.api.config.wsUrl}`);
29
+ console.log(` ${chalk.yellow('Socket ID:')} ${shell.api.getSocketId() || 'N/A'}`);
30
+ }
31
+
32
+ // Authentication
33
+ console.log(` ${chalk.yellow('Auth:')} ${user ? chalk.green(`✓ ${user.email}`) : chalk.red('✗ Not logged in')}`);
34
+
35
+ if (user) {
36
+ console.log(` ${chalk.yellow('User:')} ${user.name || user.email}`);
37
+ if (user.team) {
38
+ console.log(` ${chalk.yellow('Team:')} ${user.team}`);
39
+ }
40
+ }
41
+
42
+ // Room
43
+ if (room) {
44
+ console.log(` ${chalk.yellow('Current Room:')} ${chalk.cyan(room.name)} (${room.id})`);
45
+ console.log(` ${chalk.yellow('Participants:')} ${room.participants?.length || 0}`);
46
+ } else {
47
+ console.log(` ${chalk.yellow('Current Room:')} ${chalk.gray('None')}`);
48
+ }
49
+
50
+ // Shell info
51
+ console.log(`\n ${chalk.yellow('Shell Version:')} ${require('../../../package.json').version}`);
52
+ console.log(` ${chalk.yellow('Node Version:')} ${process.version}`);
53
+ console.log(` ${chalk.yellow('Platform:')} ${os.platform()} ${os.arch()}`);
54
+ console.log(` ${chalk.yellow('Uptime:')} ${Math.floor(process.uptime() / 60)}m`);
55
+ console.log(` ${chalk.yellow('Memory:')} ${Math.round(process.memoryUsage().rss / 1024 / 1024)} MB`);
56
+ console.log(` ${chalk.yellow('History:')} ${shell.history.length} commands`);
57
+
58
+ // Suggestions
59
+ console.log(chalk.dim('\n💡 Tips:'));
60
+ if (!user) {
61
+ console.log(chalk.dim(' • Login with: login <email> <password>'));
62
+ } else if (!room) {
63
+ console.log(chalk.dim(' • Join a room with: join <room-id>'));
64
+ console.log(chalk.dim(' • See available rooms: rooms'));
65
+ } else {
66
+ console.log(chalk.dim(' • Send messages with: say <message>'));
67
+ console.log(chalk.dim(' • See teammates: teammates'));
68
+ console.log(chalk.dim(' • Leave room: leave'));
69
+ }
70
+
71
+ console.log();
72
+ }
73
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Invite Command
3
+ *
4
+ * Usage: invite <email>
5
+ * invite <email> --role <role>
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+
10
+ module.exports = {
11
+ name: 'invite',
12
+ description: 'Invite someone to your team',
13
+ aliases: ['inv'],
14
+ requiresAuth: true,
15
+
16
+ async execute(args, shell) {
17
+ if (args.length === 0) {
18
+ console.log(chalk.red('❌ Usage: invite <email> [--role <role>]'));
19
+ console.log(chalk.dim(' Roles: admin, member, viewer'));
20
+ return;
21
+ }
22
+
23
+ const email = args[0];
24
+ const roleIndex = args.indexOf('--role');
25
+ const role = roleIndex !== -1 && args[roleIndex + 1] ? args[roleIndex + 1] : 'member';
26
+
27
+ console.log(chalk.cyan(`📧 Inviting ${email} as ${role}...`));
28
+
29
+ try {
30
+ const result = await shell.api.inviteTeammate(email, role);
31
+
32
+ console.log(chalk.green(`✅ Invitation sent to ${email}`));
33
+
34
+ if (result.inviteLink) {
35
+ console.log(chalk.dim(`📎 Invite link: ${result.inviteLink}`));
36
+ }
37
+
38
+ } catch (error) {
39
+ if (error.message.includes('already')) {
40
+ console.log(chalk.yellow(`⚠️ ${email} is already a team member`));
41
+ } else {
42
+ console.log(chalk.red(`❌ Invitation failed: ${error.message}`));
43
+ }
44
+ }
45
+ }
46
+ };