@pulso/companion 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.
- package/dist/index.js +215 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -541,6 +541,218 @@ print("\\(x),\\(y)")`;
|
|
|
541
541
|
const [cx, cy] = pos.trim().split(",").map(Number);
|
|
542
542
|
return { success: true, data: { x: cx, y: cy } };
|
|
543
543
|
}
|
|
544
|
+
// ── Browser Automation ─────────────────────────────────
|
|
545
|
+
case "sys_browser_list_tabs": {
|
|
546
|
+
const browsers = ["Google Chrome", "Safari", "Arc", "Firefox", "Microsoft Edge"];
|
|
547
|
+
const allTabs = [];
|
|
548
|
+
for (const browser of browsers) {
|
|
549
|
+
try {
|
|
550
|
+
const running = await runAppleScript(
|
|
551
|
+
`tell application "System Events" to (name of processes) contains "${browser}"`
|
|
552
|
+
);
|
|
553
|
+
if (running.trim() !== "true") continue;
|
|
554
|
+
if (browser === "Safari") {
|
|
555
|
+
const tabData = await runAppleScript(`
|
|
556
|
+
tell application "Safari"
|
|
557
|
+
set tabList to ""
|
|
558
|
+
set activeURL to URL of front document
|
|
559
|
+
repeat with w in windows
|
|
560
|
+
repeat with t in tabs of w
|
|
561
|
+
set tabList to tabList & name of t & "|||" & URL of t & "|||"
|
|
562
|
+
end repeat
|
|
563
|
+
end repeat
|
|
564
|
+
return activeURL & "~~~" & tabList
|
|
565
|
+
end tell`);
|
|
566
|
+
const [activeURL, ...rest] = tabData.split("~~~");
|
|
567
|
+
const tabStr = rest.join("~~~");
|
|
568
|
+
const pairs = tabStr.split("|||").filter(Boolean);
|
|
569
|
+
for (let i = 0; i < pairs.length - 1; i += 2) {
|
|
570
|
+
allTabs.push({ browser: "Safari", title: pairs[i], url: pairs[i + 1], active: pairs[i + 1] === activeURL.trim() });
|
|
571
|
+
}
|
|
572
|
+
} else {
|
|
573
|
+
const tabData = await runAppleScript(`
|
|
574
|
+
tell application "${browser}"
|
|
575
|
+
set tabList to ""
|
|
576
|
+
set activeTabURL to URL of active tab of front window
|
|
577
|
+
repeat with w in windows
|
|
578
|
+
repeat with t in tabs of w
|
|
579
|
+
set tabList to tabList & title of t & "|||" & URL of t & "|||"
|
|
580
|
+
end repeat
|
|
581
|
+
end repeat
|
|
582
|
+
return activeTabURL & "~~~" & tabList
|
|
583
|
+
end tell`);
|
|
584
|
+
const [activeURL, ...rest] = tabData.split("~~~");
|
|
585
|
+
const tabStr = rest.join("~~~");
|
|
586
|
+
const pairs = tabStr.split("|||").filter(Boolean);
|
|
587
|
+
for (let i = 0; i < pairs.length - 1; i += 2) {
|
|
588
|
+
allTabs.push({ browser, title: pairs[i], url: pairs[i + 1], active: pairs[i + 1] === activeURL.trim() });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
} catch {
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return { success: true, data: { tabs: allTabs, count: allTabs.length } };
|
|
595
|
+
}
|
|
596
|
+
case "sys_browser_navigate": {
|
|
597
|
+
const url = params.url;
|
|
598
|
+
const browser = params.browser || "Google Chrome";
|
|
599
|
+
if (!url) return { success: false, error: "Missing URL" };
|
|
600
|
+
try {
|
|
601
|
+
if (browser === "Safari") {
|
|
602
|
+
await runAppleScript(`
|
|
603
|
+
tell application "Safari"
|
|
604
|
+
activate
|
|
605
|
+
if (count of windows) = 0 then make new document
|
|
606
|
+
set URL of front document to "${url.replace(/"/g, '\\"')}"
|
|
607
|
+
end tell`);
|
|
608
|
+
} else {
|
|
609
|
+
await runAppleScript(`
|
|
610
|
+
tell application "${browser.replace(/"/g, '\\"')}"
|
|
611
|
+
activate
|
|
612
|
+
if (count of windows) = 0 then
|
|
613
|
+
make new window
|
|
614
|
+
set URL of active tab of front window to "${url.replace(/"/g, '\\"')}"
|
|
615
|
+
else
|
|
616
|
+
set URL of active tab of front window to "${url.replace(/"/g, '\\"')}"
|
|
617
|
+
end if
|
|
618
|
+
end tell`);
|
|
619
|
+
}
|
|
620
|
+
return { success: true, data: { navigated: url, browser } };
|
|
621
|
+
} catch (err) {
|
|
622
|
+
return { success: false, error: `Failed to navigate: ${err.message}` };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
case "sys_browser_new_tab": {
|
|
626
|
+
const url = params.url;
|
|
627
|
+
const browser = params.browser || "Google Chrome";
|
|
628
|
+
if (!url) return { success: false, error: "Missing URL" };
|
|
629
|
+
try {
|
|
630
|
+
if (browser === "Safari") {
|
|
631
|
+
await runAppleScript(`
|
|
632
|
+
tell application "Safari"
|
|
633
|
+
activate
|
|
634
|
+
tell front window to set current tab to (make new tab with properties {URL:"${url.replace(/"/g, '\\"')}"})
|
|
635
|
+
end tell`);
|
|
636
|
+
} else {
|
|
637
|
+
await runAppleScript(`
|
|
638
|
+
tell application "${browser.replace(/"/g, '\\"')}"
|
|
639
|
+
activate
|
|
640
|
+
tell front window to make new tab with properties {URL:"${url.replace(/"/g, '\\"')}"}
|
|
641
|
+
end tell`);
|
|
642
|
+
}
|
|
643
|
+
return { success: true, data: { opened: url, browser } };
|
|
644
|
+
} catch (err) {
|
|
645
|
+
return { success: false, error: `Failed to open tab: ${err.message}` };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
case "sys_browser_read_page": {
|
|
649
|
+
const browser = params.browser || "Google Chrome";
|
|
650
|
+
const maxLen = Number(params.maxLength) || 8e3;
|
|
651
|
+
let content = "";
|
|
652
|
+
let method = "";
|
|
653
|
+
try {
|
|
654
|
+
if (browser === "Safari") {
|
|
655
|
+
content = await runAppleScript(`
|
|
656
|
+
tell application "Safari"
|
|
657
|
+
set pageText to do JavaScript "document.body.innerText" in front document
|
|
658
|
+
return pageText
|
|
659
|
+
end tell`);
|
|
660
|
+
method = "javascript";
|
|
661
|
+
} else {
|
|
662
|
+
content = await runAppleScript(`
|
|
663
|
+
tell application "${browser.replace(/"/g, '\\"')}"
|
|
664
|
+
set pageText to execute javascript "document.body.innerText" in active tab of front window
|
|
665
|
+
return pageText
|
|
666
|
+
end tell`);
|
|
667
|
+
method = "javascript";
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
try {
|
|
671
|
+
const savedClipboard = await runShell("pbpaste 2>/dev/null || true");
|
|
672
|
+
await runAppleScript(`tell application "${browser.replace(/"/g, '\\"')}" to activate`);
|
|
673
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
674
|
+
await runAppleScript('tell application "System Events" to keystroke "a" using command down');
|
|
675
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
676
|
+
await runAppleScript('tell application "System Events" to keystroke "c" using command down');
|
|
677
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
678
|
+
content = await runShell("pbpaste");
|
|
679
|
+
method = "clipboard";
|
|
680
|
+
await runAppleScript('tell application "System Events" to key code 53');
|
|
681
|
+
if (savedClipboard && savedClipboard !== content) {
|
|
682
|
+
execSync(`echo ${JSON.stringify(savedClipboard)} | pbcopy`);
|
|
683
|
+
}
|
|
684
|
+
} catch (clipErr) {
|
|
685
|
+
return { success: false, error: `Could not read page. Enable 'Allow JavaScript from Apple Events' in ${browser} or grant Accessibility permission. Error: ${clipErr.message}` };
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
let pageUrl = "";
|
|
689
|
+
let pageTitle = "";
|
|
690
|
+
try {
|
|
691
|
+
if (browser === "Safari") {
|
|
692
|
+
pageUrl = await runAppleScript('tell application "Safari" to return URL of front document');
|
|
693
|
+
pageTitle = await runAppleScript('tell application "Safari" to return name of front document');
|
|
694
|
+
} else {
|
|
695
|
+
pageUrl = await runAppleScript(`tell application "${browser.replace(/"/g, '\\"')}" to return URL of active tab of front window`);
|
|
696
|
+
pageTitle = await runAppleScript(`tell application "${browser.replace(/"/g, '\\"')}" to return title of active tab of front window`);
|
|
697
|
+
}
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
return {
|
|
701
|
+
success: true,
|
|
702
|
+
data: {
|
|
703
|
+
title: pageTitle.trim(),
|
|
704
|
+
url: pageUrl.trim(),
|
|
705
|
+
content: content.slice(0, maxLen),
|
|
706
|
+
length: content.length,
|
|
707
|
+
truncated: content.length > maxLen,
|
|
708
|
+
method
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
case "sys_browser_execute_js": {
|
|
713
|
+
const js = params.code;
|
|
714
|
+
const browser = params.browser || "Google Chrome";
|
|
715
|
+
if (!js) return { success: false, error: "Missing JavaScript code" };
|
|
716
|
+
try {
|
|
717
|
+
let result;
|
|
718
|
+
if (browser === "Safari") {
|
|
719
|
+
result = await runAppleScript(`tell application "Safari" to do JavaScript ${JSON.stringify(js)} in front document`);
|
|
720
|
+
} else {
|
|
721
|
+
result = await runAppleScript(`tell application "${browser.replace(/"/g, '\\"')}" to execute javascript ${JSON.stringify(js)} in active tab of front window`);
|
|
722
|
+
}
|
|
723
|
+
return { success: true, data: { result: result.slice(0, 5e3) } };
|
|
724
|
+
} catch (err) {
|
|
725
|
+
return { success: false, error: `JS execution failed (enable 'Allow JavaScript from Apple Events' in browser): ${err.message}` };
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
// ── Email ──────────────────────────────────────────────
|
|
729
|
+
case "sys_email_send": {
|
|
730
|
+
const to = params.to;
|
|
731
|
+
const subject = params.subject;
|
|
732
|
+
const body = params.body;
|
|
733
|
+
const method = params.method || "mail";
|
|
734
|
+
if (!to || !subject || !body) return { success: false, error: "Missing to, subject, or body" };
|
|
735
|
+
if (method === "gmail") {
|
|
736
|
+
const gmailUrl = `https://mail.google.com/mail/u/0/?view=cm&fs=1&to=${encodeURIComponent(to)}&su=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
|
737
|
+
await runShell(`open "${gmailUrl}"`);
|
|
738
|
+
return { success: true, data: { method: "gmail", to, subject, note: "Gmail compose opened. User needs to click Send." } };
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
await runAppleScript(`
|
|
742
|
+
tell application "Mail"
|
|
743
|
+
set newMessage to make new outgoing message with properties {subject:"${subject.replace(/"/g, '\\"')}", content:"${body.replace(/"/g, '\\"')}"}
|
|
744
|
+
tell newMessage
|
|
745
|
+
make new to recipient at end of to recipients with properties {address:"${to.replace(/"/g, '\\"')}"}
|
|
746
|
+
end tell
|
|
747
|
+
send newMessage
|
|
748
|
+
end tell`);
|
|
749
|
+
return { success: true, data: { method: "mail", to, subject, sent: true } };
|
|
750
|
+
} catch (err) {
|
|
751
|
+
const gmailUrl = `https://mail.google.com/mail/u/0/?view=cm&fs=1&to=${encodeURIComponent(to)}&su=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
|
752
|
+
await runShell(`open "${gmailUrl}"`);
|
|
753
|
+
return { success: true, data: { method: "gmail_fallback", to, subject, note: `Mail.app failed (${err.message}). Gmail compose opened instead.` } };
|
|
754
|
+
}
|
|
755
|
+
}
|
|
544
756
|
case "sys_run_shortcut": {
|
|
545
757
|
const name = params.name;
|
|
546
758
|
const input = params.input;
|
|
@@ -575,6 +787,8 @@ function connect() {
|
|
|
575
787
|
console.log(" \u2022 Clipboard access");
|
|
576
788
|
console.log(" \u2022 Screenshots");
|
|
577
789
|
console.log(" \u2022 Text-to-speech");
|
|
790
|
+
console.log(" \u2022 Browser automation (tabs, navigate, read pages)");
|
|
791
|
+
console.log(" \u2022 Email composition (Mail.app / Gmail)");
|
|
578
792
|
console.log(" \u2022 Terminal commands");
|
|
579
793
|
console.log(" \u2022 System notifications");
|
|
580
794
|
console.log(" \u2022 macOS Shortcuts");
|
|
@@ -635,7 +849,7 @@ function scheduleReconnect() {
|
|
|
635
849
|
}
|
|
636
850
|
console.log("");
|
|
637
851
|
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
638
|
-
console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.
|
|
852
|
+
console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.8 \u2551");
|
|
639
853
|
console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
640
854
|
console.log("");
|
|
641
855
|
connect();
|