@pulso/companion 0.1.3 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +62 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -45,6 +45,54 @@ function runShell(cmd, timeout = 1e4) {
45
45
  });
46
46
  });
47
47
  }
48
+ var spotifyToken = null;
49
+ var spotifyTokenExpiry = 0;
50
+ async function getSpotifyToken() {
51
+ if (spotifyToken && Date.now() < spotifyTokenExpiry) return spotifyToken;
52
+ try {
53
+ const res = await fetch("https://open.spotify.com/embed/track/4u7EnebtmKWzUH433cf5Qv", {
54
+ headers: { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" }
55
+ });
56
+ const html = await res.text();
57
+ const match = html.match(/"accessToken":"([^"]+)"/);
58
+ if (!match) return null;
59
+ spotifyToken = match[1];
60
+ spotifyTokenExpiry = Date.now() + 55 * 60 * 1e3;
61
+ return spotifyToken;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ async function spotifySearch(query) {
67
+ const token = await getSpotifyToken();
68
+ if (!token) return null;
69
+ try {
70
+ const res = await fetch(
71
+ `https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track&limit=1`,
72
+ { headers: { Authorization: `Bearer ${token}` } }
73
+ );
74
+ if (!res.ok) {
75
+ if (res.status === 401) {
76
+ spotifyToken = null;
77
+ spotifyTokenExpiry = 0;
78
+ const newToken = await getSpotifyToken();
79
+ if (!newToken) return null;
80
+ const retry = await fetch(
81
+ `https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track&limit=1`,
82
+ { headers: { Authorization: `Bearer ${newToken}` } }
83
+ );
84
+ if (!retry.ok) return null;
85
+ const retryData = await retry.json();
86
+ return retryData.tracks?.items?.[0]?.uri ?? null;
87
+ }
88
+ return null;
89
+ }
90
+ const data = await res.json();
91
+ return data.tracks?.items?.[0]?.uri ?? null;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
48
96
  async function handleCommand(command, params) {
49
97
  try {
50
98
  switch (command) {
@@ -112,25 +160,20 @@ async function handleCommand(command, params) {
112
160
  case "search_play": {
113
161
  const query = params.query;
114
162
  if (!query) return { success: false, error: "Missing search query" };
115
- await runShell(`open "spotify:search:${encodeURIComponent(query)}"`);
116
- await new Promise((r) => setTimeout(r, 2e3));
117
- await runAppleScript(`
118
- tell application "Spotify" to activate
119
- delay 0.5
120
- tell application "System Events"
121
- tell process "Spotify"
122
- key code 36
123
- end tell
124
- end tell
125
- `);
126
- await new Promise((r) => setTimeout(r, 1500));
127
- try {
128
- const track = await runAppleScript('tell application "Spotify" to name of current track');
129
- const artist = await runAppleScript('tell application "Spotify" to artist of current track');
130
- return { success: true, data: { searched: query, nowPlaying: `${track} - ${artist}` } };
131
- } catch {
132
- return { success: true, data: { searched: query, note: "Search opened and play triggered" } };
163
+ const trackUri = await spotifySearch(query);
164
+ if (trackUri) {
165
+ await runAppleScript(`tell application "Spotify" to play track "${trackUri}"`);
166
+ await new Promise((r) => setTimeout(r, 1500));
167
+ try {
168
+ const track = await runAppleScript('tell application "Spotify" to name of current track');
169
+ const artist = await runAppleScript('tell application "Spotify" to artist of current track');
170
+ return { success: true, data: { searched: query, nowPlaying: `${track} - ${artist}`, state: "playing" } };
171
+ } catch {
172
+ return { success: true, data: { searched: query, note: "Playing track" } };
173
+ }
133
174
  }
175
+ await runShell(`open "spotify:search:${encodeURIComponent(query)}"`);
176
+ return { success: true, data: { searched: query, note: "Opened Spotify search (API unavailable). Select a song to play." } };
134
177
  }
135
178
  case "volume": {
136
179
  const level = params.level;
@@ -291,7 +334,7 @@ function scheduleReconnect() {
291
334
  }
292
335
  console.log("");
293
336
  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");
294
- console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.3 \u2551");
337
+ console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.4 \u2551");
295
338
  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");
296
339
  console.log("");
297
340
  connect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulso/companion",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Pulso Companion — gives your AI agent real control over your computer",
6
6
  "bin": {