thinkncollab-shell 0.0.10 → 0.0.13
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/Readme.md +32 -0
- package/package.json +5 -2
- package/src/commands/github/commits.js +58 -0
- package/src/commands/github/issue-create.js +67 -0
- package/src/commands/github/issues.js +57 -0
- package/src/commands/github/pulls.js +58 -0
- package/src/commands/github/statusgh.js +50 -0
- package/src/commands/index.js +30 -26
- package/src/commands/room/leave.js +6 -15
- package/src/commands/shell/status.js +5 -5
- package/src/commands/tasks/complete.js +748 -35
- package/src/commands/tasks/create-task.js +2 -2
- package/src/commands/tasks/loadtest.js +73 -0
- package/src/commands/tasks/task-status.js +63 -52
- package/src/commands/tasks/task.js +169 -65
- package/src/commands/tasks/tasks.js +4 -3
- package/src/commands/terminal/share.js +1 -1
- package/src/core/api-client.js +1 -1
- package/src/core/geekload.js +524 -0
- package/src/core/notification-window.js +25 -5
- package/src/core/shell.js +338 -299
- package/src/core/websocket.js +3 -0
- package/src/events/notifications.js +3 -4
|
@@ -16,7 +16,7 @@ module.exports = {
|
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
18
|
// Fetch columns of current room's board
|
|
19
|
-
const { columns } = await shell.api._request('GET', `/
|
|
19
|
+
const { columns } = await shell.api._request('GET', `/thinknsh/${roomId}/cli/columns`);
|
|
20
20
|
if (!columns?.length) {
|
|
21
21
|
console.log(chalk.red('❌ No columns found in this room\'s board'));
|
|
22
22
|
return;
|
|
@@ -78,7 +78,7 @@ module.exports = {
|
|
|
78
78
|
source: 'cli',
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
const result = await shell.api._request('POST',
|
|
81
|
+
const result = await shell.api._request('POST', `/thinknsh/${roomId}/cli/tasks`, payload);
|
|
82
82
|
|
|
83
83
|
console.log(chalk.green(`\n✅ Task created: "${result.task.title}"`));
|
|
84
84
|
console.log(chalk.dim(` ID: ${result.task._id}`));
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { runLoadTest } = require('../../core/geekload');
|
|
5
|
+
const Table = require('cli-table3');
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
name: 'loadtest',
|
|
9
|
+
description: 'Run a local load test script using GeekLoad',
|
|
10
|
+
aliases: ['geekload'],
|
|
11
|
+
requiresAuth: false,
|
|
12
|
+
|
|
13
|
+
async execute(args, shell) {
|
|
14
|
+
if (!args[0]) {
|
|
15
|
+
console.log(chalk.red('Usage: loadtest <script-path>'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const scriptPath = path.resolve(process.cwd(), args[0]);
|
|
20
|
+
if (!fs.existsSync(scriptPath)) {
|
|
21
|
+
console.log(chalk.red(`❌ Script not found: ${args[0]}`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(chalk.cyan(`\n📋 Loading test script: ${args[0]}`));
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const results = await runLoadTest(scriptPath);
|
|
29
|
+
|
|
30
|
+
const table = new Table({
|
|
31
|
+
head: [chalk.green('Metric'), chalk.green('Value')],
|
|
32
|
+
colWidths: [25, 20]
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
table.push(
|
|
36
|
+
['Virtual Users', results.virtual_users],
|
|
37
|
+
['Duration (sec)', results.duration_sec],
|
|
38
|
+
['Total Requests', results.total_requests],
|
|
39
|
+
['Failed Requests', results.failed_requests],
|
|
40
|
+
['Requests/sec (RPS)', results.requests_per_sec],
|
|
41
|
+
['Failure Rate', `${(results.failure_rate * 100).toFixed(2)}%`],
|
|
42
|
+
['Average Latency (ms)', results.avg_latency_ms],
|
|
43
|
+
['p95 Latency (ms)', results.p95_latency_ms],
|
|
44
|
+
['p99 Latency (ms)', results.p99_latency_ms]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
console.log(table.toString());
|
|
48
|
+
|
|
49
|
+
if (results.failures.length > 0) {
|
|
50
|
+
console.log(chalk.yellow(`\n⚠️ Recorded Failures (${results.failures.length}):`));
|
|
51
|
+
results.failures.slice(0, 5).forEach((f, idx) => {
|
|
52
|
+
console.log(chalk.dim(` ${idx + 1}. [${f.method}] ${f.url}`));
|
|
53
|
+
f.failures.forEach(msg => {
|
|
54
|
+
console.log(chalk.red(` - ${msg}`));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
if (results.failures.length > 5) {
|
|
58
|
+
console.log(chalk.dim(` ... and ${results.failures.length - 5} more failures`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (results.errors.length > 0) {
|
|
63
|
+
console.log(chalk.red(`\n❌ Execution Errors (${results.errors.length}):`));
|
|
64
|
+
results.errors.slice(0, 5).forEach((e, idx) => {
|
|
65
|
+
console.log(chalk.red(` VU ${e.vuId} Error: ${e.message}`));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.log(chalk.red(`❌ Load test execution failed: ${err.message}`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -2,60 +2,71 @@
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
|
|
4
4
|
const STATUS_MAP = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
accept: 'accepted',
|
|
6
|
+
start: 'inprogress',
|
|
7
|
+
complete: 'completed',
|
|
8
|
+
reject: 'rejected',
|
|
9
|
+
done: 'completed',
|
|
10
|
+
'task-status': null, // requires explicit arg
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
module.exports = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
14
|
+
name: 'task-status',
|
|
15
|
+
description: 'Update your task status',
|
|
16
|
+
aliases: ['accept', 'start', 'complete', 'reject', 'done'],
|
|
17
|
+
requiresAuth: true,
|
|
18
|
+
requiresRoom: true,
|
|
19
|
+
|
|
20
|
+
async execute(args, shell, invokedAs) {
|
|
21
|
+
const roomId = shell.ws.getCurrentRoom(); // ← top pe, ek baar
|
|
22
|
+
const taskId = args[0];
|
|
23
|
+
const verb = invokedAs || shell._lastCommand || 'task-status';
|
|
24
|
+
const status = STATUS_MAP[verb] ?? args[1];
|
|
25
|
+
|
|
26
|
+
if (!taskId) {
|
|
27
|
+
console.log(chalk.yellow(' Usage: accept <taskId> | start <taskId> | complete <taskId>'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!status) {
|
|
31
|
+
console.log(chalk.yellow(' Usage: task-status <taskId> <accepted|inprogress|completed|rejected>'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const result = await shell.api._request(
|
|
37
|
+
'POST',
|
|
38
|
+
`/thinknsh/${roomId}/tasks/${taskId}/status`,
|
|
39
|
+
{ status }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const statusColors = {
|
|
43
|
+
accepted: chalk.green,
|
|
44
|
+
inprogress: chalk.blue,
|
|
45
|
+
completed: chalk.green,
|
|
46
|
+
rejected: chalk.red,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const col = statusColors[status] || chalk.white;
|
|
50
|
+
console.log(col(`\n ✓ Task "${result.taskTitle}" → ${status}`));
|
|
51
|
+
|
|
52
|
+
if (result.allCompleted) {
|
|
53
|
+
console.log(chalk.green(' 🎉 All assignees done — task fully completed!\n'));
|
|
54
|
+
} else {
|
|
55
|
+
console.log('');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Socket emit
|
|
59
|
+
if (shell.ws.isConnected?.() && shell.ws.socket) {
|
|
60
|
+
shell.ws.socket.emit('task:' + (status === 'inprogress' ? 'started' : status), {
|
|
61
|
+
roomId,
|
|
62
|
+
taskId,
|
|
63
|
+
taskTitle: result.taskTitle,
|
|
64
|
+
userId: shell.api.getUser()?._id,
|
|
65
|
+
});
|
|
60
66
|
}
|
|
67
|
+
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.log(chalk.red(`\n ❌ ${err.message}\n`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
61
72
|
};
|
|
@@ -1,75 +1,179 @@
|
|
|
1
1
|
/*
|
|
2
|
-
src/commands/tasks/task.js — View
|
|
3
|
-
|
|
2
|
+
src/commands/tasks/task-info.js — View task details + testConfig + webhook URL
|
|
3
|
+
Usage: task-info <taskId> OR info <taskId> load
|
|
4
|
+
*/
|
|
4
5
|
const chalk = require('chalk');
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
name: 'task-info',
|
|
9
|
+
description: 'View full task details including test config and webhook URL',
|
|
10
|
+
aliases: ['task', 'info', 'taskInfo', '--t' ],
|
|
11
|
+
requiresAuth: true,
|
|
12
|
+
|
|
13
|
+
async execute(args, shell) {
|
|
14
|
+
if (!args[0]) {
|
|
15
|
+
console.log(chalk.red('Usage: task-info <task-id>'));
|
|
16
|
+
console.log(chalk.dim(' Alias: info <task-id>'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const taskId = args[0];
|
|
21
|
+
|
|
22
|
+
let task;
|
|
23
|
+
try {
|
|
24
|
+
task = await shell.api._request('GET', `/thinknsh/${taskId}/cli`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
if (err.message?.includes('404')) console.log(chalk.red('❌ Task not found'));
|
|
27
|
+
else if (err.message?.includes('403')) console.log(chalk.red('❌ Access denied'));
|
|
28
|
+
else console.log(chalk.red(`❌ ${err.message}`));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const stColors = {
|
|
33
|
+
pending: chalk.yellow,
|
|
34
|
+
inprogress: chalk.blue,
|
|
35
|
+
review: chalk.magenta,
|
|
36
|
+
completed: chalk.green,
|
|
37
|
+
reopened: chalk.cyan,
|
|
38
|
+
};
|
|
39
|
+
const prColors = { high: chalk.red, medium: chalk.yellow, low: chalk.green };
|
|
40
|
+
const stColor = stColors[task.status] || chalk.white;
|
|
41
|
+
const prColor = prColors[task.priority] || chalk.white;
|
|
42
|
+
|
|
43
|
+
// ── Basic Info ────────────────────────────────────────────────────────────
|
|
44
|
+
console.log(chalk.cyan('\n ─── Task Info ───────────────────────────────────'));
|
|
45
|
+
console.log(` ${chalk.dim('id ')} ${chalk.dim(task._id)}${task.taskID ? chalk.cyan(' #' + task.taskID) : ''}`);
|
|
46
|
+
console.log(` ${chalk.dim('title ')} ${chalk.bold(task.title)}`);
|
|
47
|
+
console.log(` ${chalk.dim('status ')} ${stColor(task.status)}`);
|
|
48
|
+
console.log(` ${chalk.dim('priority')} ${prColor(task.priority)}`);
|
|
49
|
+
if (task.category)
|
|
50
|
+
console.log(` ${chalk.dim('category')} ${chalk.dim(task.category)}`);
|
|
51
|
+
if (task.dueDate)
|
|
52
|
+
console.log(` ${chalk.dim('due ')} ${chalk.yellow(new Date(task.dueDate).toLocaleDateString())}`);
|
|
53
|
+
if (task.description) {
|
|
54
|
+
console.log(chalk.dim('\n description:'));
|
|
55
|
+
task.description.split('\n').forEach(l => console.log(` ${chalk.white(l)}`));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (task.referenceLinks && task.referenceLinks.length > 0) {
|
|
59
|
+
console.log(chalk.cyan('\n ─── Supported Links ─────────────────────────────'));
|
|
60
|
+
task.referenceLinks.forEach(link => {
|
|
61
|
+
console.log(` ${chalk.dim(link.title.padEnd(12))} ${chalk.blue(link.url)}`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Assignees ─────────────────────────────────────────────────────────────
|
|
66
|
+
if (task.assignedTo?.length) {
|
|
67
|
+
console.log(chalk.dim('\n assignees:'));
|
|
68
|
+
task.assignedTo.forEach(a => {
|
|
69
|
+
const name = a.userId?.name || 'Unknown';
|
|
70
|
+
const stBadge = a.status === 'completed' ? chalk.green('✓') :
|
|
71
|
+
a.status === 'accepted' ? chalk.blue('●') :
|
|
72
|
+
a.status === 'rejected' ? chalk.red('✗') : chalk.dim('○');
|
|
73
|
+
console.log(` ${stBadge} ${name} ${chalk.dim(`[${a.status}]`)}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Test Config ───────────────────────────────────────────────────────────
|
|
78
|
+
if (task.testConfig?.type) {
|
|
79
|
+
const tc = task.testConfig;
|
|
80
|
+
|
|
81
|
+
console.log(chalk.cyan('\n ─── Test Config ─────────────────────────────────'));
|
|
82
|
+
console.log(` ${chalk.dim('type ')} ${chalk.bold(tc.type)}`);
|
|
83
|
+
|
|
84
|
+
if (tc.type === 'http') {
|
|
85
|
+
console.log(` ${chalk.dim('method ')} ${chalk.green(tc.method || 'GET')}`);
|
|
86
|
+
console.log(` ${chalk.dim('target ')} ${chalk.white((tc.baseUrl || '') + (tc.endpoint || ''))}`);
|
|
87
|
+
if (tc.body && Object.keys(tc.body).length) {
|
|
88
|
+
console.log(` ${chalk.dim('body ')} ${chalk.dim(JSON.stringify(tc.body))}`);
|
|
16
89
|
}
|
|
90
|
+
if (tc.expect) {
|
|
91
|
+
console.log(chalk.dim('\n expect:'));
|
|
92
|
+
if (tc.expect.status !== undefined)
|
|
93
|
+
console.log(` ${chalk.dim('status')} ${chalk.green(tc.expect.status)}`);
|
|
94
|
+
if (tc.expect.body) {
|
|
95
|
+
Object.entries(tc.expect.body).forEach(([key, condition]) => {
|
|
96
|
+
const condStr = typeof condition === 'object'
|
|
97
|
+
? Object.entries(condition).map(([op, val]) => `${op}: ${val}`).join(', ')
|
|
98
|
+
: String(condition);
|
|
99
|
+
console.log(` ${chalk.dim('body.' + key)} ${chalk.cyan(condStr)}`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
17
104
|
|
|
105
|
+
if (tc.type === 'browser') {
|
|
106
|
+
console.log(` ${chalk.dim('url ')} ${chalk.white(tc.url || '')}`);
|
|
107
|
+
if (tc.flow?.length) {
|
|
108
|
+
console.log(chalk.dim('\n flow:'));
|
|
109
|
+
tc.flow.forEach((step, i) => {
|
|
110
|
+
console.log(` ${chalk.dim(i + 1 + '.')} ${chalk.green(step.action)} ${chalk.dim(step.selector || '')} ${step.value ? chalk.white(`"${step.value}"`) : ''}`);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (tc.expect) {
|
|
114
|
+
console.log(chalk.dim('\n expect:'));
|
|
115
|
+
if (tc.expect.element)
|
|
116
|
+
console.log(` ${chalk.dim('element ')} ${chalk.cyan(tc.expect.element)}`);
|
|
117
|
+
if (tc.expect.text)
|
|
118
|
+
console.log(` ${chalk.dim('text ')} ${chalk.cyan(`contains "${tc.expect.text}"`)}`);
|
|
119
|
+
if (tc.expect.redirectUrl)
|
|
120
|
+
console.log(` ${chalk.dim('redirectUrl')} ${chalk.cyan(tc.expect.redirectUrl)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
18
123
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
review: chalk.magenta,
|
|
27
|
-
completed: chalk.green,
|
|
28
|
-
reopened: chalk.cyan,
|
|
29
|
-
};
|
|
30
|
-
const prColors = { high: chalk.red, medium: chalk.yellow, low: chalk.green };
|
|
31
|
-
const stColor = stColors[task.status] || chalk.white;
|
|
32
|
-
const prColor = prColors[task.priority] || chalk.white;
|
|
33
|
-
|
|
34
|
-
console.log(chalk.cyan('\n ─── Task ────────────────────────────────────────'));
|
|
35
|
-
console.log(` ${chalk.dim('id')} ${chalk.dim(task._id)}`);
|
|
36
|
-
console.log(` ${chalk.dim('title')} ${chalk.bold(task.title)}`);
|
|
37
|
-
console.log(` ${chalk.dim('status')} ${stColor(task.status)}`);
|
|
38
|
-
console.log(` ${chalk.dim('priority')} ${prColor(task.priority)}`);
|
|
39
|
-
if (task.category)
|
|
40
|
-
console.log(` ${chalk.dim('category')} ${chalk.dim(task.category)}`);
|
|
41
|
-
if (task.dueDate)
|
|
42
|
-
console.log(` ${chalk.dim('due')} ${chalk.yellow(new Date(task.dueDate).toLocaleDateString())}`);
|
|
43
|
-
if (task.description) {
|
|
44
|
-
console.log(chalk.dim('\n description:'));
|
|
45
|
-
task.description.split('\n').forEach(l => console.log(` ${chalk.white(l)}`));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (task.assignedTo?.length) {
|
|
49
|
-
console.log(chalk.dim('\n assignees:'));
|
|
50
|
-
task.assignedTo.forEach(a => {
|
|
51
|
-
const name = a.userId?.name || 'Unknown';
|
|
52
|
-
const stBadge = a.status === 'completed' ? chalk.green('✓') :
|
|
53
|
-
a.status === 'accepted' ? chalk.blue('●') :
|
|
54
|
-
a.status === 'rejected' ? chalk.red('✗') : chalk.dim('○');
|
|
55
|
-
console.log(` ${stBadge} ${name} ${chalk.dim(`[${a.status}]`)}`);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (task.columnId?.name || task.columnName)
|
|
60
|
-
console.log(`\n ${chalk.dim('column')} ${task.columnId?.name || task.columnName}`);
|
|
61
|
-
if (task.boardName)
|
|
62
|
-
console.log(` ${chalk.dim('board')} ${task.boardName}`);
|
|
63
|
-
|
|
64
|
-
console.log(chalk.cyan(' ─────────────────────────────────────────────────\n'));
|
|
65
|
-
|
|
66
|
-
} catch (err) {
|
|
67
|
-
if (err.message?.includes('403'))
|
|
68
|
-
console.log(chalk.red('❌ Access denied — you are not a member of this room'));
|
|
69
|
-
else if (err.message?.includes('404'))
|
|
70
|
-
console.log(chalk.red('❌ Task not found'));
|
|
71
|
-
else
|
|
72
|
-
console.log(chalk.red(`❌ ${err.message}`));
|
|
124
|
+
if (tc.type === 'json') {
|
|
125
|
+
if (tc.expect?.condition) {
|
|
126
|
+
console.log(chalk.dim('\n conditions:'));
|
|
127
|
+
Object.entries(tc.expect.condition).forEach(([key, ops]) => {
|
|
128
|
+
const condStr = Object.entries(ops).map(([op, val]) => `${op} ${val}`).join(', ');
|
|
129
|
+
console.log(` ${chalk.dim(key)} ${chalk.cyan(condStr)}`);
|
|
130
|
+
});
|
|
73
131
|
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (tc.type === 'exitCode')
|
|
135
|
+
console.log(` ${chalk.dim('expect ')} exitCode ${chalk.green(tc.expect?.exitCode ?? 0)}`);
|
|
136
|
+
|
|
137
|
+
if (tc.type === 'stdout')
|
|
138
|
+
console.log(` ${chalk.dim('expect ')} stdout "${chalk.green(tc.expect?.stdout || '')}"`);
|
|
139
|
+
|
|
140
|
+
if (tc.type === 'regex')
|
|
141
|
+
console.log(` ${chalk.dim('expect ')} regex ${chalk.green(tc.expect?.regex || '')}`);
|
|
142
|
+
|
|
143
|
+
} else {
|
|
144
|
+
console.log(chalk.yellow('\n ⚠️ No test config — task will need manual completion'));
|
|
145
|
+
console.log(chalk.dim(' Use: complete <task-id>'));
|
|
74
146
|
}
|
|
147
|
+
|
|
148
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
149
|
+
if (task.webhookSecret) {
|
|
150
|
+
const apiUrl = shell.api.apiUrl || 'https://thinkncollab.com';
|
|
151
|
+
const webhookUrl = `${apiUrl}/webhooks/tasks/${taskId}/judge`;
|
|
152
|
+
|
|
153
|
+
console.log(chalk.cyan('\n ─── Webhook ─────────────────────────────────────'));
|
|
154
|
+
console.log(` ${chalk.dim('url ')} ${chalk.white(webhookUrl)}`);
|
|
155
|
+
console.log(` ${chalk.dim('secret ')} ${chalk.dim(task.webhookSecret)}`);
|
|
156
|
+
console.log(` ${chalk.dim('header ')} ${chalk.dim('x-tnc-signature: sha256=<HMAC of body>')}`);
|
|
157
|
+
console.log(chalk.dim('\n To submit from your project:'));
|
|
158
|
+
console.log(chalk.cyan(' submit ' + taskId));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Last Verdict ──────────────────────────────────────────────────────────
|
|
162
|
+
if (task.lastVerdict?.at) {
|
|
163
|
+
const v = task.lastVerdict;
|
|
164
|
+
const passed = v.passed;
|
|
165
|
+
const badge = passed ? chalk.bgGreen.black(' PASS ') : chalk.bgRed.white(' FAIL ');
|
|
166
|
+
const ts = new Date(v.at).toLocaleString();
|
|
167
|
+
|
|
168
|
+
console.log(chalk.cyan('\n ─── Last Verdict ────────────────────────────────'));
|
|
169
|
+
console.log(` ${badge} ${passed ? chalk.green(v.reason) : chalk.red(v.reason)}`);
|
|
170
|
+
console.log(` ${chalk.dim('at')} ${chalk.dim(ts)}`);
|
|
171
|
+
if (v.diff) {
|
|
172
|
+
console.log(` ${chalk.dim('expected')} ${chalk.cyan(JSON.stringify(v.diff.expected ?? v.diff.conditions))}`);
|
|
173
|
+
console.log(` ${chalk.dim('actual ')} ${chalk.yellow(JSON.stringify(v.diff.actual))}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log(chalk.cyan(' ─────────────────────────────────────────────────\n'));
|
|
178
|
+
}
|
|
75
179
|
};
|
|
@@ -106,9 +106,10 @@ module.exports = {
|
|
|
106
106
|
const col = task.column ? chalk.dim(` [${task.column}]`) : '';
|
|
107
107
|
|
|
108
108
|
console.log(`\n${num} ${status} ${title}`);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
|
|
110
|
+
console.log(subline('id', `${task.taskID ? '#' + task.taskID : task._id}`) +
|
|
111
|
+
` ${chalk.dim('priority:')} ${pc(task.priority)}` +
|
|
112
|
+
due + myStatus + col);
|
|
112
113
|
|
|
113
114
|
if (task.description) {
|
|
114
115
|
const desc = task.description.length > 90
|
|
@@ -63,7 +63,7 @@ async function cmdStart(shell) {
|
|
|
63
63
|
console.log(chalk.green(`\n✅ Terminal sharing started`));
|
|
64
64
|
console.log(chalk.dim(` Session : ${data.sessionId}`));
|
|
65
65
|
console.log(chalk.dim(` Room : ${roomId}`));
|
|
66
|
-
console.log(chalk.dim(` URL : /terminal/${data.sessionId}`));
|
|
66
|
+
console.log(chalk.dim(` URL : https://thinkncollab.com/terminal/${data.sessionId}`));
|
|
67
67
|
console.log(chalk.dim(`\n Room members can now view your terminal.`));
|
|
68
68
|
console.log(chalk.dim(` Use "share grant <userId>" to give write access.\n`));
|
|
69
69
|
|
package/src/core/api-client.js
CHANGED
|
@@ -148,7 +148,7 @@ async logEvent(type, roomId = null, meta = {}) {
|
|
|
148
148
|
if (!sessionId) return;
|
|
149
149
|
await this._request('POST', '/thinknsh/session/event', {
|
|
150
150
|
sessionId, type, roomId, meta
|
|
151
|
-
}).catch(() => {});
|
|
151
|
+
}).catch(() => {});
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// ─── Heartbeat — keeps lastSeenAt fresh while shell is open ──────────────
|