adhdev 0.1.7 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/index.js +187 -77
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -858,14 +858,14 @@ var import_child_process3 = require("child_process");
858
858
  var net = __toESM(require("net"));
859
859
  var os = __toESM(require("os"));
860
860
  var CDP_PORTS = {
861
- cursor: 9333,
862
- antigravity: 9335,
863
- vscode: 9229,
864
- windsurf: 9337,
865
- "vscode-insiders": 9231,
866
- vscodium: 9233
861
+ cursor: [9333, 9334],
862
+ antigravity: [9335, 9336],
863
+ vscode: [9229, 9230],
864
+ windsurf: [9337, 9338],
865
+ "vscode-insiders": [9231, 9232],
866
+ vscodium: [9233, 9234]
867
867
  };
868
- var MAC_APP_NAMES = {
868
+ var MAC_APP_IDENTIFIERS = {
869
869
  cursor: "Cursor",
870
870
  antigravity: "Antigravity",
871
871
  vscode: "Visual Studio Code",
@@ -873,46 +873,120 @@ var MAC_APP_NAMES = {
873
873
  "vscode-insiders": "Visual Studio Code - Insiders",
874
874
  vscodium: "VSCodium"
875
875
  };
876
- async function findFreePort(startPort) {
876
+ var WIN_PROCESS_NAMES = {
877
+ cursor: "Cursor.exe",
878
+ antigravity: "Antigravity.exe",
879
+ vscode: "Code.exe",
880
+ windsurf: "Windsurf.exe",
881
+ "vscode-insiders": "Code - Insiders.exe",
882
+ vscodium: "VSCodium.exe"
883
+ };
884
+ async function findFreePort(ports) {
885
+ for (const port2 of ports) {
886
+ const free = await checkPortFree(port2);
887
+ if (free) return port2;
888
+ }
889
+ let port = ports[1] + 1;
890
+ while (port < ports[1] + 10) {
891
+ if (await checkPortFree(port)) return port;
892
+ port++;
893
+ }
894
+ throw new Error("No free port found");
895
+ }
896
+ function checkPortFree(port) {
877
897
  return new Promise((resolve) => {
878
898
  const server = net.createServer();
879
899
  server.unref();
880
- server.on("error", () => resolve(findFreePort(startPort + 1)));
881
- server.listen(startPort, "127.0.0.1", () => {
882
- server.close(() => resolve(startPort));
900
+ server.on("error", () => resolve(false));
901
+ server.listen(port, "127.0.0.1", () => {
902
+ server.close(() => resolve(true));
883
903
  });
884
904
  });
885
905
  }
886
- async function isPortInUse(port) {
906
+ async function isCdpActive(port) {
887
907
  return new Promise((resolve) => {
888
- const socket = new net.Socket();
889
- socket.setTimeout(1e3);
890
- socket.on("connect", () => {
891
- socket.destroy();
892
- resolve(true);
893
- });
894
- socket.on("timeout", () => {
895
- socket.destroy();
896
- resolve(false);
908
+ const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
909
+ timeout: 2e3
910
+ }, (res) => {
911
+ let data = "";
912
+ res.on("data", (c) => data += c);
913
+ res.on("end", () => {
914
+ try {
915
+ const info = JSON.parse(data);
916
+ resolve(!!info["WebKit-Version"] || !!info["Browser"]);
917
+ } catch {
918
+ resolve(false);
919
+ }
920
+ });
897
921
  });
898
- socket.on("error", () => {
899
- socket.destroy();
922
+ req.on("error", () => resolve(false));
923
+ req.on("timeout", () => {
924
+ req.destroy();
900
925
  resolve(false);
901
926
  });
902
- socket.connect(port, "127.0.0.1");
903
927
  });
904
928
  }
929
+ async function killIdeProcess(ideId) {
930
+ const platform3 = os.platform();
931
+ const appName = MAC_APP_IDENTIFIERS[ideId];
932
+ const winProcess = WIN_PROCESS_NAMES[ideId];
933
+ try {
934
+ if (platform3 === "darwin" && appName) {
935
+ try {
936
+ (0, import_child_process3.execSync)(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
937
+ timeout: 5e3
938
+ });
939
+ } catch {
940
+ try {
941
+ (0, import_child_process3.execSync)(`pkill -f "${appName}" 2>/dev/null`);
942
+ } catch {
943
+ }
944
+ }
945
+ } else if (platform3 === "win32" && winProcess) {
946
+ try {
947
+ (0, import_child_process3.execSync)(`taskkill /IM "${winProcess}" /F 2>nul`, { timeout: 5e3 });
948
+ } catch {
949
+ }
950
+ } else {
951
+ try {
952
+ (0, import_child_process3.execSync)(`pkill -f "${ideId}" 2>/dev/null`);
953
+ } catch {
954
+ }
955
+ }
956
+ for (let i = 0; i < 30; i++) {
957
+ await new Promise((r) => setTimeout(r, 500));
958
+ if (!isIdeRunning(ideId)) return true;
959
+ }
960
+ if (platform3 === "darwin" && appName) {
961
+ try {
962
+ (0, import_child_process3.execSync)(`pkill -9 -f "${appName}" 2>/dev/null`);
963
+ } catch {
964
+ }
965
+ } else if (platform3 === "win32" && winProcess) {
966
+ try {
967
+ (0, import_child_process3.execSync)(`taskkill /IM "${winProcess}" /F 2>nul`);
968
+ } catch {
969
+ }
970
+ }
971
+ await new Promise((r) => setTimeout(r, 2e3));
972
+ return !isIdeRunning(ideId);
973
+ } catch {
974
+ return false;
975
+ }
976
+ }
905
977
  function isIdeRunning(ideId) {
906
978
  const platform3 = os.platform();
907
- const appName = MAC_APP_NAMES[ideId];
908
- if (!appName) return false;
909
979
  try {
910
980
  if (platform3 === "darwin") {
981
+ const appName = MAC_APP_IDENTIFIERS[ideId];
982
+ if (!appName) return false;
911
983
  const result = (0, import_child_process3.execSync)(`pgrep -f "${appName}" 2>/dev/null`, { encoding: "utf-8" });
912
984
  return result.trim().length > 0;
913
985
  } else if (platform3 === "win32") {
914
- const result = (0, import_child_process3.execSync)(`tasklist /FI "IMAGENAME eq ${ideId}.exe" /NH 2>nul`, { encoding: "utf-8" });
915
- return result.includes(ideId);
986
+ const winProc = WIN_PROCESS_NAMES[ideId];
987
+ if (!winProc) return false;
988
+ const result = (0, import_child_process3.execSync)(`tasklist /FI "IMAGENAME eq ${winProc}" /NH 2>nul`, { encoding: "utf-8" });
989
+ return result.includes(winProc);
916
990
  } else {
917
991
  const result = (0, import_child_process3.execSync)(`pgrep -f "${ideId}" 2>/dev/null`, { encoding: "utf-8" });
918
992
  return result.trim().length > 0;
@@ -921,6 +995,22 @@ function isIdeRunning(ideId) {
921
995
  return false;
922
996
  }
923
997
  }
998
+ function detectCurrentWorkspace(ideId) {
999
+ const platform3 = os.platform();
1000
+ if (platform3 !== "darwin") return void 0;
1001
+ try {
1002
+ const appName = MAC_APP_IDENTIFIERS[ideId];
1003
+ if (!appName) return void 0;
1004
+ const result = (0, import_child_process3.execSync)(
1005
+ `lsof -c "${appName}" 2>/dev/null | grep cwd | head -1 | awk '{print $NF}'`,
1006
+ { encoding: "utf-8", timeout: 3e3 }
1007
+ );
1008
+ const dir = result.trim();
1009
+ if (dir && dir !== "/") return dir;
1010
+ } catch {
1011
+ }
1012
+ return void 0;
1013
+ }
924
1014
  async function launchWithCdp(options = {}) {
925
1015
  const platform3 = os.platform();
926
1016
  let targetIde;
@@ -933,8 +1023,8 @@ async function launchWithCdp(options = {}) {
933
1023
  ideId: options.ideId,
934
1024
  ideName: options.ideId,
935
1025
  port: 0,
936
- alreadyRunning: false,
937
- cdpAlreadyActive: false,
1026
+ action: "failed",
1027
+ message: "",
938
1028
  error: `IDE '${options.ideId}' not found or not installed`
939
1029
  };
940
1030
  }
@@ -952,42 +1042,66 @@ async function launchWithCdp(options = {}) {
952
1042
  ideId: "unknown",
953
1043
  ideName: "Unknown",
954
1044
  port: 0,
955
- alreadyRunning: false,
956
- cdpAlreadyActive: false,
1045
+ action: "failed",
1046
+ message: "",
957
1047
  error: "No IDE found. Install VS Code, Cursor, or Antigravity first."
958
1048
  };
959
1049
  }
960
1050
  }
961
- const defaultPort = CDP_PORTS[targetIde.id] || 9333;
962
- const requestedPort = options.port || defaultPort;
963
- const cdpAlreadyActive = await isPortInUse(requestedPort);
964
- if (cdpAlreadyActive) {
965
- return {
966
- success: true,
967
- ideId: targetIde.id,
968
- ideName: targetIde.displayName,
969
- port: requestedPort,
970
- alreadyRunning: true,
971
- cdpAlreadyActive: true
972
- };
1051
+ const portPair = CDP_PORTS[targetIde.id] || [9333, 9334];
1052
+ for (const port2 of portPair) {
1053
+ if (await isCdpActive(port2)) {
1054
+ return {
1055
+ success: true,
1056
+ ideId: targetIde.id,
1057
+ ideName: targetIde.displayName,
1058
+ port: port2,
1059
+ action: "reused",
1060
+ message: `CDP already active on port ${port2}`
1061
+ };
1062
+ }
973
1063
  }
974
1064
  const alreadyRunning = isIdeRunning(targetIde.id);
975
- const port = await findFreePort(requestedPort);
1065
+ const workspace = options.workspace || (alreadyRunning ? detectCurrentWorkspace(targetIde.id) : void 0);
1066
+ if (alreadyRunning) {
1067
+ const killed = await killIdeProcess(targetIde.id);
1068
+ if (!killed) {
1069
+ return {
1070
+ success: false,
1071
+ ideId: targetIde.id,
1072
+ ideName: targetIde.displayName,
1073
+ port: 0,
1074
+ action: "failed",
1075
+ message: "",
1076
+ error: `Could not stop ${targetIde.displayName}. Close it manually and try again.`
1077
+ };
1078
+ }
1079
+ await new Promise((r) => setTimeout(r, 3e3));
1080
+ }
1081
+ const port = await findFreePort(portPair);
976
1082
  try {
977
1083
  if (platform3 === "darwin") {
978
- await launchMacOS(targetIde, port, options.workspace, options.newWindow);
1084
+ await launchMacOS(targetIde, port, workspace, options.newWindow);
979
1085
  } else if (platform3 === "win32") {
980
- await launchWindows(targetIde, port, options.workspace, options.newWindow);
1086
+ await launchWindows(targetIde, port, workspace, options.newWindow);
981
1087
  } else {
982
- await launchLinux(targetIde, port, options.workspace, options.newWindow);
1088
+ await launchLinux(targetIde, port, workspace, options.newWindow);
1089
+ }
1090
+ let cdpReady = false;
1091
+ for (let i = 0; i < 30; i++) {
1092
+ await new Promise((r) => setTimeout(r, 500));
1093
+ if (await isCdpActive(port)) {
1094
+ cdpReady = true;
1095
+ break;
1096
+ }
983
1097
  }
984
1098
  return {
985
1099
  success: true,
986
1100
  ideId: targetIde.id,
987
1101
  ideName: targetIde.displayName,
988
1102
  port,
989
- alreadyRunning,
990
- cdpAlreadyActive: false
1103
+ action: alreadyRunning ? "restarted" : "started",
1104
+ message: cdpReady ? `${targetIde.displayName} launched with CDP on port ${port}` : `${targetIde.displayName} launched (CDP may take a moment to initialize)`
991
1105
  };
992
1106
  } catch (e) {
993
1107
  return {
@@ -995,30 +1109,25 @@ async function launchWithCdp(options = {}) {
995
1109
  ideId: targetIde.id,
996
1110
  ideName: targetIde.displayName,
997
1111
  port,
998
- alreadyRunning,
999
- cdpAlreadyActive: false,
1112
+ action: "failed",
1113
+ message: "",
1000
1114
  error: e?.message || String(e)
1001
1115
  };
1002
1116
  }
1003
1117
  }
1004
1118
  async function launchMacOS(ide, port, workspace, newWindow) {
1005
- const appName = MAC_APP_NAMES[ide.id];
1006
- if (!appName) {
1007
- if (ide.cliCommand) {
1008
- const args2 = ["--remote-debugging-port=" + port];
1009
- if (newWindow) args2.push("--new-window");
1010
- if (workspace) args2.push(workspace);
1011
- (0, import_child_process3.spawn)(ide.cliCommand, args2, { detached: true, stdio: "ignore" }).unref();
1012
- return;
1013
- }
1014
- throw new Error(`No app name or CLI for ${ide.displayName}`);
1015
- }
1119
+ const appName = MAC_APP_IDENTIFIERS[ide.id];
1016
1120
  const args = ["--remote-debugging-port=" + port];
1017
1121
  if (newWindow) args.push("--new-window");
1018
1122
  if (workspace) args.push(workspace);
1019
- const openArgs = ["-a", appName, "--args", ...args];
1020
- if (newWindow) openArgs.splice(1, 0, "-n");
1021
- (0, import_child_process3.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
1123
+ if (appName) {
1124
+ const openArgs = ["-a", appName, "--args", ...args];
1125
+ (0, import_child_process3.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
1126
+ } else if (ide.cliCommand) {
1127
+ (0, import_child_process3.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
1128
+ } else {
1129
+ throw new Error(`No app identifier or CLI for ${ide.displayName}`);
1130
+ }
1022
1131
  }
1023
1132
  async function launchWindows(ide, port, workspace, newWindow) {
1024
1133
  const cli = ide.cliCommand;
@@ -1032,7 +1141,6 @@ async function launchWindows(ide, port, workspace, newWindow) {
1032
1141
  detached: true,
1033
1142
  stdio: "ignore",
1034
1143
  shell: true
1035
- // Windows에서 .cmd 파일 실행 위해
1036
1144
  }).unref();
1037
1145
  }
1038
1146
  async function launchLinux(ide, port, workspace, newWindow) {
@@ -1052,13 +1160,12 @@ program.name("adhdev").description("\u{1F309} ADHDev \u2014 Agent Dashboard Hub
1052
1160
  program.command("setup", { isDefault: true }).description("Run the interactive setup wizard").option("-f, --force", "Force re-run setup even if already configured").action(async (options) => {
1053
1161
  await runWizard({ force: options.force });
1054
1162
  });
1055
- program.command("launch [ide]").description("Launch IDE with Chrome DevTools Protocol (CDP) debugging port").option("-p, --port <port>", "CDP port number (default: auto per IDE)").option("-w, --workspace <path>", "Workspace/folder to open").option("-n, --new-window", "Open in a new window").action(async (ideArg, options) => {
1163
+ program.command("launch [ide]").description("Launch or relaunch IDE with CDP debugging port (kills existing process first)").option("-w, --workspace <path>", "Workspace/folder to open").option("-n, --new-window", "Open in a new window").action(async (ideArg, options) => {
1056
1164
  const ora2 = await import("ora");
1057
1165
  const spinner = ora2.default("Detecting IDE...").start();
1058
1166
  try {
1059
1167
  const result = await launchWithCdp({
1060
1168
  ideId: ideArg,
1061
- port: options.port ? parseInt(options.port) : void 0,
1062
1169
  workspace: options.workspace,
1063
1170
  newWindow: options.newWindow
1064
1171
  });
@@ -1075,7 +1182,7 @@ program.command("launch [ide]").description("Launch IDE with Chrome DevTools Pro
1075
1182
  }
1076
1183
  });
1077
1184
  console.log(import_chalk2.default.gray(`
1078
- Usage: adhdev launch <ide-id> [--port <port>]
1185
+ Usage: adhdev launch <ide-id>
1079
1186
  `));
1080
1187
  process.exit(1);
1081
1188
  }
@@ -1083,13 +1190,16 @@ program.command("launch [ide]").description("Launch IDE with Chrome DevTools Pro
1083
1190
  console.log(import_chalk2.default.bold(" \u{1F680} IDE Launched with CDP\n"));
1084
1191
  console.log(` ${import_chalk2.default.bold("IDE:")} ${result.ideName}`);
1085
1192
  console.log(` ${import_chalk2.default.bold("CDP:")} ${import_chalk2.default.cyan(`ws://127.0.0.1:${result.port}`)}`);
1086
- if (result.cdpAlreadyActive) {
1087
- console.log(` ${import_chalk2.default.bold("Status:")} ${import_chalk2.default.yellow("CDP already active (reusing)")}`);
1088
- } else if (result.alreadyRunning) {
1089
- console.log(` ${import_chalk2.default.bold("Status:")} ${import_chalk2.default.yellow("\u26A0 IDE was already running without CDP")}`);
1090
- console.log(import_chalk2.default.gray(" Close and reopen, or use the extension's relaunch command"));
1091
- } else {
1092
- console.log(` ${import_chalk2.default.bold("Status:")} ${import_chalk2.default.green("\u2713 Started fresh with CDP")}`);
1193
+ switch (result.action) {
1194
+ case "reused":
1195
+ console.log(` ${import_chalk2.default.bold("Action:")} ${import_chalk2.default.yellow("CDP already active \u2014 reusing existing session")}`);
1196
+ break;
1197
+ case "restarted":
1198
+ console.log(` ${import_chalk2.default.bold("Action:")} ${import_chalk2.default.green("\u2713 Killed existing process \u2192 Restarted with CDP")}`);
1199
+ break;
1200
+ case "started":
1201
+ console.log(` ${import_chalk2.default.bold("Action:")} ${import_chalk2.default.green("\u2713 Started fresh with CDP")}`);
1202
+ break;
1093
1203
  }
1094
1204
  console.log();
1095
1205
  console.log(import_chalk2.default.gray(" Bridge extension will auto-connect to CDP."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adhdev",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "ADHDev CLI — Detect, install and configure your IDE + AI agent extensions",
5
5
  "main": "dist/index.js",
6
6
  "bin": {