thinkncollab-cli 0.0.90 → 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 CHANGED
@@ -175,10 +175,16 @@ async function main() {
175
175
  break;
176
176
 
177
177
  case "my-tasks": {
178
- const roomIdx = args.indexOf("my-tasks");
179
- const roomId = args[roomIdx + 1];
180
-
181
- await myTask();
178
+ const n = args.indexOf("-n");
179
+ let limit = 5;
180
+ if (n !== -1 && args[n + 1]) {
181
+ limit = parseInt(args[n + 1], 10);
182
+ if (isNaN(limit) || limit <= 0) {
183
+ console.log("⚠️ Invalid number for -n. Using default of 5.");
184
+ limit = 5;
185
+ }
186
+ }
187
+ await myTask(limit);
182
188
  break;
183
189
  }
184
190
  case "create-task": {
@@ -236,9 +242,6 @@ default:
236
242
  console.log(" tnc-cli status Show project status");
237
243
  console.log("");
238
244
 
239
- console.log("Branch:");
240
- console.log(" tnc-cli create --room <roomId> Create branch for a room");
241
- console.log("");
242
245
 
243
246
 
244
247
  console.log("Tasks:");
@@ -248,6 +251,7 @@ default:
248
251
  console.log(" tnc-cli task-complete Mark task as completed");
249
252
  console.log("");
250
253
 
254
+
251
255
  console.log("Team:");
252
256
  console.log(" tnc-cli myteam Show team members");
253
257
  console.log(" tnc-cli invite <email> Invite member to room");
@@ -12,56 +12,67 @@ async function connect(roomId) {
12
12
  console.error("❌ You are not logged in. Run 'tnc login' first.");
13
13
  process.exit(1);
14
14
  }
15
- const answer = await inquirer.prompt([
16
- { type: "input", name: "BranchName", message: "Enter Branch Name to create a branch:" }
17
- ]);
15
+
16
+ const answer = await inquirer.prompt([
17
+ { type: "input", name: "BranchName", message: "Enter Branch Name to create a branch:" }
18
+ ]);
18
19
 
19
20
  const data = fs.readFileSync(tncrcPath, "utf-8");
20
21
  const { email, token } = JSON.parse(data);
22
+ const machineId = machine.machineIdSync();
21
23
 
22
24
  try {
23
- const response = await axios.post(`https://thinkncollab.com/cli/connect/${roomId}`, {
24
- email: email,
25
- token: token,
26
- machineId: await machine.machineIdSync(),
27
- branchName: answer.BranchName,
25
+ // USING HEADERS FOR AUTHENTICATION
26
+ const response = await axios({
27
+ method: 'post',
28
+ url: `https://thinkncollab.com/cli/connect/${roomId}`,
29
+ data: {
30
+ branchName: answer.BranchName // Only branchName in request body
31
+ },
32
+ headers: {
33
+ 'x-user-email': email,
34
+ 'x-user-token': token,
35
+ 'x-machine-id': machineId
36
+ }
28
37
  });
29
- const CWD= process.cwd();
38
+
39
+ const CWD = process.cwd();
30
40
  const tncFolderPath = path.join(CWD, ".tnc");
31
- if (!fs.existsSync(tncFolderPath)) fs.mkdirSync(tncFolderPath, { recursive: true });
32
-
33
- // Write metadata file
34
- const metaFilePath = path.join(tncFolderPath, ".tncmeta.json");
35
- const pushFilePath = path.join(tncFolderPath, ".tncpush.json");
41
+
42
+ if (!fs.existsSync(tncFolderPath)) {
43
+ fs.mkdirSync(tncFolderPath, { recursive: true });
44
+ }
45
+
46
+ // Write metadata file
47
+ const metaFilePath = path.join(tncFolderPath, ".tncmeta.json");
48
+ const pushFilePath = path.join(tncFolderPath, ".tncpush.json");
36
49
 
37
50
  const metaFileInfo = JSON.stringify({
38
51
  "projectId": response.data.project._id,
39
52
  "projectName": response.data.project.name,
40
53
  "roomId": response.data.project.roomId,
41
- "branch": null
42
- });
54
+ "currentBranch": answer.BranchName,
55
+ "lastCommit": null,
56
+ "files": {}
57
+ }, null, 2);
58
+
43
59
  fs.writeFileSync(metaFilePath, metaFileInfo);
44
- fs.writeFileSync(
45
- pushFilePath,
46
- " {} "
47
- )
48
-
49
- console.log(response.data);
50
-
51
-
52
-
53
-
60
+ fs.writeFileSync(pushFilePath, JSON.stringify({}, null, 2));
54
61
 
55
62
  console.log("✅ Connected to project:", response.data.project.name);
56
- console.log("Members connected:", response.data.project.membersConnected);
63
+ console.log("👥 Members connected:", response.data.project.membersConnected.length);
64
+ console.log("🌿 Current branch:", answer.BranchName);
57
65
 
58
66
  } catch (err) {
59
67
  if (err.response) {
60
- console.error(" Error:", err.response.data.error);
68
+ console.error(" Error:", err.response.data.error || err.response.data.message);
69
+ if (err.response.status === 401) {
70
+ console.error(" Please login again.");
71
+ }
61
72
  } else {
62
- console.error(" Error:", err.message);
73
+ console.error(" Error:", err.message);
63
74
  }
64
75
  }
65
76
  }
66
77
 
67
- export default connect;
78
+ export default connect;
@@ -1,6 +1,5 @@
1
1
  import axios from "axios";
2
2
  import fs from "fs";
3
- import os from "os";
4
3
  import path from "path";
5
4
  import machine from "node-machine-id";
6
5
  import chalk from "chalk";
@@ -9,250 +8,125 @@ import open from 'open';
9
8
  import http from 'http';
10
9
 
11
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
-
13
-
14
11
  const CWD = process.cwd();
15
-
16
-
17
- function createSpinner(message) {
18
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
19
- let i = 0;
20
-
21
- const spinner = setInterval(() => {
22
- process.stdout.write(`\r${chalk.cyan(frames[i])} ${message}`);
23
- i = (i + 1) % frames.length;
24
- }, 80);
25
-
26
- return {
27
- stop: (clearMessage = true) => {
28
- clearInterval(spinner);
29
- if (clearMessage) {
30
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 50) + '\r');
31
- }
32
- },
33
- update: (newMessage) => {
34
- message = newMessage;
35
- }
36
- };
37
- }
12
+ const BASE_URL = "https://thinkncollab.com/cli";
38
13
 
