thinkncollab-cli 0.0.84 → 0.0.86
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 +18 -0
- package/commands/createTask.js +255 -0
- package/commands/myTeam.js +44 -0
- package/commands/send.js +16 -0
- package/commands/task.js +1 -1
- package/package.json +6 -3
package/bin/index.js
CHANGED
|
@@ -18,6 +18,8 @@ import machine from "node-machine-id";
|
|
|
18
18
|
import push from "../commands/push.js";
|
|
19
19
|
import taskCompletion from "../commands/taskCompletion.js";
|
|
20
20
|
import task from "../commands/task.js";
|
|
21
|
+
import createTask from "../commands/createTask.js";
|
|
22
|
+
import myTeam from "../commands/myTeam.js";
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
|
|
@@ -188,6 +190,17 @@ case "my-tasks": {
|
|
|
188
190
|
await myTask();
|
|
189
191
|
break;
|
|
190
192
|
}
|
|
193
|
+
case "create-task": {
|
|
194
|
+
const roomIdx = args.indexOf("create-task");
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
await createTask();
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case "send": {
|
|
201
|
+
await send();
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
191
204
|
case "invite": {
|
|
192
205
|
const roomIdx = args.indexOf("invite");
|
|
193
206
|
if (roomIdx === -1 || !args[roomIdx + 1]) {
|
|
@@ -204,6 +217,11 @@ case "task": {
|
|
|
204
217
|
break;
|
|
205
218
|
}
|
|
206
219
|
|
|
220
|
+
case "myteam": {
|
|
221
|
+
myTeam();
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
|
|
207
225
|
case "signout": {
|
|
208
226
|
await DAPP();
|
|
209
227
|
break;
|
|
@@ -0,0 +1,255 @@
|
|
|
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 chalk from "chalk";
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import open from 'open';
|
|
9
|
+
import http from 'http';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const getVerifyModule = await import("../lib/getVerify.js");
|
|
13
|
+
const getVerify = getVerifyModule.default;
|
|
14
|
+
const { token } = await getVerify();
|
|
15
|
+
|
|
16
|
+
const CWD = process.cwd();
|
|
17
|
+
const tncmetaPath = path.join(CWD, ".tnc", ".tncmeta.json");
|
|
18
|
+
const fileData = fs.existsSync(tncmetaPath) ? JSON.parse(fs.readFileSync(tncmetaPath, "utf-8")) : null;
|
|
19
|
+
const roomId = fileData?.roomId || null;
|
|
20
|
+
|
|
21
|
+
function createSpinner(message) {
|
|
22
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
23
|
+
let i = 0;
|
|
24
|
+
|
|
25
|
+
const spinner = setInterval(() => {
|
|
26
|
+
process.stdout.write(`\r${chalk.cyan(frames[i])} ${message}`);
|
|
27
|
+
i = (i + 1) % frames.length;
|
|
28
|
+
}, 80);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
stop: (clearMessage = true) => {
|
|
32
|
+
clearInterval(spinner);
|
|
33
|
+
if (clearMessage) {
|
|
34
|
+
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 50) + '\r');
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
update: (newMessage) => {
|
|
38
|
+
message = newMessage;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function createTask() {
|
|
44
|
+
try {
|
|
45
|
+
console.log(chalk.blue('\n🚀 Task Creation CLI\n'));
|
|
46
|
+
|
|
47
|
+
// Validate roomId and token
|
|
48
|
+
if (!roomId) {
|
|
49
|
+
console.error(chalk.red('❌ No roomId found in .tnc/.tncmeta.json'));
|
|
50
|
+
console.log(chalk.yellow('Please ensure you are in a ThinkNCollab project directory'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!token) {
|
|
55
|
+
console.error(chalk.red('❌ No token found'));
|
|
56
|
+
console.log(chalk.yellow('Please login first using: tnc login'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// STEP 1: Find available port FIRST
|
|
61
|
+
console.log(chalk.gray('🔍 Finding available port for callback server...'));
|
|
62
|
+
const PORT = await findAvailablePort(3002);
|
|
63
|
+
console.log(chalk.green(`✅ Callback server will run on port: ${PORT}`));
|
|
64
|
+
|
|
65
|
+
// STEP 2: Construct the URL with the found port
|
|
66
|
+
const redirectUrl = `https://thinkncollab.in/cli/tasks/${roomId}/${token}/create?cli=true&callbackPort=${PORT}`;
|
|
67
|
+
|
|
68
|
+
const spinner = createSpinner('Preparing task creation interface...');
|
|
69
|
+
|
|
70
|
+
console.log(chalk.blue('🔗 Opening task creation interface in your browser...'));
|
|
71
|
+
console.log(chalk.gray(`URL: ${redirectUrl}`));
|
|
72
|
+
|
|
73
|
+
spinner.stop();
|
|
74
|
+
|
|
75
|
+
// STEP 3: Create callback server
|
|
76
|
+
const server = http.createServer((req, res) => {
|
|
77
|
+
console.log(chalk.yellow(`\n📨 Callback received: ${req.url}`));
|
|
78
|
+
|
|
79
|
+
// CORS headers for all responses
|
|
80
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
81
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
82
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-CLI-Callback-URL');
|
|
83
|
+
res.setHeader('Access-Control-Max-Age', '86400'); // 24 hours
|
|
84
|
+
|
|
85
|
+
// Handle preflight
|
|
86
|
+
if (req.method === 'OPTIONS') {
|
|
87
|
+
res.writeHead(200);
|
|
88
|
+
res.end();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Parse URL
|
|
93
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
94
|
+
|
|
95
|
+
// Check for callback (handle both /callback and direct query params)
|
|
96
|
+
if (req.url.includes('/callback') || url.searchParams.has('taskId')) {
|
|
97
|
+
const taskId = url.searchParams.get('taskId');
|
|
98
|
+
const attachments = url.searchParams.get('attachments');
|
|
99
|
+
const status = url.searchParams.get('status');
|
|
100
|
+
|
|
101
|
+
// Log success
|
|
102
|
+
if (status === 'error') {
|
|
103
|
+
console.log(chalk.red('\n❌ Task creation failed!'));
|
|
104
|
+
} else {
|
|
105
|
+
console.log(chalk.green('\n✅ Task created successfully!'));
|
|
106
|
+
if (taskId) {
|
|
107
|
+
console.log(chalk.blue(`📋 Task ID: ${taskId}`));
|
|
108
|
+
|
|
109
|
+
// Save task ID to file
|
|
110
|
+
const taskMetaPath = path.join(CWD, ".tnc", "last-task.json");
|
|
111
|
+
fs.writeFileSync(taskMetaPath, JSON.stringify({
|
|
112
|
+
taskId,
|
|
113
|
+
attachments: attachments ? parseInt(attachments) : 0,
|
|
114
|
+
timestamp: new Date().toISOString(),
|
|
115
|
+
roomId
|
|
116
|
+
}, null, 2));
|
|
117
|
+
console.log(chalk.gray(`📝 Task ID saved to .tnc/last-task.json`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Send response to close browser (HTML with auto-close)
|
|
122
|
+
res.writeHead(200, {
|
|
123
|
+
'Content-Type': 'text/html',
|
|
124
|
+
'Access-Control-Allow-Origin': '*'
|
|
125
|
+
});
|
|
126
|
+
res.end(`
|
|
127
|
+
<!DOCTYPE html>
|
|
128
|
+
<html>
|
|
129
|
+
<head>
|
|
130
|
+
<title>${status === 'error' ? 'Failed' : 'Success'}</title>
|
|
131
|
+
<style>
|
|
132
|
+
body {
|
|
133
|
+
font-family: Arial, sans-serif;
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
align-items: center;
|
|
137
|
+
height: 100vh;
|
|
138
|
+
margin: 0;
|
|
139
|
+
background: ${status === 'error' ? '#f44336' : '#4CAF50'};
|
|
140
|
+
color: white;
|
|
141
|
+
}
|
|
142
|
+
.message {
|
|
143
|
+
text-align: center;
|
|
144
|
+
padding: 20px;
|
|
145
|
+
}
|
|
146
|
+
</style>
|
|
147
|
+
</head>
|
|
148
|
+
<body>
|
|
149
|
+
<div class="message">
|
|
150
|
+
<h1>${status === 'error' ? '❌ Task Failed' : '✅ Task Created'}</h1>
|
|
151
|
+
${taskId ? `<p>Task ID: ${taskId}</p>` : ''}
|
|
152
|
+
<p>Closing in 3 seconds...</p>
|
|
153
|
+
</div>
|
|
154
|
+
<script>
|
|
155
|
+
// Send one more callback to ensure it was received
|
|
156
|
+
fetch(window.location.href, { method: 'GET', mode: 'no-cors' });
|
|
157
|
+
|
|
158
|
+
// Auto close
|
|
159
|
+
setTimeout(() => window.close(), 3000);
|
|
160
|
+
|
|
161
|
+
// Fallback
|
|
162
|
+
setTimeout(() => {
|
|
163
|
+
window.location.href = 'about:blank';
|
|
164
|
+
setTimeout(() => window.close(), 100);
|
|
165
|
+
}, 4000);
|
|
166
|
+
</script>
|
|
167
|
+
</body>
|
|
168
|
+
</html>
|
|
169
|
+
`);
|
|
170
|
+
|
|
171
|
+
// Close server after response
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
server.close();
|
|
174
|
+
console.log(chalk.gray('\n📡 Callback server closed'));
|
|
175
|
+
process.exit(0);
|
|
176
|
+
}, 2000);
|
|
177
|
+
} else {
|
|
178
|
+
// Handle other requests (like favicon)
|
|
179
|
+
res.writeHead(404);
|
|
180
|
+
res.end('Not found');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Server error handling
|
|
185
|
+
server.on('error', (err) => {
|
|
186
|
+
if (err.code === 'EADDRINUSE') {
|
|
187
|
+
console.log(chalk.yellow(`⚠️ Port ${PORT} is already in use. Callback might not work.`));
|
|
188
|
+
console.log(chalk.yellow('The task will still be created, but you may need to check manually.'));
|
|
189
|
+
} else {
|
|
190
|
+
console.error(chalk.red('Server error:'), err.message);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Start server
|
|
195
|
+
server.listen(PORT, () => {
|
|
196
|
+
console.log(chalk.gray(`📡 Waiting for callback on http://localhost:${PORT}/callback`));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Open browser
|
|
200
|
+
setTimeout(async () => {
|
|
201
|
+
try {
|
|
202
|
+
await open(redirectUrl);
|
|
203
|
+
console.log(chalk.green('✅ Browser opened successfully!'));
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (err.message.includes('No application')) {
|
|
206
|
+
console.error(chalk.red('❌ Could not open browser automatically.'));
|
|
207
|
+
console.log(chalk.yellow('Please manually open this URL in your browser:'));
|
|
208
|
+
console.log(chalk.cyan(redirectUrl));
|
|
209
|
+
} else {
|
|
210
|
+
console.error(chalk.red('Browser error:'), err.message);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}, 500);
|
|
214
|
+
|
|
215
|
+
// Wait for callback or timeout
|
|
216
|
+
await new Promise((resolve) => {
|
|
217
|
+
const timeout = setTimeout(() => {
|
|
218
|
+
console.log(chalk.yellow('\n⚠️ No callback received within 5 minutes.'));
|
|
219
|
+
console.log(chalk.gray('Your task may have been created successfully.'));
|
|
220
|
+
console.log(chalk.gray('Check your browser to confirm.'));
|
|
221
|
+
server.close();
|
|
222
|
+
resolve();
|
|
223
|
+
}, 300000); // 5 minutes
|
|
224
|
+
|
|
225
|
+
server.on('close', () => {
|
|
226
|
+
clearTimeout(timeout);
|
|
227
|
+
resolve();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
} catch (err) {
|
|
232
|
+
console.error(chalk.red("❌ An unexpected error occurred:"), err.message);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Helper function to find available port
|
|
237
|
+
async function findAvailablePort(startPort) {
|
|
238
|
+
const net = await import('net');
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const server = net.createServer();
|
|
241
|
+
server.listen(startPort, () => {
|
|
242
|
+
const port = server.address().port;
|
|
243
|
+
server.close(() => resolve(port));
|
|
244
|
+
});
|
|
245
|
+
server.on('error', (err) => {
|
|
246
|
+
if (err.code === 'EADDRINUSE') {
|
|
247
|
+
resolve(findAvailablePort(startPort + 1));
|
|
248
|
+
} else {
|
|
249
|
+
reject(err);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export default createTask;
|
|
@@ -0,0 +1,44 @@
|
|
|
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 chalk from "chalk";
|
|
7
|
+
import FormData from "form-data";
|
|
8
|
+
import getVerify from "../lib/getVerify.js";
|
|
9
|
+
|
|
10
|
+
const CWD = process.cwd();
|
|
11
|
+
const TNC_API_URL = "http://localhost:3001/";
|
|
12
|
+
const metaDataFile = path.join(CWD, ".tnc", ".tncmeta.json");
|
|
13
|
+
const metaData = JSON.parse(fs.readFileSync(metaDataFile, "utf-8"));
|
|
14
|
+
const roomId = metaData.roomId;
|
|
15
|
+
|
|
16
|
+
async function myTeam() {
|
|
17
|
+
try {
|
|
18
|
+
const verifyData = await getVerify();
|
|
19
|
+
const { email, token } = verifyData;
|
|
20
|
+
const response = await axios.get(`${TNC_API_URL}cli/myTeam/${roomId}`, {
|
|
21
|
+
headers: {
|
|
22
|
+
email,
|
|
23
|
+
token,
|
|
24
|
+
machineId: machine.machineIdSync(),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const teamMembers = response.data.RoomMembers || [];
|
|
28
|
+
if (teamMembers.length === 0) {
|
|
29
|
+
console.log(chalk.yellow("⚠️ You are not part of any team yet."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log(chalk.green("👥 Your Team Members:"))
|
|
33
|
+
teamMembers.forEach(member => {
|
|
34
|
+
console.log(chalk.blue(`- ${member.name} (${member.email})`));
|
|
35
|
+
});
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err.response) {
|
|
38
|
+
console.error(chalk.red("❌ Error:"), chalk.red(err.response.data.error));
|
|
39
|
+
} else {
|
|
40
|
+
console.error(chalk.red("❌ Error:"), chalk.red(err.message));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export default myTeam;
|
package/commands/send.js
ADDED
package/commands/task.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkncollab-cli",
|
|
3
3
|
"author": "Raman Singh",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.86",
|
|
5
5
|
"description": "CLI tool for ThinkNCollab",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"axios": "^1.12.2",
|
|
16
16
|
"chalk": "^5.6.2",
|
|
17
17
|
"cli-table3": "^0.6.5",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"form-data": "^4.0.5",
|
|
19
|
+
"inquirer": "^8.2.5",
|
|
20
|
+
"inquirer-file-selector": "^1.0.1",
|
|
21
|
+
"node-machine-id": "^1.1.12",
|
|
22
|
+
"open": "^11.0.0"
|
|
20
23
|
}
|
|
21
24
|
}
|