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/bin/index.js +14 -10
- package/commands/connect.js +41 -30
- package/commands/createTask.js +63 -189
- package/commands/init.js +86 -48
- package/commands/myTask.js +144 -71
- package/commands/myTeam.js +75 -26
- package/commands/sendInvite.js +60 -25
- package/commands/task.js +140 -85
- package/commands/taskCompletion.js +36 -13
- package/package.json +1 -1
- package/commands/send.js +0 -16
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(
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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.
|
|
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(
|
|
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({
|
|
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("
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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 || "
|
|
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).
|
|
151
|
+
task.dueDate ? new Date(task.dueDate).toLocaleDateString() : "Not set",
|
|
107
152
|
chalk.cyan.bold,
|
|
108
153
|
chalk.white
|
|
109
154
|
);
|
|
110
155
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
task.
|
|
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}.
|
|
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(`
|
|
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("
|
|
181
|
+
console.log(chalk.gray("────────────────────────────────────\n"));
|
|
127
182
|
});
|
|
128
183
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
184
|
} catch (err) {
|
|
133
|
-
|
|
134
|
-
"❌ Error:",
|
|
135
|
-
err.response
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
package/commands/send.js
DELETED