39
14
  async function createTask() {
40
15
  try {
41
- const { default: getVerify } = await import("../lib/getVerify.js");
16
+ const { default: getVerify } = await import("../lib/getVerify.js");
17
+ const { email, token } = await getVerify();
18
+ const machineId = machine.machineIdSync();
42
19
 
43
- const { token } = await getVerify();
44
-
45
- const CWD = process.cwd();
46
20
  const tncmetaPath = path.join(CWD, ".tnc", ".tncmeta.json");
47
21
 
48
22
  if (!fs.existsSync(tncmetaPath)) {
49
23
  console.error(chalk.red("❌ Not inside a ThinkNCollab project."));
50
- console.log(chalk.yellow("Run `tnc-cli init <roomId>` first."));
51
24
  return;
52
25
  }
53
26
 
54
- const fileData = JSON.parse(
55
- fs.readFileSync(tncmetaPath, "utf-8")
56
- );
57
-
27
+ const fileData = JSON.parse(fs.readFileSync(tncmetaPath, "utf-8"));
58
28
  const roomId = fileData.roomId;
29
+ const projectName = fileData.projectName || "Unknown Project";
59
30
 
60
31
  if (!roomId) {
61
32
  console.error(chalk.red("❌ No roomId found."));
62
33
  return;
63
34
  }
64
- console.log(chalk.blue('\n🚀 Task Creation CLI\n'));
65
-
66
- // Validate roomId and token
67
- if (!roomId) {
68
- console.error(chalk.red('❌ No roomId found in .tnc/.tncmeta.json'));
69
- console.log(chalk.yellow('Please ensure you are in a ThinkNCollab project directory'));
70
- return;
71
- }
72
-
73
- if (!token) {
74
- console.error(chalk.red('❌ No token found'));
75
- console.log(chalk.yellow('Please login first using: tnc login'));
76
- return;
77
- }
78
35
 
79
- // STEP 1: Find available port FIRST
80
- console.log(chalk.gray('🔍 Finding available port for callback server...'));
81
- const PORT = await findAvailablePort(3002);
82
- console.log(chalk.green(`✅ Callback server will run on port: ${PORT}`));
36
+ console.log(chalk.blue('\n🚀 Task Creation CLI'));
37
+ console.log(chalk.gray(`📁 Project: ${projectName}`));
38
+ console.log(chalk.gray(`🏠 Room ID: ${roomId}\n`));
83
39
 
84
- // STEP 2: Construct the URL with the found port
85
- const redirectUrl = `https://thinkncollab.in/cli/tasks/${roomId}/${token}/create?cli=true&callbackPort=${PORT}`;
40
+ // STEP 1: Find available port for callback
41
+ console.log(chalk.gray('🔍 Finding available port for callback...'));
42
+ const CALLBACK_PORT = await findAvailablePort(3002);
43
+ console.log(chalk.green(`✅ Callback server on port: ${CALLBACK_PORT}`));
86
44
 
87
- const spinner = createSpinner('Preparing task creation interface...');
45
+ // STEP 2: Create a session first using a direct API call
46
+ console.log(chalk.blue('\n🔐 Creating secure session...'));
88
47
 
89
- console.log(chalk.blue('🔗 Opening task creation interface in your browser...'));
90
- console.log(chalk.gray(`URL: ${redirectUrl}`));
48
+ const sessionResponse = await axios.post(`${BASE_URL}/create-session`, {}, {
49
+ headers: {
50
+ 'x-user-email': email,
51
+ 'x-user-token': token,
52
+ 'x-machine-id': machineId,
53
+ 'x-room-id': roomId
54
+ }
55
+ });
91
56
 
92
- spinner.stop();
57
+ const { sessionToken } = sessionResponse.data;
58
+ console.log(chalk.green(`✅ Session created successfully!`));
93
59
 
94
60
  // STEP 3: Create callback server
95
- const server = http.createServer((req, res) => {
96
- console.log(chalk.yellow(`\n📨 Callback received: ${req.url}`));
97
-
98
- // CORS headers for all responses
99
- res.setHeader('Access-Control-Allow-Origin', '*');
100
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
101
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-CLI-Callback-URL');
102
- res.setHeader('Access-Control-Max-Age', '86400'); // 24 hours
103
-
104
- // Handle preflight
105
- if (req.method === 'OPTIONS') {
106
- res.writeHead(200);
107
- res.end();
108
- return;
109
- }
110
-
111
- // Parse URL
112
- const url = new URL(req.url, `http://localhost:${PORT}`);
113
-
114
- // Check for callback (handle both /callback and direct query params)
115
- if (req.url.includes('/callback') || url.searchParams.has('taskId')) {
61
+ const callbackServer = http.createServer((req, res) => {
62
+ if (req.url.includes('/callback') || req.url.includes('?taskId=')) {
63
+ const url = new URL(req.url, `http://localhost:${CALLBACK_PORT}`);
116
64
  const taskId = url.searchParams.get('taskId');
117
- const attachments = url.searchParams.get('attachments');
118
- const status = url.searchParams.get('status');
65
+ const taskTitle = url.searchParams.get('title');
119
66
 
120
- // Log success
121
- if (status === 'error') {
122
- console.log(chalk.red('\n❌ Task creation failed!'));
123
- } else {
124
- console.log(chalk.green('\n✅ Task created successfully!'));
125
- if (taskId) {
126
- console.log(chalk.blue(`📋 Task ID: ${taskId}`));
127
-
128
- // Save task ID to file
129
- const taskMetaPath = path.join(CWD, ".tnc", "last-task.json");
130
- fs.writeFileSync(taskMetaPath, JSON.stringify({
131
- taskId,
132
- attachments: attachments ? parseInt(attachments) : 0,
133
- timestamp: new Date().toISOString(),
134
- roomId
135
- }, null, 2));
136
- console.log(chalk.gray(`📝 Task ID saved to .tnc/last-task.json`));
137
- }
138
- }
67
+ console.log(chalk.green(`\n✅ Task Created: ${taskTitle || 'Untitled'} (ID: ${taskId})`));
139
68
 
140
- // Send response to close browser (HTML with auto-close)
141
- res.writeHead(200, {
142
- 'Content-Type': 'text/html',
143
- 'Access-Control-Allow-Origin': '*'
144
- });
69
+ res.writeHead(200, { 'Content-Type': 'text/html' });
145
70
  res.end(`
146
- <!DOCTYPE html>
147
71
  <html>
148
- <head>
149
- <title>${status === 'error' ? 'Failed' : 'Success'}</title>
150
- <style>
151
- body {
152
- font-family: Arial, sans-serif;
153
- display: flex;
154
- justify-content: center;
155
- align-items: center;
156
- height: 100vh;
157
- margin: 0;
158
- background: ${status === 'error' ? '#f44336' : '#4CAF50'};
159
- color: white;
160
- }
161
- .message {
162
- text-align: center;
163
- padding: 20px;
164
- }
165
- </style>
166
- </head>
167
- <body>
168
- <div class="message">
169
- <h1>${status === 'error' ? '❌ Task Failed' : '✅ Task Created'}</h1>
170
- ${taskId ? `<p>Task ID: ${taskId}</p>` : ''}
171
- <p>Closing in 3 seconds...</p>
172
- </div>
173
- <script>
174
- // Send one more callback to ensure it was received
175
- fetch(window.location.href, { method: 'GET', mode: 'no-cors' });
176
-
177
- // Auto close
178
- setTimeout(() => window.close(), 3000);
179
-
180
- // Fallback
181
- setTimeout(() => {
182
- window.location.href = 'about:blank';
183
- setTimeout(() => window.close(), 100);
184
- }, 4000);
185
- </script>
186
- </body>
72
+ <body style="font-family: Arial; text-align: center; padding: 50px;">
73
+ <h2>✅ Task Created Successfully!</h2>
74
+ <p>Task ID: ${taskId}</p>
75
+ <p>Title: ${taskTitle || 'Untitled'}</p>
76
+ <p>You can close this window now.</p>
77
+ <script>
78
+ setTimeout(() => window.close(), 2000);
79
+ </script>
80
+ </body>
187
81
  </html>
188
82
  `);
189
83
 
190
- // Close server after response
84
+ // Close servers after callback
191
85
  setTimeout(() => {
192
- server.close();
193
- console.log(chalk.gray('\n📡 Callback server closed'));
86
+ callbackServer.close();
87
+ console.log(chalk.green('\n Task creation complete!'));
194
88
  process.exit(0);
195
89
  }, 2000);
196
90
  } else {
197
- // Handle other requests (like favicon)
198
91
  res.writeHead(404);
199
92
  res.end('Not found');
200
93
  }
201
94
  });
202
95
 
203
- // Server error handling
204
- server.on('error', (err) => {
205
- if (err.code === 'EADDRINUSE') {
206
- console.log(chalk.yellow(`⚠️ Port ${PORT} is already in use. Callback might not work.`));
207
- console.log(chalk.yellow('The task will still be created, but you may need to check manually.'));
208
- } else {
209
- console.error(chalk.red('Server error:'), err.message);
210
- }
96
+ callbackServer.listen(CALLBACK_PORT, () => {
97
+ console.log(chalk.green(`✅ Callback server on http://localhost:${CALLBACK_PORT}`));
211
98
  });
212
99
 
213
- // Start server
214
- server.listen(PORT, () => {
215
- console.log(chalk.gray(`📡 Waiting for callback on http://localhost:${PORT}/callback`));
216
- });
100
+ // STEP 4: Open the task creation page directly with session token
101
+ const taskCreationUrl = `http://localhost:3001/cli/tasks/${roomId}/create?session=${sessionToken}&cli=true&callbackPort=${CALLBACK_PORT}`;
102
+
103
+ console.log(chalk.blue('\n🔗 Opening task creation interface...'));
217
104
 
218
- // Open browser
219
105
  setTimeout(async () => {
220
106
  try {
221
- await open(redirectUrl);
107
+ await open(taskCreationUrl);
222
108
  console.log(chalk.green('✅ Browser opened successfully!'));
223
109
  } catch (err) {
224
- if (err.message.includes('No application')) {
225
- console.error(chalk.red(' Could not open browser automatically.'));
226
- console.log(chalk.yellow('Please manually open this URL in your browser:'));
227
- console.log(chalk.cyan(redirectUrl));
228
- } else {
229
- console.error(chalk.red('Browser error:'), err.message);
230
- }
110
+ console.error(chalk.red(' Could not open browser automatically.'));
111
+ console.log(chalk.yellow('Please open this URL manually in your browser:'));
112
+ console.log(chalk.cyan(taskCreationUrl));
231
113
  }
232
- }, 500);
114
+ }, 1000);
233
115
 
234
- // Wait for callback or timeout
235
- await new Promise((resolve) => {
236
- const timeout = setTimeout(() => {
237
- console.log(chalk.yellow('\n⚠️ No callback received within 5 minutes.'));
238
- console.log(chalk.gray('Your task may have been created successfully.'));
239
- console.log(chalk.gray('Check your browser to confirm.'));
240
- server.close();
241
- resolve();
242
- }, 300000); // 5 minutes
243
-
244
- server.on('close', () => {
245
- clearTimeout(timeout);
246
- resolve();
247
- });
248
- });
116
+ console.log(chalk.gray('\n⏳ Waiting for task creation in browser...'));
117
+ console.log(chalk.gray(' (The CLI will automatically close when task is created)\n'));
118
+
119
+ // Wait forever (until callback closes)
120
+ await new Promise(() => {});
249
121
 
250
122
  } catch (err) {
251
- console.error(chalk.red("❌ An unexpected error occurred:"), err.message);
123
+ console.error(chalk.red("❌ Error:"), err.message);
124
+ if (err.response) {
125
+ console.error(chalk.red("Server response:"), err.response.data);
126
+ }
252
127
  }
253
128
  }
254
129
 
255
- // Helper function to find available port
256
130
  async function findAvailablePort(startPort) {
257
131
  const net = await import('net');
258
132
  return new Promise((resolve, reject) => {
package/commands/init.js CHANGED
@@ -3,66 +3,104 @@ import os from "os";
3
3
  import path from "path";
4
4
  import inquirer from "inquirer";
5
5
  import axios from "axios";
6
- import machine from "node-machine-id";
6
+ import machineId from "node-machine-id";
7
7
 
8
8
  const CWD = process.cwd();
9
9
 
10
10
  async function projectInit(roomId) {
11
- const answer = await inquirer.prompt([
12
- { type: "input", name: "projectName", message: "Enter Project Name:" }
13
- ]);
11
+ try {
12
+ const answer = await inquirer.prompt([
13
+ { type: "input", name: "projectName", message: "Enter Project Name:" }
14
+ ]);
14
15
 
15
- const HomeDir = os.homedir();
16
- const tncrcPath = path.join(HomeDir, ".tncrc");
16
+ const HomeDir = os.homedir();
17
+ const tncrcPath = path.join(HomeDir, ".tncrc");
17
18
 
18
- if (!fs.existsSync(tncrcPath)) {
19
- console.error("❌ You are not logged in. Run 'tnc login' first.");
20
- process.exit(1);
21
- }
22
-
23
- const data = fs.readFileSync(tncrcPath, "utf-8");
24
- const currentUser = JSON.parse(data).email;
25
- const userToken = JSON.parse(data).token;
19
+ if (!fs.existsSync(tncrcPath)) {
20
+ console.error("❌ You are not logged in. Run 'tnc login' first.");
21
+ process.exit(1);
22
+ }
26
23
 
27
- // Initialize project via backend
28
- const response = await axios.post("https://thinkncollab.com/cli/init", {
29
- projectName: answer.projectName,
30
- owner: currentUser,
31
- token: userToken,
32
- machineId: await machine.machineIdSync(),
33
- roomId: roomId
34
- });
24
+ // Read user credentials from .tncrc
25
+ const data = fs.readFileSync(tncrcPath, "utf-8");
26
+ const userData = JSON.parse(data);
27
+ const currentUser = userData.email;
28
+ const userToken = userData.token;
29
+ const machineIdValue = machineId.machineIdSync(); // or await machineId.machineId()
35
30
 
36
- const projectId = response.data.project._id;
31
+ console.log("Initializing project...");
37
32
 
38
- // Ensure .tnc folder exists at project root
39
- const tncFolderPath = path.join(CWD, ".tnc");
40
- if (!fs.existsSync(tncFolderPath)) fs.mkdirSync(tncFolderPath, { recursive: true });
41
-
42
- // Write metadata file
43
- const metaFilePath = path.join(tncFolderPath, ".tncmeta.json");
44
- const pushFilePath = path.join(tncFolderPath, ".tncpush.json");
45
- fs.writeFileSync(
46
- metaFilePath,
47
- JSON.stringify(
33
+ // Initialize project via backend - CREDENTIALS IN HEADERS
34
+ const response = await axios.post(
35
+ "https://thinkncollab.com/cli/init",
48
36
  {
49
- projectId,
50
37
  projectName: answer.projectName,
51
- currentBranch: "main",
52
- roomId: roomId,
53
- lastCommit: null,
54
- files: {}
38
+ roomId: roomId
39
+ // Note: owner, token, machineId are NOT in body
55
40
  },
56
- null,
57
- 2
58
- )
59
- );
60
- fs.writeFileSync(
61
- pushFilePath,
62
- " {} "
63
- )
41
+ {
42
+ headers: {
43
+ 'x-user-email': currentUser,
44
+ 'x-user-token': userToken,
45
+ 'x-machine-id': machineIdValue
46
+ }
47
+ }
48
+ );
64
49
 
65
- console.log("✅ Project initialized successfully!");
50
+ const projectData = response.data;
51
+ const projectId = projectData.project._id;
52
+
53
+ // Ensure .tnc folder exists at project root
54
+ const tncFolderPath = path.join(CWD, ".tnc");
55
+ if (!fs.existsSync(tncFolderPath)) {
56
+ fs.mkdirSync(tncFolderPath, { recursive: true });
57
+ }
58
+
59
+ // Write metadata file
60
+ const metaFilePath = path.join(tncFolderPath, ".tncmeta.json");
61
+ const pushFilePath = path.join(tncFolderPath, ".tncpush.json");
62
+
63
+ fs.writeFileSync(
64
+ metaFilePath,
65
+ JSON.stringify(
66
+ {
67
+ projectId,
68
+ projectName: answer.projectName,
69
+ currentBranch: "main",
70
+ roomId: roomId,
71
+ lastCommit: null,
72
+ files: {}
73
+ },
74
+ null,
75
+ 2
76
+ )
77
+ );
78
+
79
+ fs.writeFileSync(
80
+ pushFilePath,
81
+ JSON.stringify({}, null, 2)
82
+ );
83
+
84
+ console.log("✅ Project initialized successfully!");
85
+ console.log(`📁 Project ID: ${projectId}`);
86
+ console.log(`🌿 Branch: main`);
87
+
88
+ } catch (error) {
89
+ if (error.response) {
90
+ // The request was made and the server responded with a status code outside of 2xx
91
+ console.error(`❌ Server error: ${error.response.data.message || error.response.status}`);
92
+ if (error.response.status === 400) {
93
+ console.error("Please check your login credentials");
94
+ }
95
+ } else if (error.request) {
96
+ // The request was made but no response was received
97
+ console.error("❌ No response from server. Please check your internet connection.");
98
+ } else {
99
+ // Something happened in setting up the request
100
+ console.error("❌ Error:", error.message);
101
+ }
102
+ process.exit(1);
103
+ }
66
104
  }
67
105
 
68
- export default projectInit;
106
+ export default projectInit;