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 +81 -13
- package/package.json +1 -1
- package/src/tools/browser.ts +101 -9
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())
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1652
|
+
unlinkSync2(zipPath);
|
|
1585
1653
|
} catch {
|
|
1586
|
-
|
|
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
|
-
|
|
1725
|
+
unlinkSync2(skill.filePath);
|
|
1658
1726
|
}
|
|
1659
1727
|
this.skills.delete(name);
|
|
1660
1728
|
return true;
|
package/package.json
CHANGED
package/src/tools/browser.ts
CHANGED
|
@@ -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())
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|