knoxis-helper 1.4.2 → 1.4.4
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/lib/knoxis-local-agent.js +72 -23
- package/package.json +1 -1
|
@@ -426,6 +426,20 @@ function sendJSON(res, statusCode, data, requestOrigin) {
|
|
|
426
426
|
res.end(JSON.stringify(data));
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
// Resolve the interactive pair programming script
|
|
430
|
+
function resolveInteractiveScript() {
|
|
431
|
+
const candidates = [
|
|
432
|
+
path.join(__dirname, 'knoxis-interactive-pair.js'),
|
|
433
|
+
path.join(__dirname, '..', 'knoxis-interactive-pair.js'),
|
|
434
|
+
path.join(os.homedir(), '.knoxis', 'agent', 'knoxis-interactive-pair.js'),
|
|
435
|
+
path.join(__dirname, '..', '..', 'knoxis-interactive-pair.js'),
|
|
436
|
+
];
|
|
437
|
+
for (const candidate of candidates) {
|
|
438
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
429
443
|
// Request handler
|
|
430
444
|
async function handleRequest(req, res) {
|
|
431
445
|
const parsedUrl = url.parse(req.url, true);
|
|
@@ -679,20 +693,6 @@ async function handleRequest(req, res) {
|
|
|
679
693
|
|
|
680
694
|
// ===== PAIR PROGRAMMING ENDPOINTS =====
|
|
681
695
|
|
|
682
|
-
// Resolve the interactive pair programming script
|
|
683
|
-
function resolveInteractiveScript() {
|
|
684
|
-
const candidates = [
|
|
685
|
-
path.join(__dirname, 'knoxis-interactive-pair.js'),
|
|
686
|
-
path.join(__dirname, '..', 'knoxis-interactive-pair.js'),
|
|
687
|
-
path.join(os.homedir(), '.knoxis', 'agent', 'knoxis-interactive-pair.js'),
|
|
688
|
-
path.join(__dirname, '..', '..', 'knoxis-interactive-pair.js'),
|
|
689
|
-
];
|
|
690
|
-
for (const candidate of candidates) {
|
|
691
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
692
|
-
}
|
|
693
|
-
return null;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
696
|
// Start pair programming session (opens terminal)
|
|
697
697
|
if (pathname === '/pair/start' && method === 'POST') {
|
|
698
698
|
// Build CLAUDE.md content from task when backend doesn't provide claudeMdContent
|
|
@@ -876,7 +876,15 @@ end tell
|
|
|
876
876
|
}
|
|
877
877
|
|
|
878
878
|
/**
|
|
879
|
-
* Open terminal on Windows
|
|
879
|
+
* Open terminal on Windows.
|
|
880
|
+
*
|
|
881
|
+
* If the agent is running elevated (admin), Windows won't let us spawn an
|
|
882
|
+
* unelevated child directly — `start cmd` inherits the parent's integrity
|
|
883
|
+
* level. We detect that case and route through a one-shot scheduled task
|
|
884
|
+
* at /rl LIMITED, which runs as the interactive user at medium integrity.
|
|
885
|
+
* That matters because claude's credentials live under the user's real
|
|
886
|
+
* %USERPROFILE%\.claude\, not the admin profile, so an elevated cmd can't
|
|
887
|
+
* find them and hangs on launch.
|
|
880
888
|
*/
|
|
881
889
|
function openWindowsTerminal(workspaceDir, command) {
|
|
882
890
|
return new Promise((resolve, reject) => {
|
|
@@ -884,16 +892,57 @@ function openWindowsTerminal(workspaceDir, command) {
|
|
|
884
892
|
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
885
893
|
}
|
|
886
894
|
|
|
887
|
-
|
|
895
|
+
// `net session` exits 0 only when elevated.
|
|
896
|
+
exec('net session >nul 2>&1', (elevErr) => {
|
|
897
|
+
const isElevated = !elevErr;
|
|
888
898
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
899
|
+
if (!isElevated) {
|
|
900
|
+
const fullCommand = `start cmd /K "cd /d "${workspaceDir}" && ${command}"`;
|
|
901
|
+
exec(fullCommand, (error) => {
|
|
902
|
+
if (error) {
|
|
903
|
+
console.error('❌ Windows terminal error:', error);
|
|
904
|
+
reject(error);
|
|
905
|
+
} else {
|
|
906
|
+
console.log('✅ Terminal opened on Windows');
|
|
907
|
+
resolve();
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
return;
|
|
896
911
|
}
|
|
912
|
+
|
|
913
|
+
const tempBat = path.join(
|
|
914
|
+
os.tmpdir(),
|
|
915
|
+
`knoxis-launch-${process.pid}-${Date.now()}.bat`
|
|
916
|
+
);
|
|
917
|
+
const batContent = `@echo off\r\ncd /d "${workspaceDir}"\r\n${command}\r\ncmd /K\r\n`;
|
|
918
|
+
fs.writeFileSync(tempBat, batContent);
|
|
919
|
+
|
|
920
|
+
const taskName = `KnoxisSpawn_${process.pid}_${Date.now()}`;
|
|
921
|
+
const createCmd = `schtasks /create /tn "${taskName}" /tr "${tempBat}" /sc once /st 00:00 /it /rl LIMITED /f`;
|
|
922
|
+
|
|
923
|
+
exec(createCmd, (cErr) => {
|
|
924
|
+
if (cErr) {
|
|
925
|
+
try { fs.unlinkSync(tempBat); } catch (_) {}
|
|
926
|
+
console.error('❌ Windows terminal error (schtasks create):', cErr);
|
|
927
|
+
return reject(cErr);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
exec(`schtasks /run /tn "${taskName}"`, (rErr) => {
|
|
931
|
+
// Task registration cleanup. Leave the .bat in %TEMP% — cmd may
|
|
932
|
+
// still have an open handle to it, and the OS cleans %TEMP%.
|
|
933
|
+
setTimeout(() => {
|
|
934
|
+
exec(`schtasks /delete /tn "${taskName}" /f`, () => {});
|
|
935
|
+
}, 5000);
|
|
936
|
+
|
|
937
|
+
if (rErr) {
|
|
938
|
+
console.error('❌ Windows terminal error (schtasks run):', rErr);
|
|
939
|
+
reject(rErr);
|
|
940
|
+
} else {
|
|
941
|
+
console.log('✅ Terminal opened on Windows (de-elevated via scheduled task)');
|
|
942
|
+
resolve();
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
});
|
|
897
946
|
});
|
|
898
947
|
});
|
|
899
948
|
}
|