thinkncollab-cli 0.0.82 → 0.0.84

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/index.js CHANGED
@@ -17,6 +17,7 @@ import connect from '../commands/connect.js'
17
17
  import machine from "node-machine-id";
18
18
  import push from "../commands/push.js";
19
19
  import taskCompletion from "../commands/taskCompletion.js";
20
+ import task from "../commands/task.js";
20
21
 
21
22
 
22
23
 
@@ -26,6 +27,18 @@ const VERSION_FILE = path.join(process.cwd(), ".tncversions");
26
27
  const BASE_URL = "https://thinkncollab.com/rooms";
27
28
  const CWD = process.cwd();
28
29
 
30
+ async function DAPP() {
31
+ try {
32
+ const res = await axios.get(`http://localhost:4545/logout`);
33
+ console.log("✅ Command sent to ThinkNCollab Agent");
34
+ console.log(res.data.message);
35
+ } catch (err) {
36
+ console.error("❌ ThinkNCollab Agent not running");
37
+ process.exit(1);
38
+ }
39
+ }
40
+
41
+
29
42
  /** ------------------ LOGIN ------------------ **/
30
43
  async function login() {
31
44
  const answers = await inquirer.prompt([
@@ -185,10 +198,23 @@ case "invite": {
185
198
 
186
199
  await sendInvite(email);
187
200
  break;
201
+ }
202
+ case "task": {
203
+ task();
204
+ break;
205
+ }
206
+
207
+ case "signout": {
208
+ await DAPP();
209
+ break;
210
+
188
211
  }
189
212
 
190
213
  default:
191
- console.log("✅ TNC CLI ready!");
214
+ console.log('================================');
215
+ console.log(' START REAL SYSTEM SHELL ');
216
+ console.log('================================ */ ');
217
+ console.log("✅ ThinkNCollab CLI ready!");
192
218
  console.log("Commands:");
193
219
  console.log(" tnc-cli login --> to login");
194
220
  console.log(" tnc-cli init <roomId> --> to use CLI for a room");
@@ -199,7 +225,8 @@ case "invite": {
199
225
  // console.log(" tnc-cli merge <roomId>")
200
226
  // console.log(" tnc-cli status");
201
227
  console.log(" tnc-cli whoami --> to know by which Id you are loggedin");
202
- console.log(" tnc-cli my-tasks <roomId> --> to get ur tasks from the room ");
228
+ console.log(" tnc-cli my-tasks --> to get ur tasks from the room ");
229
+ console.log(" tnc-cli tasks --> to know thetask details");
203
230
  console.log(" tnc-cli task-complete --> to change the task status to complete")
204
231
  console.log(" tnc-cli logout --> to logout");
205
232
  console.log(" tnc-cli help --> to get help");
@@ -3,6 +3,7 @@ import machine from "node-machine-id";
3
3
  import fs from "fs";
4
4
  import os from "os";
5
5
  import path from "path";
6
+ import inquirer from "inquirer";
6
7
 
7
8
  const tncrcPath = path.join(os.homedir(), ".tncrc");
8
9
 
@@ -19,7 +20,7 @@ async function connect(roomId) {
19
20
  const { email, token } = JSON.parse(data);
20
21
 
21
22
  try {
22
- const response = await axios.post(`https://thinkncollab.com/cli/connect/${roomId}`, {
23
+ const response = await axios.post(`http://localhost:3001/cli/connect/${roomId}`, {
23
24
  email: email,
24
25
  token: token,
25
26
  machineId: await machine.machineIdSync(),
@@ -2,63 +2,138 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import axios from "axios";
4
4
  import os from "os";
5
+ import machine from "node-machine-id";
6
+ import CliTable3 from "cli-table3";
7
+ import chalk from "chalk";
5
8
 
6
9
  const homeDir = os.homedir();
7
- const url = "https://thinkncollab.com/cli/mytasks"; // backend endpoint
10
+ const url = "https://thinkncollab.com/cli/mytasks";
8
11
 
9
- // Get saved email from ~/.tncrc
10
- async function getEmail() {
12
+ // -----------------------------
13
+ // Read ~/.tncrc
14
+ // -----------------------------
15
+ function getAuthData() {
11
16
  const rcFile = path.join(homeDir, ".tncrc");
12
17
 
13
18
  if (!fs.existsSync(rcFile)) {
14
- console.log("⚠️ Please login first!");
19
+ console.log(chalk.red("⚠️ Please login first!"));
15
20
  process.exit(1);
16
21
  }
17
22
 
18
- const content = fs.readFileSync(rcFile, "utf-8");
19
- const email = JSON.parse(content).email;
20
- return email;
21
- }
22
- async function getToken() {
23
- const rcFile = path.join(homeDir, '.tncrc');
24
- if(!fs.readFileSync(rcFile)){
25
- console.log("⚠️ Please login first! ")
26
- }
27
- const content = fs.readFileSync(rcFile, 'utf-8');
28
- const token = JSON.parse(content).token;
29
- return token;
30
-
31
-
23
+ return JSON.parse(fs.readFileSync(rcFile, "utf-8"));
32
24
  }
33
25
 
34
- // Fetch tasks for a given room
26
+ // -----------------------------
27
+ // Main Function
28
+ // -----------------------------
35
29
  async function myTask() {
36
30
  try {
37
- const email = await getEmail();
38
- const token = await getToken();
31
+ const { email, token } = getAuthData();
32
+
39
33
  const CWD = process.cwd();
40
- const metaDataPath = path.join(".tnc", '.tncmeta.json'); // assuming room ID is the current directory name
41
- const metaData = JSON.parse(fs.readFileSync(path.join(CWD, metaDataPath), 'utf-8'));
34
+ const metaDataPath = path.join(CWD, ".tnc", ".tncmeta.json");
35
+
36
+ if (!fs.existsSync(metaDataPath)) {
37
+ console.log(chalk.yellow("⚠️ Room metadata not found in current directory!"));
38
+ return;
39
+ }
40
+
41
+ const metaData = JSON.parse(fs.readFileSync(metaDataPath, "utf-8"));
42
42
  const roomId = metaData.roomId;
43
43
 
44
- const res = await axios.get(`${url}/${roomId}`, {
45
- params: { email, token } // since backend uses req.query
46
- });
44
+ const cacheDir = path.join(CWD, ".tnc");
45
+ if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir);
46
+
47
+ const cacheFile = path.join(cacheDir, "tasks.json");
48
+
49
+ let tasks = [];
47
50
 
48
- const tasks = res.data.tasks;
51
+ try {
52
+ const res = await axios.get(`${url}/${roomId}`, {
53
+ params: {
54
+ email,
55
+ token,
56
+ machineId: machine.machineIdSync()
57
+ }
58
+ });
59
+
60
+ tasks = res.data.tasks || [];
61
+
62
+ fs.writeFileSync(cacheFile, JSON.stringify(tasks, null, 2));
63
+
64
+ } catch (error) {
65
+ if (fs.existsSync(cacheFile)) {
66
+ console.log(chalk.yellow("⚠️ Offline mode: showing cached tasks"));
67
+ tasks = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
68
+ } else {
69
+ console.error(
70
+ chalk.red("❌ Error fetching tasks and no cache available:"),
71
+ error.response?.data || error.message
72
+ );
73
+ return;
74
+ }
75
+ }
49
76
 
50
77
  if (!tasks.length) {
51
- console.log("📭 No tasks assigned.");
78
+ console.log(chalk.gray("📭 No tasks assigned."));
52
79
  return;
53
80
  }
54
81
 
55
- console.log("📋 Your Tasks:");
56
- tasks.forEach((task, i) => {
57
- console.log(`${i + 1}. ${task.title} — ${task.status} ${task._id}`);
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"]
99
+ }
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;
114
+
115
+ // Priority color
116
+ let priorityColor =
117
+ task.priority === "high"
118
+ ? chalk.red
119
+ : task.priority === "medium"
120
+ ? chalk.yellow
121
+ : chalk.green;
122
+
123
+ table.push([
124
+ chalk.white(task.title),
125
+ statusColor(task.status),
126
+ chalk.magenta(task.category || "General"),
127
+ priorityColor(task.priority || "low"),
128
+ chalk.gray(task._id),
129
+ chalk.blue(task.attachments?.length || 0)
130
+ ]);
58
131
  });
59
132
 
133
+ console.log(table.toString());
134
+
60
135
  } catch (error) {
61
- console.error("❌ Error fetching tasks:", error.response?.data || error.message);
136
+ console.error(chalk.red("❌ Error:"), error.message);
62
137
  }
63
138
  }
64
139
 
@@ -0,0 +1,190 @@
1
+ import axios from "axios";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import machine from "node-machine-id";
6
+ import inquirer from "inquirer";
7
+ import chalk from "chalk";
8
+
9
+ const CWD = process.cwd();
10
+ const tncrcPath = path.join(os.homedir(), ".tncrc");
11
+ const taskFilePath = path.join(CWD, ".tnc", "tasks.json");
12
+
13
+ async function task() {
14
+ try {
15
+ // Validate login
16
+ if (!fs.existsSync(tncrcPath)) {
17
+ console.error("❌ You are not logged in. Run 'tnc login' first.");
18
+ return;
19
+ }
20
+
21
+ // Validate project init
22
+ if (!fs.existsSync(taskFilePath)) {
23
+ console.error("❌ This directory is not initialized. Run 'tnc init' first.");
24
+ return;
25
+ }
26
+
27
+ // Ask Task ID
28
+ const { taskId } = await inquirer.prompt([
29
+ {
30
+ type: "input",
31
+ name: "taskId",
32
+ message: "Enter Task ID:",
33
+ },
34
+ ]);
35
+
36
+ if (!taskId?.trim()) {
37
+ console.error("❌ Task ID cannot be empty.");
38
+ return;
39
+ }
40
+
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
+ `http://localhost:3001/cli/task/${taskId.trim()}`,
48
+ {
49
+ headers: {
50
+ email,
51
+ token,
52
+ machineId: machine.machineIdSync(),
53
+ },
54
+ }
55
+ );
56
+
57
+ const serverTasks = Array.isArray(response.data.task)
58
+ ? response.data.task
59
+ : [response.data.task];
60
+
61
+ // Sync with local file
62
+ serverTasks.forEach((serverTask) => {
63
+ const index = localTasks.findIndex((t) => t.id === serverTask.id);
64
+
65
+ if (index !== -1) {
66
+ localTasks[index] = {
67
+ ...localTasks[index],
68
+ ...serverTask,
69
+ };
70
+ } else {
71
+ localTasks.push(serverTask);
72
+ }
73
+ });
74
+
75
+ fs.writeFileSync(
76
+ taskFilePath,
77
+ JSON.stringify({ tasks: localTasks }, null, 2)
78
+ );
79
+
80
+ console.log("✅ Task synced successfully");
81
+
82
+ serverTasks.forEach(task => {
83
+ console.log(chalk.gray("\n------------------------------------------"));
84
+
85
+ const statusColor =
86
+ task.status === "completed"
87
+ ? chalk.green
88
+ : task.status === "in-progress"
89
+ ? chalk.yellow
90
+ : chalk.red;
91
+ const priorityColor =
92
+ task.priority === "high"
93
+ ? chalk.red
94
+ : task.priority === "medium"
95
+ ? chalk.yellow
96
+ : chalk.green;
97
+
98
+
99
+ printField("Task ID", task._id, chalk.cyan.bold, chalk.white);
100
+ printField("Title", task.title, chalk.green.bold, chalk.white);
101
+ printField("Description", task.description || "N/A", chalk.yellow.bold, chalk.white);
102
+ printField("Status", task.status, chalk.blue.bold, statusColor);
103
+ printField("Priority", task.priority, chalk.magenta.bold, priorityColor);
104
+ printField(
105
+ "Due Date",
106
+ task.dueDate ? new Date(task.dueDate).toDateString() : "N/A",
107
+ chalk.cyan.bold,
108
+ chalk.white
109
+ );
110
+
111
+ if (task.attachments?.length > 0) {
112
+ printField("Attachments", `${task.attachments.length}`, chalk.blue.bold, chalk.white);
113
+
114
+ task.attachments.forEach((att, index) => {
115
+ console.log(
116
+ chalk.gray(` ${index + 1}. ID: ${att._id}`)
117
+ );
118
+ console.log(
119
+ chalk.gray(` Name: ${att.name}`)
120
+ );
121
+ });
122
+ } else {
123
+ printField("Attachments", "None", chalk.blue.bold, chalk.gray);
124
+ }
125
+
126
+ console.log(chalk.gray("------------------------------------------\n"));
127
+ });
128
+
129
+
130
+
131
+
132
+ } catch (err) {
133
+ console.error(
134
+ "❌ Error:",
135
+ err.response?.data?.error || err.message
136
+ );
137
+ }
138
+ }
139
+
140
+ 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 = "";
155
+
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 + " ";
165
+ }
166
+ } else {
167
+ currentLine += word + " ";
168
+ }
169
+ });
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
+ }
187
+
188
+
189
+
190
+ export default task;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thinkncollab-cli",
3
3
  "author": "Raman Singh",
4
- "version": "0.0.82",
4
+ "version": "0.0.84",
5
5
  "description": "CLI tool for ThinkNCollab",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -14,7 +14,8 @@
14
14
  "dependencies": {
15
15
  "axios": "^1.12.2",
16
16
  "chalk": "^5.6.2",
17
+ "cli-table3": "^0.6.5",
17
18
  "inquirer": "^9.3.8",
18
19
  "node-machine-id": "^1.1.12"
19
20
  }
20
- }
21
+ }