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.
package/commands/task.js CHANGED
@@ -7,7 +7,9 @@ import inquirer from "inquirer";
7
7
  import chalk from "chalk";
8
8
 
9
9
  const CWD = process.cwd();
10
+ const BASE_URL = "https://thinkncollab.cm/cli";
10
11
  const tncrcPath = path.join(os.homedir(), ".tncrc");
12
+ const tncMetaPath = path.join(CWD, ".tnc", ".tncmeta.json");
11
13
  const taskFilePath = path.join(CWD, ".tnc", "tasks.json");
12
14
 
13
15
  async function task() {
@@ -19,11 +21,20 @@ async function task() {
19
21
  }
20
22
 
21
23
  // Validate project init
22
- if (!fs.existsSync(taskFilePath)) {
24
+ if (!fs.existsSync(tncMetaPath)) {
23
25
  console.error("❌ This directory is not initialized. Run 'tnc init' first.");
24
26
  return;
25
27
  }
26
28
 
29
+ // Read credentials
30
+ const tncrcData = fs.readFileSync(tncrcPath, "utf-8");
31
+ const { email, token } = JSON.parse(tncrcData);
32
+ const machineId = machine.machineIdSync();
33
+
34
+ // Read project metadata
35
+ const tncMetaData = fs.readFileSync(tncMetaPath, "utf-8");
36
+ const { projectId, projectName } = JSON.parse(tncMetaData);
37
+
27
38
  // Ask Task ID
28
39
  const { taskId } = await inquirer.prompt([
29
40
  {
@@ -38,153 +49,197 @@ async function task() {
38
49
  return;
39
50
  }
40
51
 
41
- const { email, token } = JSON.parse(fs.readFileSync(tncrcPath, "utf-8"));
42
- const localData = JSON.parse(fs.readFileSync(taskFilePath, "utf-8"));
43
- const localTasks = localData.tasks || [];
44
-
45
- // API call (correct way using headers)
46
- const response = await axios.get(
47
- `https://thinkncollab.com/cli/task/${taskId.trim()}`,
48
- {
49
- headers: {
50
- email,
51
- token,
52
- machineId: machine.machineIdSync(),
53
- },
52
+ console.log(`📋 Fetching task ${taskId.trim()} from project: ${projectName}`);
53
+
54
+ // API call with headers for authentication
55
+ const response = await axios({
56
+ method: 'get',
57
+ url: `${BASE_URL}/task/${taskId.trim()}`,
58
+ headers: {
59
+ 'x-user-email': email,
60
+ 'x-user-token': token,
61
+ 'x-machine-id': machineId,
62
+ 'x-project-id': projectId // Optional: for project context
54
63
  }
55
- );
64
+ });
56
65
 
66
+ // Handle response data
57
67
  const serverTasks = Array.isArray(response.data.task)
58
68
  ? response.data.task
59
69
  : [response.data.task];
60
70
 
71
+ // Read or initialize local tasks file
72
+ let localData = { tasks: [] };
73
+ if (fs.existsSync(taskFilePath)) {
74
+ try {
75
+ localData = JSON.parse(fs.readFileSync(taskFilePath, "utf-8"));
76
+ } catch (e) {
77
+ console.log("⚠️ Corrupted tasks file, creating new one");
78
+ }
79
+ }
80
+
81
+ const localTasks = localData.tasks || [];
82
+
61
83
  // Sync with local file
62
84
  serverTasks.forEach((serverTask) => {
63
- const index = localTasks.findIndex((t) => t.id === serverTask.id);
85
+ const index = localTasks.findIndex((t) => t._id === serverTask._id);
64
86
 
65
87
  if (index !== -1) {
66
88
  localTasks[index] = {
67
89
  ...localTasks[index],
68
90
  ...serverTask,
91
+ lastSynced: new Date().toISOString()
69
92
  };
70
93
  } else {
71
- localTasks.push(serverTask);
94
+ localTasks.push({
95
+ ...serverTask,
96
+ lastSynced: new Date().toISOString()
97
+ });
72
98
  }
73
99
  });
74
100
 
101
+ // Ensure .tnc folder exists
102
+ const tncFolder = path.join(CWD, ".tnc");
103
+ if (!fs.existsSync(tncFolder)) {
104
+ fs.mkdirSync(tncFolder, { recursive: true });
105
+ }
106
+
107
+ // Write updated tasks
75
108
  fs.writeFileSync(
76
109
  taskFilePath,
77
- JSON.stringify({ tasks: localTasks }, null, 2)
110
+ JSON.stringify({
111
+ tasks: localTasks,
112
+ lastUpdated: new Date().toISOString(),
113
+ projectId
114
+ }, null, 2)
78
115
  );
79
116
 
80
- console.log("✅ Task synced successfully");
117
+ console.log("✅ Task synced successfully\n");
81
118
 
119
+ // Display task information
82
120
  serverTasks.forEach(task => {
83
- console.log(chalk.gray("\n------------------------------------------"));
121
+ console.log(chalk.gray("────────────────────────────────────"));
84
122
 
85
123
  const statusColor =
86
124
  task.status === "completed"
87
125
  ? chalk.green
88
126
  : task.status === "in-progress"
89
127
  ? chalk.yellow
90
- : chalk.red;
128
+ : task.status === "todo"
129
+ ? chalk.blue
130
+ : chalk.red;
131
+
91
132
  const priorityColor =
92
133
  task.priority === "high"
93
134
  ? chalk.red
94
135
  : task.priority === "medium"
95
136
  ? chalk.yellow
96
- : chalk.green;
97
-
137
+ : task.priority === "low"
138
+ ? chalk.green
139
+ : chalk.gray;
98
140
 
141
+ // Print task details
99
142
  printField("Task ID", task._id, chalk.cyan.bold, chalk.white);
100
143
  printField("Title", task.title, chalk.green.bold, chalk.white);
101
- printField("Description", task.description || "N/A", chalk.yellow.bold, chalk.white);
144
+ printField("Description", task.description || "No description", chalk.yellow.bold, chalk.white);
102
145
  printField("Status", task.status, chalk.blue.bold, statusColor);
103
- printField("Priority", task.priority, chalk.magenta.bold, priorityColor);
146
+ printField("Priority", task.priority || "Not set", chalk.magenta.bold, priorityColor);
147
+ printField("Created At", task.createdAt ? new Date(task.createdAt).toLocaleString() : "N/A", chalk.cyan.bold, chalk.white);
148
+ printField("Updated At", task.updatedAt ? new Date(task.updatedAt).toLocaleString() : "N/A", chalk.cyan.bold, chalk.white);
104
149
  printField(
105
150
  "Due Date",
106
- task.dueDate ? new Date(task.dueDate).toDateString() : "N/A",
151
+ task.dueDate ? new Date(task.dueDate).toLocaleDateString() : "Not set",
107
152
  chalk.cyan.bold,
108
153
  chalk.white
109
154
  );
110
155
 
111
- if (task.attachments?.length > 0) {
112
- printField("Attachments", `${task.attachments.length}`, chalk.blue.bold, chalk.white);
113
-
114
- task.attachments.forEach((att, index) => {
156
+ // Show assigned users if any
157
+ if (task.assignedTo && task.assignedTo.length > 0) {
158
+ printField("Assigned To", `${task.assignedTo.length} user(s)`, chalk.blue.bold, chalk.white);
159
+ task.assignedTo.forEach((user, index) => {
115
160
  console.log(
116
- chalk.gray(` ${index + 1}. ID: ${att._id}`)
161
+ chalk.gray(` ${index + 1}. ${ user.userId.name} , ${(user.userId.email)} [Status: ${user.status}]`)
117
162
  );
163
+ });
164
+ }
165
+
166
+ // Show attachments if any
167
+ if (task.attachments?.length > 0) {
168
+ printField("Attachments", `${task.attachments.length} file(s)`, chalk.blue.bold, chalk.white);
169
+ task.attachments.forEach((att, index) => {
118
170
  console.log(
119
- chalk.gray(` Name: ${att.name}`)
171
+ chalk.gray(` ${index + 1}. ${att.name || att.filename || 'Attachment'}`)
120
172
  );
173
+ if (att.url) {
174
+ console.log(chalk.gray(` URL: ${att.url}`));
175
+ }
121
176
  });
122
177
  } else {
123
178
  printField("Attachments", "None", chalk.blue.bold, chalk.gray);
124
179
  }
125
180
 
126
- console.log(chalk.gray("------------------------------------------\n"));
181
+ console.log(chalk.gray("────────────────────────────────────\n"));
127
182
  });
128
183
 
129
-
130
-
131
-
132
184
  } catch (err) {
133
- console.error(
134
- "❌ Error:",
135
- err.response?.data?.error || err.message
136
- );
185
+ if (err.response) {
186
+ console.error("❌ Server Error:", err.response.data.message || err.response.data.error);
187
+ if (err.response.status === 401) {
188
+ console.error(" Unauthorized. Please login again.");
189
+ } else if (err.response.status === 404) {
190
+ console.error(" Task not found. Please check the Task ID.");
191
+ } else if (err.response.status === 403) {
192
+ console.error(" You don't have permission to access this task.");
193
+ }
194
+ } else if (err.request) {
195
+ console.error("❌ No response from server. Check your internet connection.");
196
+ } else {
197
+ console.error("❌ Error:", err.message);
198
+ }
137
199
  }
138
200
  }
139
201
 
140
202
  function printField(label, value, labelColor, valueColor, width = 15) {
141
- const terminalWidth = process.stdout.columns || 80;
142
-
143
- const plainLabel = label.padEnd(width);
144
- const labelPart = `${plainLabel} : `;
145
- const indent = " ".repeat(labelPart.length);
146
-
147
- const availableWidth = terminalWidth - indent.length;
148
-
149
- // Normalize multiline input
150
- const rawLines = String(value).replace(/\r/g, "").split("\n");
151
-
152
- rawLines.forEach((rawLine, lineIndex) => {
153
- let words = rawLine.split(" ");
154
- let currentLine = "";
203
+ const terminalWidth = process.stdout.columns || 80;
204
+ const plainLabel = label.padEnd(width);
205
+ const labelPart = `${plainLabel} : `;
206
+ const indent = " ".repeat(labelPart.length);
207
+ const availableWidth = terminalWidth - indent.length;
208
+
209
+ // Normalize multiline input
210
+ const rawLines = String(value).replace(/\r/g, "").split("\n");
211
+
212
+ rawLines.forEach((rawLine, lineIndex) => {
213
+ let words = rawLine.split(" ");
214
+ let currentLine = "";
215
+
216
+ words.forEach(word => {
217
+ if ((currentLine + word).length > availableWidth) {
218
+ if (lineIndex === 0 && currentLine === "") {
219
+ console.log(
220
+ labelColor(plainLabel) + " : " + valueColor(word)
221
+ );
222
+ } else {
223
+ console.log(indent + valueColor(currentLine.trim()));
224
+ currentLine = word + " ";
225
+ }
226
+ } else {
227
+ currentLine += word + " ";
228
+ }
229
+ });
155
230
 
156
- words.forEach(word => {
157
- if ((currentLine + word).length > availableWidth) {
158
- if (lineIndex === 0 && currentLine === "") {
159
- console.log(
160
- labelColor(plainLabel) + " : " + valueColor(word)
161
- );
162
- } else {
163
- console.log(indent + valueColor(currentLine.trim()));
164
- currentLine = word + " ";
231
+ if (currentLine.trim()) {
232
+ if (lineIndex === 0) {
233
+ console.log(
234
+ labelColor(plainLabel) +
235
+ " : " +
236
+ valueColor(currentLine.trim())
237
+ );
238
+ } else {
239
+ console.log(indent + valueColor(currentLine.trim()));
240
+ }
165
241
  }
166
- } else {
167
- currentLine += word + " ";
168
- }
169
242
  });
170
-
171
- if (currentLine.trim()) {
172
- if (lineIndex === 0) {
173
- console.log(
174
- labelColor(plainLabel) +
175
- " : " +
176
- valueColor(currentLine.trim())
177
- );
178
- } else {
179
- console.log(indent + valueColor(currentLine.trim()));
180
- }
181
- }
182
-
183
- // After first printed line, ensure next raw lines are indented
184
- lineIndex = 1;
185
- });
186
243
  }
187
244
 
188
-
189
-
190
- export default task;
245
+ export default task;
@@ -5,31 +5,35 @@ import path from 'path';
5
5
  import machine from 'node-machine-id';
6
6
  import inquirer from "inquirer";
7
7
 
8
-
9
-
10
8
  const CWD = process.cwd();
11
-
9
+ const BASE_URL = "https://thinkncollab.com/cli"; // Use your actual base URL
12
10
  const tncrcPath = path.join(os.homedir(), '.tncrc');
13
11
  const tncMetaPath = path.join(CWD, '.tnc', '.tncmeta.json');
14
12
 
15
13
  async function taskCompletion() {
16
14
  try {
15
+ // Check login status
17
16
  if (!fs.existsSync(tncrcPath)) {
18
17
  console.error("❌ You are not logged in. Run 'tnc login' first.");
19
18
  process.exit(1);
20
19
  }
21
20
 
21
+ // Check project initialization
22
22
  if (!fs.existsSync(tncMetaPath)) {
23
23
  console.error("❌ This directory is not initialized. Run 'tnc init' first.");
24
24
  process.exit(1);
25
25
  }
26
26
 
27
+ // Read credentials
27
28
  const tncrcData = fs.readFileSync(tncrcPath, "utf-8");
28
29
  const { email, token } = JSON.parse(tncrcData);
30
+ const machineId = machine.machineIdSync();
29
31
 
32
+ // Read project metadata
30
33
  const tncMetaData = fs.readFileSync(tncMetaPath, "utf-8");
31
- const { projectId } = JSON.parse(tncMetaData);
34
+ const { projectId, projectName } = JSON.parse(tncMetaData);
32
35
 
36
+ // Get task ID from user
33
37
  const { taskId } = await inquirer.prompt([
34
38
  {
35
39
  type: "input",
@@ -45,23 +49,42 @@ async function taskCompletion() {
45
49
  process.exit(1);
46
50
  }
47
51
 
48
- const response = await axios.post(
49
- `https://thinkncollab.com/cli/taskcomplete/${trimmedTaskId}`,
50
- {
51
- email,
52
- token,
53
- machineId: machine.machineIdSync(),
52
+ console.log(`📋 Marking task ${trimmedTaskId} as completed...`);
53
+
54
+ // USING HEADERS FOR AUTHENTICATION
55
+ const response = await axios({
56
+ method: 'post',
57
+ url: `${BASE_URL}/taskcomplete/${trimmedTaskId}`,
58
+ headers: {
59
+ 'x-user-email': email,
60
+ 'x-user-token': token,
61
+ 'x-machine-id': machineId,
62
+ 'x-project-id': projectId // Optional: send project context
54
63
  }
55
- );
64
+ // NO REQUEST BODY - all auth in headers
65
+ });
66
+
67
+ console.log("✅", response.data.message);
68
+
69
+ // Optional: Update local metadata if needed
70
+ console.log(`📌 Project: ${projectName}`);
71
+ console.log(`🔗 Task ID: ${trimmedTaskId}`);
56
72
 
57
- console.log("✅ Task marked as complete:", response.data.message);
58
73
  } catch (err) {
59
74
  if (err.response) {
60
- console.error("❌ Error:", err.response.data.error);
75
+ console.error("❌ Error:", err.response.data.message || err.response.data.error);
76
+ if (err.response.status === 401) {
77
+ console.error(" Please login again - invalid or expired token.");
78
+ } else if (err.response.status === 404) {
79
+ console.error(" Task not found. Please check the Task ID.");
80
+ }
81
+ } else if (err.request) {
82
+ console.error("❌ No response from server. Check your internet connection.");
61
83
  } else {
62
84
  console.error("❌ An unexpected error occurred:", err.message);
63
85
  }
64
86
  process.exit(1);
65
87
  }
66
88
  }
89
+
67
90
  export default taskCompletion;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thinkncollab-cli",
3
3
  "author": "Raman Singh",
4
- "version": "0.0.89",
4
+ "version": "0.0.91",
5
5
  "description": "CLI tool for ThinkNCollab",
6
6
  "main": "index.js",
7
7
  "bin": {
package/commands/send.js DELETED
@@ -1,16 +0,0 @@
1
- import axios from "axios"
2
-
3
- let data = {
4
-
5
- }
6
-
7
- function sendData() {
8
- const res = axios.post('http://172.16.44.215:5001/senddata',
9
- {
10
- "name": "Raman Singh",
11
- "email": "admin@thinkncollab.com"
12
- }
13
- );
14
-
15
- }
16
- sendData();