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 +3 -2
- package/package.json +8 -5
- package/src/commands/auth/login.js +77 -0
- package/src/commands/auth/logout.js +32 -0
- package/src/commands/auth/whoami.js +47 -0
- package/src/commands/index.js +104 -0
- package/src/commands/message/say.js +43 -0
- package/src/commands/room/join.js +68 -0
- package/src/commands/room/leave.js +33 -0
- package/src/commands/room/rooms.js +66 -0
- package/src/commands/shell/status.js +73 -0
- package/src/commands/team/invite.js +46 -0
- package/src/commands/team/myteam.js +78 -0
- package/src/commands/team/teammates.js +52 -0
- package/src/core/api-client.js +156 -0
- package/src/core/notification-window.js +135 -0
- package/src/core/shell.js +415 -854
- package/src/core/websocket.js +230 -292
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:
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
+
};
|