thinkncollab-cli 0.0.89 → 0.0.91

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.
@@ -24,15 +24,16 @@ function getAuthData() {
24
24
  }
25
25
 
26
26
  // -----------------------------
27
- // Main Function
27
+ // Main Function - with limit parameter
28
28
  // -----------------------------
29
- async function myTask() {
29
+ async function myTask(limit = 5) {
30
30
  try {
31
31
  const { email, token } = getAuthData();
32
-
32
+ const machineId = machine.machineIdSync();
33
+
33
34
  const CWD = process.cwd();
34
35
  const metaDataPath = path.join(CWD, ".tnc", ".tncmeta.json");
35
-
36
+
36
37
  if (!fs.existsSync(metaDataPath)) {
37
38
  console.log(chalk.yellow("⚠️ Room metadata not found in current directory!"));
38
39
  return;
@@ -46,95 +47,167 @@ async function myTask() {
46
47
 
47
48
  const cacheFile = path.join(cacheDir, "tasks.json");
48
49
 
49
- let tasks = [];
50
+ let userTasks = [];
51
+ let recommendations = [];
50
52
 
51
53
  try {
54
+ console.log(chalk.blue("🔄 Fetching from server..."));
55
+
56
+ // FIX: Headers me bhejo - safe!
52
57
  const res = await axios.get(`${url}/${roomId}`, {
53
58
  params: {
54
- email,
55
- token,
56
- machineId: machine.machineIdSync()
57
- }
59
+ n: limit, // sirf n query me
60
+ format: 'json'
61
+ },
62
+ headers: {
63
+ 'x-user-email': email,
64
+ 'x-user-token': token,
65
+ 'x-machine-id': machineId,
66
+ 'User-Agent': 'tnc-cli/1.0'
67
+ },
68
+ timeout: 5000
58
69
  });
59
70
 
60
- tasks = res.data.tasks || [];
71
+ console.log(chalk.green("✅ Server response received!"));
72
+
73
+ // Server sends userTasks and recommendations
74
+ userTasks = res.data.userTasks || [];
75
+ recommendations = res.data.recommendations?.topTasks || [];
61
76
 
62
- fs.writeFileSync(cacheFile, JSON.stringify(tasks, null, 2));
77
+ // Cache ONLY top 5 tasks (combine userTasks + recommendations, take first 5)
78
+ const top5Tasks = [...userTasks, ...recommendations].slice(0, 5);
79
+
80
+ // Cache the data - sirf top 5
81
+ fs.writeFileSync(cacheFile, JSON.stringify({
82
+ userTasks: top5Tasks.filter(t => t.assignedTo?.some(a => a.userId?._id?.toString() === userTasks[0]?.assignedTo?.[0]?.userId?._id?.toString())),
83
+ recommendations: top5Tasks.filter(t => !t.assignedTo?.some(a => a.userId?._id?.toString() === userTasks[0]?.assignedTo?.[0]?.userId?._id?.toString()))
84
+ }, null, 2));
85
+
86
+ // Display logic...
87
+ displayTasks(userTasks, recommendations);
63
88
 
64
89
  } catch (error) {
90
+ // Offline mode...
91
+ console.log(chalk.red(`❌ Server error: ${error.message}`));
92
+ if (error.code === 'ECONNREFUSED') {
93
+ console.log(chalk.red(`🚨 Server not running at ${url}`));
94
+ console.log(chalk.yellow("💡 Start server: npm start"));
95
+ } else if (error.response?.status === 401) {
96
+ console.log(chalk.red("🔒 Unauthorized! Token expired or invalid."));
97
+ console.log(chalk.yellow("💡 Please login again: tnc-cli login"));
98
+ }
99
+
65
100
  if (fs.existsSync(cacheFile)) {
66
101
  console.log(chalk.yellow("⚠️ Offline mode: showing cached tasks"));
67
- tasks = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
102
+ const cached = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
103
+ userTasks = cached.userTasks || [];
104
+ recommendations = cached.recommendations || [];
105
+ displayTasks(userTasks, recommendations);
68
106
  } else {
69
- console.error(
70
- chalk.red("❌ Error fetching tasks and no cache available:"),
71
- error.response?.data || error.message
72
- );
73
- return;
107
+ console.log(chalk.gray("📭 No cached tasks available."));
74
108
  }
75
109
  }
76
-
77
- if (!tasks.length) {
78
- console.log(chalk.gray("📭 No tasks assigned."));
79
- return;
80
- }
81
-
82
- // -----------------------------
83
- // Create Table
84
- // -----------------------------
85
- const table = new CliTable3({
86
- head: [
87
- chalk.cyan.bold("Title"),
88
- chalk.green.bold("Status"),
89
- chalk.magenta.bold("Category"),
90
- chalk.red.bold("Priority"),
91
- chalk.yellow.bold("ID"),
92
- chalk.blue.bold("Att.")
93
- ],
94
- colWidths: [30, 12, 18, 10, 26, 6], // adjusted
95
- wordWrap: true,
96
- style: {
97
- head: [],
98
- border: ["grey"]
110
+ } catch (error) {
111
+ console.error(chalk.red("❌ Error:"), error.message);
99
112
  }
100
- });
101
-
102
-
103
- console.log(chalk.bold.cyan("\n📋 YOUR TASKS\n"));
104
-
105
- tasks.forEach(task => {
106
-
107
- // Status color
108
- let statusColor =
109
- task.status === "completed"
110
- ? chalk.green
111
- : task.status === "in-progress"
112
- ? chalk.yellow
113
- : chalk.red;
113
+ }
114
114
 
115
- // Priority color
116
- let priorityColor =
117
- task.priority === "high"
118
- ? chalk.red
119
- : task.priority === "medium"
120
- ? chalk.yellow
121
- : chalk.green;
115
+ // Display function
116
+ function displayTasks(userTasks, recommendations) {
117
+ // Show user tasks
118
+ if (userTasks.length > 0) {
119
+ const userTable = new CliTable3({
120
+ head: [
121
+ chalk.cyan.bold("Title"),
122
+ chalk.green.bold("Status"),
123
+ chalk.magenta.bold("Category"),
124
+ chalk.red.bold("Priority"),
125
+ chalk.yellow.bold("ID")
126
+ ],
127
+ colWidths: [30, 12, 18, 8, 30],
128
+ wordWrap: true
129
+ });
122
130
 
123
- table.push([
124
- chalk.white(task.title),
125
- statusColor(task.status),
131
+ console.log(chalk.bold.cyan("\n📋 YOUR ASSIGNED TASKS\n"));
132
+ userTasks.forEach(task => {
133
+ userTable.push([
134
+ chalk.white(task.title || "Untitled"),
135
+ task.status === "completed" ? chalk.green(task.status) :
136
+ task.status === "in-progress" ? chalk.yellow(task.status) :
137
+ task.status === "pending" ? chalk.blue(task.status) : chalk.red(task.status || "unknown"),
126
138
  chalk.magenta(task.category || "General"),
127
- priorityColor(task.priority || "low"),
128
- chalk.gray(task._id),
129
- chalk.blue(task.attachments?.length || 0)
139
+ task.priority === "critical" ? chalk.bgRed.white(" CRITICAL ") :
140
+ task.priority === "high" ? chalk.red(task.priority) :
141
+ task.priority === "medium" ? chalk.yellow(task.priority) :
142
+ task.priority === "low" ? chalk.green(task.priority) : chalk.gray("none"),
143
+ chalk.gray(task._id ? task._id : 'N/A')
130
144
  ]);
131
145
  });
146
+ console.log(userTable.toString());
147
+ }
132
148
 
133
- console.log(table.toString());
149
+ // Show recommendations
150
+ if (recommendations.length > 0) {
151
+ const recTable = new CliTable3({
152
+ head: [
153
+ chalk.cyan.bold("Title"),
154
+ chalk.green.bold("Score"),
155
+ chalk.blue.bold("Status"),
156
+ chalk.red.bold("Priority"),
157
+ chalk.yellow.bold("Due Date"),
158
+ chalk.magenta.bold("ID") // ← ID column add kiya
159
+ ],
160
+ colWidths: [30, 10, 12, 12, 15, 30], // ID ke liye width
161
+ wordWrap: true
162
+ });
163
+
164
+ console.log(chalk.bold.cyan("\n🎯 RECOMMENDED TASKS\n"));
165
+
166
+ recommendations.forEach((task, idx) => {
167
+ // Status color
168
+ let statusColor =
169
+ task.status === "completed" ? chalk.green("✓ Completed") :
170
+ task.status === "in-progress" ? chalk.yellow("⟳ In Progress") :
171
+ task.status === "pending" ? chalk.blue("⏳ Pending") :
172
+ task.isOverdue ? chalk.red("⚠️ Overdue") :
173
+ chalk.gray(task.status || "Unknown");
174
+
175
+ recTable.push([
176
+ chalk.white(task.title || "Untitled"),
177
+
178
+ // Score column
179
+ task.score >= 80 ? chalk.green.bold(task.score) :
180
+ task.score >= 60 ? chalk.yellow(task.score) :
181
+ task.score >= 40 ? chalk.hex('#FFA500')(task.score) : chalk.red(task.score || 0),
182
+
183
+ // Status column
184
+ statusColor,
185
+
186
+ // Priority column
187
+ task.priority === "critical" ? chalk.bgRed.white(" CRITICAL ") :
188
+ task.priority === "high" ? chalk.red(task.priority) :
189
+ task.priority === "medium" ? chalk.yellow(task.priority) :
190
+ task.priority === "low" ? chalk.green(task.priority) : chalk.gray("none"),
191
+
192
+ // Due Date column
193
+ task.dueDate ? new Date(task.dueDate).toLocaleDateString() : chalk.gray("No date"),
194
+
195
+ // ID column - short version
196
+ chalk.gray(task._id ? task._id : 'N/A')
197
+ ]);
198
+ });
199
+
200
+ console.log(recTable.toString());
201
+ }
134
202
 
135
- } catch (error) {
136
- console.error(chalk.red(" Error:"), error.message);
203
+ if (userTasks.length === 0 && recommendations.length === 0) {
204
+ console.log(chalk.gray("📭 No tasks found."));
137
205
  }
206
+
207
+ // Summary
208
+ console.log(chalk.gray("\n" + "─".repeat(60)));
209
+ console.log(chalk.cyan(`📊 Summary: ${userTasks.length} assigned, ${recommendations.length} recommended`));
210
+ console.log(chalk.gray(`💡 Use -n <number> to see more tasks (e.g., tnc-cli my-tasks -n 10)`));
138
211
  }
139
212
 
140
- export default myTask;
213
+ export default myTask;
@@ -6,7 +6,7 @@ import chalk from "chalk";
6
6
  import getVerify from "../lib/getVerify.js";
7
7
 
8
8
  const CWD = process.cwd();
9
- const TNC_API_URL = "https://thinkncollab.com/";
9
+ const BASE_URL = "https://thinkncollab.com/cli";
10
10
  const metaDataFile = path.join(CWD, ".tnc", ".tncmeta.json");
11
11
 
12
12
  async function myTeam() {
@@ -18,9 +18,7 @@ async function myTeam() {
18
18
  return;
19
19
  }
20
20
 
21
- const metaData = JSON.parse(
22
- fs.readFileSync(metaDataFile, "utf-8")
23
- );
21
+ const metaData = JSON.parse(fs.readFileSync(metaDataFile, "utf-8"));
24
22
 
25
23
  if (!metaData.roomId) {
26
24
  console.log(chalk.red("❌ No room associated with this project."));
@@ -28,48 +26,99 @@ async function myTeam() {
28
26
  }
29
27
 
30
28
  const roomId = metaData.roomId;
29
+ const projectName = metaData.projectName || "Unknown Project";
31
30
 
31
+ // Get user credentials
32
32
  const verifyData = await getVerify();
33
33
  const { email, token } = verifyData;
34
+ const machineId = machine.machineIdSync();
34
35
 
35
- const response = await axios.get(
36
- `${TNC_API_URL}cli/myTeam/${roomId}`,
37
- {
38
- headers: {
39
- email,
40
- token,
41
- machineId: machine.machineIdSync(),
42
- },
36
+ console.log(chalk.blue(`📋 Fetching team members for project: ${projectName}`));
37
+ console.log(chalk.blue(`🏠 Room ID: ${roomId}`));
38
+
39
+ // API call with headers for authentication
40
+ const response = await axios({
41
+ method: 'get',
42
+ url: `${BASE_URL}/myTeam/${roomId}`,
43
+ headers: {
44
+ 'x-user-email': email,
45
+ 'x-user-token': token,
46
+ 'x-machine-id': machineId,
47
+ 'x-project-id': metaData.projectId // Optional: for project context
43
48
  }
44
- );
49
+ // NO REQUEST BODY - all auth in headers
50
+ });
45
51
 
46
52
  const teamMembers = response.data.RoomMembers || [];
47
53
 
48
54
  if (teamMembers.length === 0) {
49
- console.log(chalk.yellow("⚠️ You are not part of any team yet."));
55
+ console.log(chalk.yellow("⚠️ No team members found in this room."));
50
56
  return;
51
57
  }
52
58
 
53
- console.log(chalk.green("👥 Your Team Members:"));
54
- teamMembers.forEach(member => {
59
+ // Display team information
60
+ console.log(chalk.green("\n👥 Team Members:"));
61
+ console.log(chalk.gray("────────────────────────"));
62
+
63
+ // Sort members by role or name
64
+ const sortedMembers = teamMembers.sort((a, b) => {
65
+ // Put room owner first if available
66
+ if (a.role === 'owner' && b.role !== 'owner') return -1;
67
+ if (b.role === 'owner' && a.role !== 'owner') return 1;
68
+ return (a.name || a.email).localeCompare(b.name || b.email);
69
+ });
70
+
71
+ sortedMembers.forEach((member, index) => {
72
+ // Determine role badge
73
+ let roleBadge = '';
74
+ if (member.role === 'owner') {
75
+ roleBadge = chalk.yellow('👑 Owner');
76
+ } else if (member.role === 'admin' || member.role === 'manager') {
77
+ roleBadge = chalk.blue('⚙️ Admin');
78
+ } else {
79
+ roleBadge = chalk.gray('👤 Member');
80
+ }
81
+
82
+ // Format online status
83
+ const status = member.isOnline
84
+ ? chalk.green('● Online')
85
+ : chalk.gray('○ Offline');
86
+
55
87
  console.log(
56
- chalk.blue(`- ${member.name} (${member.email})`)
88
+ chalk.white(`${index + 1}. `) +
89
+ chalk.cyan.bold(member.name || 'Unknown') +
90
+ chalk.gray(` <${member.email}>`)
57
91
  );
92
+ console.log(chalk.gray(` ${roleBadge} • ${status}`));
93
+
94
+ // Show last active if available
95
+ if (member.lastActive) {
96
+ const lastActive = new Date(member.lastActive).toLocaleString();
97
+ console.log(chalk.gray(` Last active: ${lastActive}`));
98
+ }
99
+
100
+ console.log(chalk.gray("────────────────────────"));
58
101
  });
59
102
 
103
+ console.log(chalk.green(`\n✅ Total members: ${teamMembers.length}`));
104
+
60
105
  } catch (err) {
61
106
  if (err.response) {
62
- console.error(
63
- chalk.red("❌ Error:"),
64
- chalk.red(err.response.data.error)
65
- );
107
+ console.error(chalk.red("❌ Server Error:"), chalk.red(err.response.data.message || err.response.data.error));
108
+
109
+ if (err.response.status === 401) {
110
+ console.error(chalk.yellow(" Unauthorized. Please login again."));
111
+ } else if (err.response.status === 404) {
112
+ console.error(chalk.yellow(" Room not found. Please check your project."));
113
+ } else if (err.response.status === 403) {
114
+ console.error(chalk.yellow(" You don't have permission to view this team."));
115
+ }
116
+ } else if (err.request) {
117
+ console.error(chalk.red("❌ No response from server. Check your internet connection."));
66
118
  } else {
67
- console.error(
68
- chalk.red("❌ Error:"),
69
- chalk.red(err.message)
70
- );
119
+ console.error(chalk.red("❌ Error:"), chalk.red(err.message));
71
120
  }
72
121
  }
73
122
  }
74
123
 
75
- export default myTeam;
124
+ export default myTeam;
@@ -2,51 +2,86 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import axios from "axios";
4
4
  import os from "os";
5
+ import machineId from "node-machine-id"; // Add this import
5
6
 
6
7
  const homeDir = os.homedir();
7
- const url = "https://thinkncollab.com/cli/invite";
8
+ const baseUrl = "https://thinkncollab.com/cli";
9
+
8
10
  async function getEmail() {
9
11
  const rcFile = path.join(homeDir, ".tncrc");
10
12
  if (!fs.existsSync(rcFile)) {
11
- console.log(" Please login first!");
13
+ console.log(" Please login first!");
12
14
  process.exit(1);
13
15
  }
14
16
  const content = fs.readFileSync(rcFile, "utf-8");
15
- const email = JSON.parse(content).email;
16
- return email;
17
+ return JSON.parse(content).email;
17
18
  }
19
+
18
20
  async function getToken() {
19
21
  const rcFile = path.join(homeDir, '.tncrc');
20
- if(!fs.readFileSync(rcFile)){
21
- console.log(" Please login first! ")
22
+ if (!fs.existsSync(rcFile)) {
23
+ console.log(" Please login first!");
24
+ process.exit(1);
22
25
  }
23
26
  const content = fs.readFileSync(rcFile, 'utf-8');
24
- const token = JSON.parse(content).token;
25
- return token;
27
+ return JSON.parse(content).token;
26
28
  }
29
+
27
30
  async function sendInvite(inviteeEmail) {
28
31
  try {
29
32
  const email = await getEmail();
30
33
  const token = await getToken();
34
+ const machineIdValue = machineId.machineIdSync(); // Get machine ID
35
+
31
36
  const CWD = process.cwd();
32
- const metaDataPath = path.join(".tnc", '.tncmeta.json');
33
- const metaData = JSON.parse(fs.readFileSync(path.join(CWD, metaDataPath), 'utf-8'));
37
+
38
+ // Read project metadata
39
+ const metaDataPath = path.join(CWD, ".tnc", '.tncmeta.json');
40
+ if (!fs.existsSync(metaDataPath)) {
41
+ console.log("❌ Not in a ThinkNCollab project. Run 'tnc init' first.");
42
+ process.exit(1);
43
+ }
44
+
45
+ const metaData = JSON.parse(fs.readFileSync(metaDataPath, 'utf-8'));
34
46
  const roomId = metaData.roomId;
35
- console.log(email, token, roomId, inviteeEmail);
36
- const res = await axios({
37
- method: 'post',
38
- url: `${url}/${roomId}`,
39
- params: {
40
- email,
41
- token,
42
- inviteeEmail,
43
- roomId
44
- }
45
- });
46
- console.log(" Invitation sent successfully to", inviteeEmail);
47
- console.log("Invite Link:", res.data.invitationLink);
47
+
48
+ console.log(`📧 Inviting: ${inviteeEmail}`);
49
+ console.log(`🏠 Room ID: ${roomId}`);
50
+
51
+ // USING HEADERS FOR AUTHENTICATION
52
+ const res = await axios({
53
+ method: 'post',
54
+ url: `${baseUrl}/invite/${roomId}`,
55
+ params: {
56
+ inviteeEmail: inviteeEmail // Only inviteeEmail in query params
57
+ },
58
+ headers: {
59
+ 'x-user-email': email,
60
+ 'x-user-token': token,
61
+ 'x-machine-id': machineIdValue
62
+ }
63
+ });
64
+
65
+ console.log("✅ Invitation sent successfully to", inviteeEmail);
66
+ console.log("🔗 Invite Link:", res.data.invitationLink);
67
+
48
68
  } catch (error) {
49
- console.error(" Error while generating the invitation link:", error.response?.data || error.message);
69
+ if (error.response) {
70
+ // Server responded with error
71
+ console.error("❌ Server error:", error.response.data.message || error.response.status);
72
+ if (error.response.status === 401) {
73
+ console.error(" Unauthorized. Please login again.");
74
+ } else if (error.response.status === 404) {
75
+ console.error(" Room not found.");
76
+ }
77
+ } else if (error.request) {
78
+ // No response received
79
+ console.error("❌ No response from server. Check your internet connection.");
80
+ } else {
81
+ // Other errors
82
+ console.error("❌ Error:", error.message);
83
+ }
50
84
  }
51
85
  }
52
- export default sendInvite;
86
+
87
+ export default sendInvite;