thinkncollab-shell 0.0.12 → 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 +1 -1
- 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 +219 -26
- package/src/commands/tasks/loadtest.js +73 -0
- package/src/commands/tasks/task-status.js +63 -52
- package/src/commands/tasks/task.js +8 -1
- package/src/commands/terminal/share.js +1 -1
- package/src/core/geekload.js +524 -0
- package/src/core/notification-window.js +25 -5
- package/src/core/shell.js +3 -2
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const crypto = require('crypto');
|
|
5
|
+
const cp = require('child_process');
|
|
6
|
+
const path = require('path');
|
|
5
7
|
|
|
6
8
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
7
9
|
|
|
@@ -381,46 +383,169 @@ async function runBrowserTest(testConfig, testUser, boardAuth = {}) {
|
|
|
381
383
|
}
|
|
382
384
|
}
|
|
383
385
|
|
|
386
|
+
// Helper to run a local command and capture exitCode & stdout/stderr
|
|
387
|
+
function runLocalCommand(command) {
|
|
388
|
+
return new Promise((resolve) => {
|
|
389
|
+
cp.exec(command, (error, stdout, stderr) => {
|
|
390
|
+
const exitCode = error ? (error.code || 1) : 0;
|
|
391
|
+
resolve({
|
|
392
|
+
exitCode,
|
|
393
|
+
stdout: stdout.toString(),
|
|
394
|
+
stderr: stderr.toString()
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
384
400
|
// ─── Local Judge ──────────────────────────────────────────────────────────────
|
|
385
401
|
function judgeLocally(testConfig, result) {
|
|
386
|
-
const { type, expect } = testConfig;
|
|
402
|
+
const { type, value, condition, expect } = testConfig;
|
|
387
403
|
const failures = [];
|
|
388
404
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
405
|
+
try {
|
|
406
|
+
switch (type) {
|
|
407
|
+
case 'exitCode': {
|
|
408
|
+
const passed = result.exitCode === value;
|
|
409
|
+
return {
|
|
410
|
+
passed,
|
|
411
|
+
reason: passed
|
|
412
|
+
? `Exit code matched: ${value}`
|
|
413
|
+
: `Expected exit code ${value}, got ${result.exitCode}`
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
case 'stdout': {
|
|
418
|
+
const actualOut = (result.stdout || '').trim();
|
|
419
|
+
const expectedOut = (value || '').trim();
|
|
420
|
+
const passed = actualOut === expectedOut;
|
|
421
|
+
return {
|
|
422
|
+
passed,
|
|
423
|
+
reason: passed ? `Output matched` : `Output mismatch`,
|
|
424
|
+
diff: passed ? undefined : { expected: expectedOut, actual: actualOut }
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
case 'regex': {
|
|
429
|
+
const pattern = new RegExp(value);
|
|
430
|
+
const target = result.stdout || result.output || '';
|
|
431
|
+
const passed = pattern.test(target);
|
|
432
|
+
return {
|
|
433
|
+
passed,
|
|
434
|
+
reason: passed ? `Regex matched: ${value}` : `Regex did not match: ${value}`
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
case 'http': {
|
|
439
|
+
if (expect?.status !== undefined && result.status !== expect.status) {
|
|
440
|
+
failures.push(`Status: expected ${expect.status}, got ${result.status}`);
|
|
441
|
+
}
|
|
442
|
+
if (expect?.body) {
|
|
443
|
+
for (const [key, condition] of Object.entries(expect.body)) {
|
|
444
|
+
const actualVal = getNestedValue(result.body || {}, key);
|
|
445
|
+
if (condition?.exists) {
|
|
446
|
+
if (actualVal === undefined || actualVal === null)
|
|
447
|
+
failures.push(`body.${key}: expected to exist`);
|
|
448
|
+
} else if (condition?.eq !== undefined) {
|
|
449
|
+
if (actualVal !== condition.eq)
|
|
450
|
+
failures.push(`body.${key}: expected "${condition.eq}", got "${actualVal}"`);
|
|
451
|
+
} else if (condition?.contains !== undefined) {
|
|
452
|
+
if (!String(actualVal).includes(String(condition.contains)))
|
|
453
|
+
failures.push(`body.${key}: expected to contain "${condition.contains}"`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
case 'json': {
|
|
461
|
+
const actualData = result.output || result.json || {};
|
|
462
|
+
if (condition) {
|
|
463
|
+
return judgeWithConditions(condition, actualData);
|
|
464
|
+
} else {
|
|
465
|
+
const passed = JSON.stringify(value) === JSON.stringify(actualData);
|
|
466
|
+
return {
|
|
467
|
+
passed,
|
|
468
|
+
reason: passed ? `JSON matched` : `JSON mismatch`,
|
|
469
|
+
diff: passed ? undefined : { expected: value, actual: actualData }
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
case 'loadtest':
|
|
474
|
+
case 'geekload': {
|
|
475
|
+
const actualData = result.output || result.json || {};
|
|
476
|
+
const cond = condition || expect?.condition || expect || {};
|
|
477
|
+
return judgeWithConditions(cond, actualData);
|
|
402
478
|
}
|
|
479
|
+
|
|
480
|
+
case 'browser': {
|
|
481
|
+
if (expect?.element && !result.elementExists) {
|
|
482
|
+
failures.push(`Element "${expect.element}" not found on page`);
|
|
483
|
+
}
|
|
484
|
+
if (expect?.text && result.elementText) {
|
|
485
|
+
if (!result.elementText.includes(expect.text))
|
|
486
|
+
failures.push(`Element text: expected to contain "${expect.text}", got "${result.elementText}"`);
|
|
487
|
+
}
|
|
488
|
+
if (expect?.redirectUrl) {
|
|
489
|
+
try {
|
|
490
|
+
const actualPath = new URL(result.url).pathname;
|
|
491
|
+
if (!actualPath.includes(expect.redirectUrl))
|
|
492
|
+
failures.push(`URL: expected "${expect.redirectUrl}", got "${actualPath}"`);
|
|
493
|
+
} catch {
|
|
494
|
+
failures.push(`Invalid URL in result: "${result.url}"`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
default:
|
|
501
|
+
return { passed: false, reason: `Unknown test type: ${type}` };
|
|
403
502
|
}
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
passed: failures.length === 0,
|
|
506
|
+
reason: failures.length === 0 ? 'All checks passed' : failures.join(' | '),
|
|
507
|
+
failures,
|
|
508
|
+
};
|
|
509
|
+
} catch (err) {
|
|
510
|
+
return { passed: false, reason: `Judge error: ${err.message}` };
|
|
404
511
|
}
|
|
512
|
+
}
|
|
405
513
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
514
|
+
function judgeWithConditions(conditions, actual) {
|
|
515
|
+
const failures = [];
|
|
516
|
+
|
|
517
|
+
for (const [key, ops] of Object.entries(conditions)) {
|
|
518
|
+
const actualVal = actual[key];
|
|
409
519
|
|
|
410
|
-
if (
|
|
411
|
-
failures.push(`
|
|
520
|
+
if (actualVal === undefined) {
|
|
521
|
+
failures.push(`Missing key: "${key}"`);
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
412
524
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
525
|
+
for (const [op, expected] of Object.entries(ops)) {
|
|
526
|
+
let pass = false;
|
|
527
|
+
switch (op) {
|
|
528
|
+
case 'gte': pass = actualVal >= expected; break;
|
|
529
|
+
case 'lte': pass = actualVal <= expected; break;
|
|
530
|
+
case 'gt': pass = actualVal > expected; break;
|
|
531
|
+
case 'lt': pass = actualVal < expected; break;
|
|
532
|
+
case 'eq': pass = actualVal === expected; break;
|
|
533
|
+
case 'contains': pass = String(actualVal).includes(String(expected)); break;
|
|
534
|
+
case 'regex': pass = new RegExp(expected).test(String(actualVal)); break;
|
|
535
|
+
default: pass = false;
|
|
536
|
+
}
|
|
537
|
+
if (!pass) {
|
|
538
|
+
failures.push(`"${key}" failed: ${actualVal} ${op} ${expected}`);
|
|
539
|
+
}
|
|
417
540
|
}
|
|
418
541
|
}
|
|
419
542
|
|
|
420
543
|
return {
|
|
421
|
-
passed:
|
|
422
|
-
reason:
|
|
423
|
-
|
|
544
|
+
passed: failures.length === 0,
|
|
545
|
+
reason: failures.length === 0
|
|
546
|
+
? `All conditions passed`
|
|
547
|
+
: `Failed conditions: ${failures.join('; ')}`,
|
|
548
|
+
diff: failures.length > 0 ? { conditions, actual } : undefined
|
|
424
549
|
};
|
|
425
550
|
}
|
|
426
551
|
|
|
@@ -525,6 +650,74 @@ const authConfig = { ...boardAuth, ...(testConfig.auth || {}) };
|
|
|
525
650
|
if (result.elementText)
|
|
526
651
|
console.log(chalk.dim(` → Text : "${result.elementText.trim().slice(0, 60)}"`));
|
|
527
652
|
|
|
653
|
+
} else if (testConfig.type === 'exitCode' || testConfig.type === 'stdout' || testConfig.type === 'regex') {
|
|
654
|
+
const cmd = testConfig.command || testConfig.script;
|
|
655
|
+
if (!cmd) {
|
|
656
|
+
throw new Error(`testConfig type "${testConfig.type}" requires a "command" or "script" to execute.`);
|
|
657
|
+
}
|
|
658
|
+
console.log(chalk.dim(` → Executing local command: "${cmd}"`));
|
|
659
|
+
const runRes = await runLocalCommand(cmd);
|
|
660
|
+
result = {
|
|
661
|
+
exitCode: runRes.exitCode,
|
|
662
|
+
stdout: runRes.stdout,
|
|
663
|
+
stderr: runRes.stderr
|
|
664
|
+
};
|
|
665
|
+
console.log(chalk.dim(` → Exit Code: ${result.exitCode}`));
|
|
666
|
+
if (result.stdout) console.log(chalk.dim(` → Stdout : ${result.stdout.trim().slice(0, 80)}`));
|
|
667
|
+
|
|
668
|
+
} else if (testConfig.type === 'json') {
|
|
669
|
+
const cmd = testConfig.command || testConfig.script;
|
|
670
|
+
if (cmd) {
|
|
671
|
+
console.log(chalk.dim(` → Executing local command: "${cmd}"`));
|
|
672
|
+
const runRes = await runLocalCommand(cmd);
|
|
673
|
+
let parsedJson = {};
|
|
674
|
+
try {
|
|
675
|
+
parsedJson = JSON.parse(runRes.stdout.trim());
|
|
676
|
+
} catch (e) {
|
|
677
|
+
throw new Error(`Command executed but stdout was not valid JSON: ${runRes.stdout}`);
|
|
678
|
+
}
|
|
679
|
+
result = {
|
|
680
|
+
exitCode: runRes.exitCode,
|
|
681
|
+
stdout: runRes.stdout,
|
|
682
|
+
stderr: runRes.stderr,
|
|
683
|
+
json: parsedJson,
|
|
684
|
+
output: parsedJson
|
|
685
|
+
};
|
|
686
|
+
} else {
|
|
687
|
+
throw new Error('testConfig type "json" requires a "command" or "script" to output JSON.');
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
} else if (testConfig.type === 'loadtest' || testConfig.type === 'geekload') {
|
|
691
|
+
const script = testConfig.script || testConfig.command;
|
|
692
|
+
if (!script) {
|
|
693
|
+
throw new Error(`testConfig type "${testConfig.type}" requires a "script" or "command" specifying the load test script path.`);
|
|
694
|
+
}
|
|
695
|
+
const scriptPath = path.resolve(process.cwd(), script);
|
|
696
|
+
console.log(chalk.dim(` → Running GeekLoad test script: ${script}`));
|
|
697
|
+
const { runLoadTest } = require('../../core/geekload');
|
|
698
|
+
const loadtestResult = await runLoadTest(scriptPath);
|
|
699
|
+
|
|
700
|
+
const Table = require('cli-table3');
|
|
701
|
+
const table = new Table({
|
|
702
|
+
head: [chalk.cyan('Metric'), chalk.cyan('Value')],
|
|
703
|
+
colWidths: [25, 20]
|
|
704
|
+
});
|
|
705
|
+
table.push(
|
|
706
|
+
['Virtual Users', loadtestResult.virtual_users],
|
|
707
|
+
['Duration (sec)', loadtestResult.duration_sec],
|
|
708
|
+
['Total Requests', loadtestResult.total_requests],
|
|
709
|
+
['Failed Requests', loadtestResult.failed_requests],
|
|
710
|
+
['Failure Rate', `${(loadtestResult.failure_rate * 100).toFixed(2)}%`],
|
|
711
|
+
['p95 Latency (ms)', loadtestResult.p95_latency_ms]
|
|
712
|
+
);
|
|
713
|
+
console.log('\n' + table.toString() + '\n');
|
|
714
|
+
|
|
715
|
+
result = {
|
|
716
|
+
exitCode: loadtestResult.failed_requests > 0 ? 1 : 0,
|
|
717
|
+
output: loadtestResult,
|
|
718
|
+
json: loadtestResult
|
|
719
|
+
};
|
|
720
|
+
|
|
528
721
|
} else {
|
|
529
722
|
console.log(chalk.red(`❌ Unsupported test type: ${testConfig.type}`));
|
|
530
723
|
return;
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
src/commands/tasks/task-info.js — View task details + testConfig + webhook URL
|
|
3
|
-
Usage: task-info <taskId> OR info <taskId>
|
|
3
|
+
Usage: task-info <taskId> OR info <taskId> load
|
|
4
4
|
*/
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
|
|
@@ -55,6 +55,13 @@ console.log(` ${chalk.dim('id ')} ${chalk.dim(task._id)}${task.taskID ? c
|
|
|
55
55
|
task.description.split('\n').forEach(l => console.log(` ${chalk.white(l)}`));
|
|
56
56
|
}
|
|
57
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
|
+
|
|
58
65
|
// ── Assignees ─────────────────────────────────────────────────────────────
|
|
59
66
|
if (task.assignedTo?.length) {
|
|
60
67
|
console.log(chalk.dim('\n assignees:'));
|
|
@@ -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
|
|