thinkncollab-shell 0.0.13 ā 0.0.14
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 +8 -8
- package/package.json +1 -1
- package/src/commands/tasks/complete.js +180 -0
- package/src/commands/tasks/task.js +3 -0
package/Readme.md
CHANGED
|
@@ -22,7 +22,7 @@ A real-time collaborative terminal shell with WebSocket integration, built on to
|
|
|
22
22
|
- [Notification Commands](#notification-commands)
|
|
23
23
|
- [Shell Commands](#shell-commands)
|
|
24
24
|
- [GitHub Integration Commands](#github-integration-commands)
|
|
25
|
-
- [
|
|
25
|
+
- [`tncproject` Auto-Boot](#tncproject-auto-boot)
|
|
26
26
|
- [Notification Window](#notification-window)
|
|
27
27
|
- [Built-in Shell Features](#built-in-shell-features)
|
|
28
28
|
- [Single-Command Mode](#single-command-mode)
|
|
@@ -38,7 +38,7 @@ A real-time collaborative terminal shell with WebSocket integration, built on to
|
|
|
38
38
|
- š **Task Management** ā Accept, start, complete, and reject tasks from the CLI
|
|
39
39
|
- šŗ **Terminal Sharing** ā Share your live terminal session with teammates (read + write access)
|
|
40
40
|
- š **Notification Window** ā Background TCP-based popup window for live events
|
|
41
|
-
- š
|
|
41
|
+
- š **`tncproject` Auto-Boot** ā Drop a config file in your project root to auto-login and join a room on shell start
|
|
42
42
|
- š”ļø **Shell Token Auth** ā Secure HMAC-SHA256 token validation for shell connections
|
|
43
43
|
- š **File System** ā Built-in `ls`, `cd`, `pwd`, plus passthrough to your system shell
|
|
44
44
|
- š **History** ā Persisted command history at `~/.thinknsh_history`
|
|
@@ -443,9 +443,9 @@ Create a new issue in the linked GitHub repository. If arguments are missing, th
|
|
|
443
443
|
|
|
444
444
|
---
|
|
445
445
|
|
|
446
|
-
##
|
|
446
|
+
## `tncproject` Auto-Boot
|
|
447
447
|
|
|
448
|
-
Place a
|
|
448
|
+
Place a `tncproject` file in your project root to auto-login and auto-join a room when `thinknsh` starts in that directory ā no manual `login` or `join` needed.
|
|
449
449
|
|
|
450
450
|
```
|
|
451
451
|
.tncproject
|
|
@@ -456,12 +456,12 @@ On startup the shell sends the encrypted config to the server, receives a shell
|
|
|
456
456
|
```
|
|
457
457
|
š tncproject found
|
|
458
458
|
Room : room-abc123
|
|
459
|
-
File : /home/user/myproject
|
|
459
|
+
File : /home/user/myproject/tncproject
|
|
460
460
|
|
|
461
461
|
ā
Raman Singh ā room "My Project Room"
|
|
462
462
|
```
|
|
463
463
|
|
|
464
|
-
If
|
|
464
|
+
If `tncproject` auth fails, the shell falls back to manual login.
|
|
465
465
|
|
|
466
466
|
---
|
|
467
467
|
|
|
@@ -560,8 +560,8 @@ thinknsh> notify "Daily standup in 5 mins"
|
|
|
560
560
|
thinknsh> status
|
|
561
561
|
thinknsh> exit
|
|
562
562
|
|
|
563
|
-
#
|
|
564
|
-
cd ~/myproject # contains
|
|
563
|
+
# tncproject auto-boot (no login needed)
|
|
564
|
+
cd ~/myproject # contains tncproject
|
|
565
565
|
thinknsh # auto-logs in + auto-joins room
|
|
566
566
|
|
|
567
567
|
# Quick one-liners from CI / scripts
|
package/package.json
CHANGED
|
@@ -497,6 +497,14 @@ function judgeLocally(testConfig, result) {
|
|
|
497
497
|
break;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
case 'tnc-build': {
|
|
501
|
+
const passed = result.exitCode === 0;
|
|
502
|
+
return {
|
|
503
|
+
passed,
|
|
504
|
+
reason: passed ? 'TNC Local CI pipeline passed' : 'TNC Local CI pipeline failed'
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
500
508
|
default:
|
|
501
509
|
return { passed: false, reason: `Unknown test type: ${type}` };
|
|
502
510
|
}
|
|
@@ -718,6 +726,178 @@ const authConfig = { ...boardAuth, ...(testConfig.auth || {}) };
|
|
|
718
726
|
json: loadtestResult
|
|
719
727
|
};
|
|
720
728
|
|
|
729
|
+
} else if (testConfig.type === 'tnc-build') {
|
|
730
|
+
const fs = require('fs');
|
|
731
|
+
const path = require('path');
|
|
732
|
+
const { spawn } = require('child_process');
|
|
733
|
+
|
|
734
|
+
const projectRoot = process.cwd();
|
|
735
|
+
const workflowsDir = path.join(projectRoot, '.tnc/workflows');
|
|
736
|
+
|
|
737
|
+
if (!fs.existsSync(workflowsDir)) {
|
|
738
|
+
throw new Error('No .tnc/workflows directory found in project root.');
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const files = fs.readdirSync(workflowsDir).filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
|
|
742
|
+
if (files.length === 0) {
|
|
743
|
+
throw new Error('No workflow files found in .tnc/workflows/');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const workflowFile = files[0];
|
|
747
|
+
console.log(chalk.dim(` ā Found workflow file: ${workflowFile}`));
|
|
748
|
+
const content = fs.readFileSync(path.join(workflowsDir, workflowFile), 'utf8');
|
|
749
|
+
|
|
750
|
+
// Parse steps
|
|
751
|
+
const lines = content.split('\n');
|
|
752
|
+
let workflowName = workflowFile;
|
|
753
|
+
let steps = [];
|
|
754
|
+
let currentStepName = '';
|
|
755
|
+
|
|
756
|
+
for (let i = 0; i < lines.length; i++) {
|
|
757
|
+
const line = lines[i].trim();
|
|
758
|
+
if (line.startsWith('name:')) {
|
|
759
|
+
workflowName = line.substring(5).trim().replace(/['"]/g, '');
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (line.startsWith('-')) {
|
|
763
|
+
const cleaned = line.substring(1).trim();
|
|
764
|
+
if (cleaned.startsWith('name:')) {
|
|
765
|
+
currentStepName = cleaned.substring(5).trim().replace(/['"]/g, '');
|
|
766
|
+
} else if (cleaned.startsWith('run:')) {
|
|
767
|
+
let runCmd = cleaned.substring(4).trim();
|
|
768
|
+
if ((runCmd.startsWith('"') && runCmd.endsWith('"')) || (runCmd.startsWith("'") && runCmd.endsWith("'"))) {
|
|
769
|
+
runCmd = runCmd.substring(1, runCmd.length - 1);
|
|
770
|
+
}
|
|
771
|
+
if (runCmd === '|') {
|
|
772
|
+
let block = [];
|
|
773
|
+
let j = i + 1;
|
|
774
|
+
while (j < lines.length) {
|
|
775
|
+
const next = lines[j];
|
|
776
|
+
const match = next.match(/^(\s+)(.*)/);
|
|
777
|
+
if (match && match[1].length > 4) {
|
|
778
|
+
block.push(next.trim());
|
|
779
|
+
j++;
|
|
780
|
+
} else {
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
runCmd = block.join('\n');
|
|
785
|
+
i = j - 1;
|
|
786
|
+
}
|
|
787
|
+
steps.push({
|
|
788
|
+
name: currentStepName || `run: ${runCmd.substring(0, 30)}`,
|
|
789
|
+
run: runCmd
|
|
790
|
+
});
|
|
791
|
+
currentStepName = '';
|
|
792
|
+
}
|
|
793
|
+
} else if (line.startsWith('name:')) {
|
|
794
|
+
currentStepName = line.substring(5).trim().replace(/['"]/g, '');
|
|
795
|
+
} else if (line.startsWith('run:')) {
|
|
796
|
+
let runCmd = line.substring(4).trim();
|
|
797
|
+
if ((runCmd.startsWith('"') && runCmd.endsWith('"')) || (runCmd.startsWith("'") && runCmd.endsWith("'"))) {
|
|
798
|
+
runCmd = runCmd.substring(1, runCmd.length - 1);
|
|
799
|
+
}
|
|
800
|
+
if (runCmd === '|') {
|
|
801
|
+
let block = [];
|
|
802
|
+
let j = i + 1;
|
|
803
|
+
while (j < lines.length) {
|
|
804
|
+
const next = lines[j];
|
|
805
|
+
const match = next.match(/^(\s+)(.*)/);
|
|
806
|
+
if (match && match[1].length > 4) {
|
|
807
|
+
block.push(next.trim());
|
|
808
|
+
j++;
|
|
809
|
+
} else {
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
runCmd = block.join('\n');
|
|
814
|
+
i = j - 1;
|
|
815
|
+
}
|
|
816
|
+
steps.push({
|
|
817
|
+
name: currentStepName || `run: ${runCmd.substring(0, 30)}`,
|
|
818
|
+
run: runCmd
|
|
819
|
+
});
|
|
820
|
+
currentStepName = '';
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (steps.length === 0) {
|
|
825
|
+
throw new Error('No steps found in the workflow file.');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
console.log(chalk.cyan(`\nš [TNC Actions] Starting Local Workflow: "${workflowName}"\n`));
|
|
829
|
+
|
|
830
|
+
const stepResults = [];
|
|
831
|
+
let overallPassed = true;
|
|
832
|
+
let logsBuffer = '';
|
|
833
|
+
|
|
834
|
+
for (let s = 0; s < steps.length; s++) {
|
|
835
|
+
const step = steps[s];
|
|
836
|
+
console.log(chalk.cyan(`āļø [Step ${s+1}/${steps.length}] "${step.name}"`));
|
|
837
|
+
console.log(chalk.gray(`$ ${step.run}`));
|
|
838
|
+
|
|
839
|
+
// Run command shell execution
|
|
840
|
+
const exitCode = await new Promise((resolve) => {
|
|
841
|
+
const shellCmd = process.platform === 'win32' ? 'cmd.exe' : '/bin/sh';
|
|
842
|
+
const shellArgs = process.platform === 'win32' ? ['/s', '/c', step.run] : ['-c', step.run];
|
|
843
|
+
|
|
844
|
+
const proc = spawn(shellCmd, shellArgs, {
|
|
845
|
+
cwd: projectRoot,
|
|
846
|
+
env: { ...process.env, PATH: process.env.PATH, PUPPETEER_SKIP_DOWNLOAD: 'true' }
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
proc.stdout.on('data', (chunk) => {
|
|
850
|
+
const chunkStr = chunk.toString();
|
|
851
|
+
process.stdout.write(chunkStr);
|
|
852
|
+
logsBuffer += chunkStr;
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
proc.stderr.on('data', (chunk) => {
|
|
856
|
+
const chunkStr = chunk.toString();
|
|
857
|
+
process.stderr.write(chalk.red(chunkStr));
|
|
858
|
+
logsBuffer += chunkStr;
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
proc.on('close', (code) => {
|
|
862
|
+
resolve(code);
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const stepPassed = (exitCode === 0);
|
|
867
|
+
stepResults.push({
|
|
868
|
+
name: step.name,
|
|
869
|
+
command: step.run,
|
|
870
|
+
exitCode,
|
|
871
|
+
passed: stepPassed
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
if (!stepPassed) {
|
|
875
|
+
overallPassed = false;
|
|
876
|
+
console.log(chalk.red(`\nā Step "${step.name}" failed with exit code: ${exitCode}. Aborting.\n`));
|
|
877
|
+
break;
|
|
878
|
+
} else {
|
|
879
|
+
console.log(chalk.green(`\nā
Step "${step.name}" passed successfully.\n`));
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
result = {
|
|
884
|
+
exitCode: overallPassed ? 0 : 1,
|
|
885
|
+
stdout: logsBuffer,
|
|
886
|
+
stderr: '',
|
|
887
|
+
output: {
|
|
888
|
+
workflow: workflowName,
|
|
889
|
+
status: overallPassed ? 'passed' : 'failed',
|
|
890
|
+
stepsCount: steps.length,
|
|
891
|
+
stepsRun: stepResults
|
|
892
|
+
},
|
|
893
|
+
json: {
|
|
894
|
+
workflow: workflowName,
|
|
895
|
+
status: overallPassed ? 'passed' : 'failed',
|
|
896
|
+
stepsCount: steps.length,
|
|
897
|
+
stepsRun: stepResults
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
|
|
721
901
|
} else {
|
|
722
902
|
console.log(chalk.red(`ā Unsupported test type: ${testConfig.type}`));
|
|
723
903
|
return;
|
|
@@ -140,6 +140,9 @@ console.log(` ${chalk.dim('id ')} ${chalk.dim(task._id)}${task.taskID ? c
|
|
|
140
140
|
if (tc.type === 'regex')
|
|
141
141
|
console.log(` ${chalk.dim('expect ')} regex ${chalk.green(tc.expect?.regex || '')}`);
|
|
142
142
|
|
|
143
|
+
if (tc.type === 'tnc-build')
|
|
144
|
+
console.log(` ${chalk.dim('runner ')} ${chalk.green('Local tnc-ci.yml workflow execution')}`);
|
|
145
|
+
|
|
143
146
|
} else {
|
|
144
147
|
console.log(chalk.yellow('\n ā ļø No test config ā task will need manual completion'));
|
|
145
148
|
console.log(chalk.dim(' Use: complete <task-id>'));
|