assistme 0.1.9 → 0.1.10

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/dist/index.js CHANGED
@@ -394,7 +394,7 @@ import {
394
394
  import { WebSocket } from "ws";
395
395
  import { execSync, spawn } from "child_process";
396
396
  import { platform } from "os";
397
- import { existsSync } from "fs";
397
+ import { existsSync, unlinkSync } from "fs";
398
398
  var BrowserController = class {
399
399
  ws = null;
400
400
  debugPort;
@@ -884,7 +884,7 @@ async function killChromeGracefully() {
884
884
  stdio: ["pipe", "pipe", "pipe"]
885
885
  });
886
886
  } else if (os === "linux") {
887
- execSync("pkill -TERM -f chrome", {
887
+ execSync("pkill -TERM -f '(chrome|chromium)'", {
888
888
  timeout: 5e3,
889
889
  stdio: ["pipe", "pipe", "pipe"]
890
890
  });
@@ -898,33 +898,78 @@ async function killChromeGracefully() {
898
898
  }
899
899
  const start = Date.now();
900
900
  while (Date.now() - start < 8e3) {
901
- if (!isChromeRunning()) return;
901
+ if (!isChromeRunning()) {
902
+ log.debug(`Chrome exited after ${Date.now() - start}ms`);
903
+ return;
904
+ }
902
905
  await new Promise((r) => setTimeout(r, 500));
903
906
  }
907
+ log.debug("Chrome still running after graceful quit, force-killing...");
904
908
  try {
905
909
  if (os === "win32") {
906
910
  execSync("taskkill /F /IM chrome.exe", {
907
911
  stdio: ["pipe", "pipe", "pipe"]
908
912
  });
909
913
  } else {
910
- execSync("pkill -9 -f chrome", {
914
+ execSync("pkill -9 -f '(chrome|chromium)'", {
911
915
  stdio: ["pipe", "pipe", "pipe"]
912
916
  });
913
917
  }
914
918
  } catch {
915
919
  }
916
920
  await new Promise((r) => setTimeout(r, 1e3));
921
+ if (os !== "win32") {
922
+ const home = process.env.HOME;
923
+ if (home) {
924
+ const lockPaths = os === "darwin" ? [
925
+ `${home}/Library/Application Support/Google/Chrome/SingletonLock`,
926
+ `${home}/Library/Application Support/Google/Chrome/SingletonSocket`,
927
+ `${home}/Library/Application Support/Google/Chrome/SingletonCookie`
928
+ ] : [
929
+ `${home}/.config/google-chrome/SingletonLock`,
930
+ `${home}/.config/google-chrome/SingletonSocket`,
931
+ `${home}/.config/google-chrome/SingletonCookie`,
932
+ `${home}/.config/chromium/SingletonLock`,
933
+ `${home}/.config/chromium/SingletonSocket`,
934
+ `${home}/.config/chromium/SingletonCookie`
935
+ ];
936
+ for (const lockPath of lockPaths) {
937
+ try {
938
+ if (existsSync(lockPath)) {
939
+ unlinkSync(lockPath);
940
+ log.debug(`Removed stale lock: ${lockPath}`);
941
+ }
942
+ } catch {
943
+ }
944
+ }
945
+ }
946
+ }
917
947
  }
918
948
  function spawnChrome(chromePath, port) {
919
949
  const cdpFlag = `--remote-debugging-port=${port}`;
920
950
  log.debug(`Spawning Chrome: ${chromePath} ${cdpFlag} --restore-last-session`);
921
951
  const child = spawn(chromePath, [cdpFlag, "--restore-last-session"], {
922
952
  detached: true,
923
- stdio: "ignore"
953
+ stdio: ["ignore", "pipe", "pipe"]
954
+ });
955
+ let stderr = "";
956
+ child.stderr?.on("data", (chunk) => {
957
+ stderr += chunk.toString();
924
958
  });
925
959
  child.on("error", (err) => {
926
960
  log.error(`Chrome spawn error: ${err.message}`);
927
961
  });
962
+ child.on("exit", (code, signal) => {
963
+ if (code !== null && code !== 0) {
964
+ log.debug(`Chrome exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`);
965
+ if (stderr) {
966
+ const lines = stderr.split("\n").filter(Boolean).slice(0, 5);
967
+ for (const line of lines) {
968
+ log.debug(` chrome stderr: ${line}`);
969
+ }
970
+ }
971
+ }
972
+ });
928
973
  child.unref();
929
974
  return child;
930
975
  }
@@ -978,34 +1023,57 @@ async function ensureBrowserAvailable(port = 9222) {
978
1023
  if (running) {
979
1024
  log.debug("Killing Chrome gracefully for restart with CDP...");
980
1025
  await killChromeGracefully();
981
- spawnChrome(chromePath, port);
1026
+ await new Promise((r) => setTimeout(r, 2e3));
1027
+ const child2 = spawnChrome(chromePath, port);
982
1028
  if (await waitForCDP(browser)) {
983
1029
  return { success: true, action: "restarted", chromePath };
984
1030
  }
1031
+ if (child2.exitCode !== null) {
1032
+ log.debug(
1033
+ `Chrome process already exited (code ${child2.exitCode}) \u2014 may have crashed or profile is locked`
1034
+ );
1035
+ return {
1036
+ success: false,
1037
+ action: "launch_failed",
1038
+ chromePath,
1039
+ detail: `Chrome exited immediately (code ${child2.exitCode}). This often means the profile is locked by another Chrome instance. Try closing all Chrome windows first, then run assistme again.`
1040
+ };
1041
+ }
985
1042
  log.debug("First CDP wait timed out after restart, retrying...");
986
1043
  if (await waitForCDP(browser, 15e3)) {
987
1044
  return { success: true, action: "restarted", chromePath };
988
1045
  }
1046
+ const stillRunning2 = isChromeRunning();
989
1047
  return {
990
1048
  success: false,
991
1049
  action: "launch_failed",
992
1050
  chromePath,
993
- detail: "Chrome was restarted but CDP did not become reachable within timeout."
1051
+ detail: stillRunning2 ? "Chrome is running but CDP port is not responding. Chrome may have started without the --remote-debugging-port flag. Try: 1) Quit Chrome completely, 2) Run assistme again." : "Chrome was restarted but exited unexpectedly. Check if Chrome can start normally from the command line."
994
1052
  };
995
1053
  }
996
- spawnChrome(chromePath, port);
1054
+ const child = spawnChrome(chromePath, port);
997
1055
  if (await waitForCDP(browser)) {
998
1056
  return { success: true, action: "launched", chromePath };
999
1057
  }
1058
+ if (child.exitCode !== null) {
1059
+ log.debug(`Chrome process already exited (code ${child.exitCode})`);
1060
+ return {
1061
+ success: false,
1062
+ action: "launch_failed",
1063
+ chromePath,
1064
+ detail: `Chrome exited immediately (code ${child.exitCode}). Try launching Chrome manually to see any error dialogs.`
1065
+ };
1066
+ }
1000
1067
  log.debug("First CDP wait timed out after launch, retrying...");
1001
1068
  if (await waitForCDP(browser, 15e3)) {
1002
1069
  return { success: true, action: "launched", chromePath };
1003
1070
  }
1071
+ const stillRunning = isChromeRunning();
1004
1072
  return {
1005
1073
  success: false,
1006
1074
  action: "launch_failed",
1007
1075
  chromePath,
1008
- detail: "Chrome was launched but CDP did not become reachable within timeout."
1076
+ detail: stillRunning ? "Chrome is running but CDP port is not responding. Try quitting Chrome completely and running assistme again." : "Chrome exited unexpectedly after launch."
1009
1077
  };
1010
1078
  }
1011
1079
  var browserInstance = null;
@@ -1161,7 +1229,7 @@ import {
1161
1229
  readFileSync,
1162
1230
  writeFileSync,
1163
1231
  statSync,
1164
- unlinkSync,
1232
+ unlinkSync as unlinkSync2,
1165
1233
  rmSync
1166
1234
  } from "fs";
1167
1235
  import { join, basename, dirname } from "path";
@@ -1581,9 +1649,9 @@ ${content}
1581
1649
  await execUnzip(`unzip -o "${zipPath}" -d "${skillDir}"`, {
1582
1650
  timeout: 15e3
1583
1651
  });
1584
- unlinkSync(zipPath);
1652
+ unlinkSync2(zipPath);
1585
1653
  } catch {
1586
- unlinkSync(zipPath);
1654
+ unlinkSync2(zipPath);
1587
1655
  const fileResp = await fetch(
1588
1656
  `${CLAWHUB_API}/skills/${encodeURIComponent(name)}/file?path=SKILL.md&tag=latest`
1589
1657
  );
@@ -1654,7 +1722,7 @@ ${content}
1654
1722
  if (dir !== SKILLS_DIR) {
1655
1723
  rmSync(dir, { recursive: true, force: true });
1656
1724
  } else {
1657
- unlinkSync(skill.filePath);
1725
+ unlinkSync2(skill.filePath);
1658
1726
  }
1659
1727
  this.skills.delete(name);
1660
1728
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistme",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "AssistMe CLI Agent - AI-powered assistant that controls your real browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,7 @@
17
17
  import { WebSocket } from "ws";
18
18
  import { execSync, spawn, type ChildProcess } from "node:child_process";
19
19
  import { platform } from "node:os";
20
- import { existsSync } from "node:fs";
20
+ import { existsSync, unlinkSync } from "node:fs";
21
21
  import { log } from "../utils/logger.js";
22
22
 
23
23
  interface CDPTab {
@@ -639,7 +639,7 @@ async function killChromeGracefully(): Promise<void> {
639
639
  stdio: ["pipe", "pipe", "pipe"],
640
640
  });
641
641
  } else if (os === "linux") {
642
- execSync("pkill -TERM -f chrome", {
642
+ execSync("pkill -TERM -f '(chrome|chromium)'", {
643
643
  timeout: 5000,
644
644
  stdio: ["pipe", "pipe", "pipe"],
645
645
  });
@@ -656,10 +656,15 @@ async function killChromeGracefully(): Promise<void> {
656
656
  // Wait for Chrome to fully exit (up to 8s)
657
657
  const start = Date.now();
658
658
  while (Date.now() - start < 8000) {
659
- if (!isChromeRunning()) return;
659
+ if (!isChromeRunning()) {
660
+ log.debug(`Chrome exited after ${Date.now() - start}ms`);
661
+ return;
662
+ }
660
663
  await new Promise((r) => setTimeout(r, 500));
661
664
  }
662
665
 
666
+ log.debug("Chrome still running after graceful quit, force-killing...");
667
+
663
668
  // Force kill if still alive
664
669
  try {
665
670
  if (os === "win32") {
@@ -667,14 +672,49 @@ async function killChromeGracefully(): Promise<void> {
667
672
  stdio: ["pipe", "pipe", "pipe"],
668
673
  });
669
674
  } else {
670
- execSync("pkill -9 -f chrome", {
675
+ execSync("pkill -9 -f '(chrome|chromium)'", {
671
676
  stdio: ["pipe", "pipe", "pipe"],
672
677
  });
673
678
  }
674
679
  } catch {
675
680
  /* already dead */
676
681
  }
682
+
683
+ // Wait for processes to fully terminate after SIGKILL
677
684
  await new Promise((r) => setTimeout(r, 1000));
685
+
686
+ // On macOS/Linux, remove the Chrome profile SingletonLock that may linger
687
+ // after a force-kill, preventing the next Chrome instance from starting.
688
+ if (os !== "win32") {
689
+ const home = process.env.HOME;
690
+ if (home) {
691
+ const lockPaths =
692
+ os === "darwin"
693
+ ? [
694
+ `${home}/Library/Application Support/Google/Chrome/SingletonLock`,
695
+ `${home}/Library/Application Support/Google/Chrome/SingletonSocket`,
696
+ `${home}/Library/Application Support/Google/Chrome/SingletonCookie`,
697
+ ]
698
+ : [
699
+ `${home}/.config/google-chrome/SingletonLock`,
700
+ `${home}/.config/google-chrome/SingletonSocket`,
701
+ `${home}/.config/google-chrome/SingletonCookie`,
702
+ `${home}/.config/chromium/SingletonLock`,
703
+ `${home}/.config/chromium/SingletonSocket`,
704
+ `${home}/.config/chromium/SingletonCookie`,
705
+ ];
706
+ for (const lockPath of lockPaths) {
707
+ try {
708
+ if (existsSync(lockPath)) {
709
+ unlinkSync(lockPath);
710
+ log.debug(`Removed stale lock: ${lockPath}`);
711
+ }
712
+ } catch {
713
+ /* best effort */
714
+ }
715
+ }
716
+ }
717
+ }
678
718
  }
679
719
 
680
720
  /**
@@ -690,13 +730,32 @@ function spawnChrome(chromePath: string, port: number): ChildProcess {
690
730
  log.debug(`Spawning Chrome: ${chromePath} ${cdpFlag} --restore-last-session`);
691
731
  const child = spawn(chromePath, [cdpFlag, "--restore-last-session"], {
692
732
  detached: true,
693
- stdio: "ignore",
733
+ stdio: ["ignore", "pipe", "pipe"],
734
+ });
735
+
736
+ // Capture stderr for diagnostics — Chrome prints errors here
737
+ let stderr = "";
738
+ child.stderr?.on("data", (chunk: Buffer) => {
739
+ stderr += chunk.toString();
694
740
  });
695
741
 
696
742
  child.on("error", (err) => {
697
743
  log.error(`Chrome spawn error: ${err.message}`);
698
744
  });
699
745
 
746
+ child.on("exit", (code, signal) => {
747
+ if (code !== null && code !== 0) {
748
+ log.debug(`Chrome exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`);
749
+ if (stderr) {
750
+ // Log first few lines of stderr for diagnostics
751
+ const lines = stderr.split("\n").filter(Boolean).slice(0, 5);
752
+ for (const line of lines) {
753
+ log.debug(` chrome stderr: ${line}`);
754
+ }
755
+ }
756
+ }
757
+ });
758
+
700
759
  child.unref();
701
760
  return child;
702
761
  }
@@ -798,44 +857,77 @@ export async function ensureBrowserAvailable(port = 9222): Promise<AutoLaunchRes
798
857
  if (running) {
799
858
  log.debug("Killing Chrome gracefully for restart with CDP...");
800
859
  await killChromeGracefully();
801
- spawnChrome(chromePath, port);
860
+
861
+ // Extra wait for profile lock release after kill
862
+ await new Promise((r) => setTimeout(r, 2000));
863
+
864
+ const child = spawnChrome(chromePath, port);
802
865
 
803
866
  if (await waitForCDP(browser)) {
804
867
  return { success: true, action: "restarted", chromePath };
805
868
  }
806
869
 
870
+ // Check if Chrome is still alive before retrying
871
+ if (child.exitCode !== null) {
872
+ log.debug(
873
+ `Chrome process already exited (code ${child.exitCode}) — may have crashed or profile is locked`
874
+ );
875
+ return {
876
+ success: false,
877
+ action: "launch_failed",
878
+ chromePath,
879
+ detail: `Chrome exited immediately (code ${child.exitCode}). This often means the profile is locked by another Chrome instance. Try closing all Chrome windows first, then run assistme again.`,
880
+ };
881
+ }
882
+
807
883
  // Retry once — Chrome can be slow to start (extensions, session restore)
808
884
  log.debug("First CDP wait timed out after restart, retrying...");
809
885
  if (await waitForCDP(browser, 15000)) {
810
886
  return { success: true, action: "restarted", chromePath };
811
887
  }
812
888
 
889
+ const stillRunning = isChromeRunning();
813
890
  return {
814
891
  success: false,
815
892
  action: "launch_failed",
816
893
  chromePath,
817
- detail: "Chrome was restarted but CDP did not become reachable within timeout.",
894
+ detail: stillRunning
895
+ ? "Chrome is running but CDP port is not responding. Chrome may have started without the --remote-debugging-port flag. Try: 1) Quit Chrome completely, 2) Run assistme again."
896
+ : "Chrome was restarted but exited unexpectedly. Check if Chrome can start normally from the command line.",
818
897
  };
819
898
  }
820
899
 
821
900
  // Case 4: Chrome not running → launch
822
- spawnChrome(chromePath, port);
901
+ const child = spawnChrome(chromePath, port);
823
902
 
824
903
  if (await waitForCDP(browser)) {
825
904
  return { success: true, action: "launched", chromePath };
826
905
  }
827
906
 
907
+ if (child.exitCode !== null) {
908
+ log.debug(`Chrome process already exited (code ${child.exitCode})`);
909
+ return {
910
+ success: false,
911
+ action: "launch_failed",
912
+ chromePath,
913
+ detail: `Chrome exited immediately (code ${child.exitCode}). Try launching Chrome manually to see any error dialogs.`,
914
+ };
915
+ }
916
+
828
917
  // Retry once
829
918
  log.debug("First CDP wait timed out after launch, retrying...");
830
919
  if (await waitForCDP(browser, 15000)) {
831
920
  return { success: true, action: "launched", chromePath };
832
921
  }
833
922
 
923
+ const stillRunning = isChromeRunning();
834
924
  return {
835
925
  success: false,
836
926
  action: "launch_failed",
837
927
  chromePath,
838
- detail: "Chrome was launched but CDP did not become reachable within timeout.",
928
+ detail: stillRunning
929
+ ? "Chrome is running but CDP port is not responding. Try quitting Chrome completely and running assistme again."
930
+ : "Chrome exited unexpectedly after launch.",
839
931
  };
840
932
  }
841
933