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.
@@ -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
- const fullCommand = `start cmd /K "cd /d "${workspaceDir}" && ${command}"`;
895
+ // `net session` exits 0 only when elevated.
896
+ exec('net session >nul 2>&1', (elevErr) => {
897
+ const isElevated = !elevErr;
888
898
 
889
- exec(fullCommand, (error) => {
890
- if (error) {
891
- console.error('❌ Windows terminal error:', error);
892
- reject(error);
893
- } else {
894
- console.log('✅ Terminal opened on Windows');
895
- resolve();
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knoxis-helper",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "Local helper for Knoxis pair programming - connects your machine to Knoxis on qig.ai",
5
5
  "bin": {
6
6
  "knoxis-helper": "./bin/knoxis-helper.js"