@pulso/companion 0.1.1 → 0.1.3

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 +54 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -113,7 +113,44 @@ async function handleCommand(command, params) {
113
113
  const query = params.query;
114
114
  if (!query) return { success: false, error: "Missing search query" };
115
115
  await runShell(`open "spotify:search:${encodeURIComponent(query)}"`);
116
- return { success: true, data: { searched: query, note: "Opened Spotify search. Play the first result manually or use 'play' after." } };
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" } };
133
+ }
134
+ }
135
+ case "volume": {
136
+ const level = params.level;
137
+ if (level === void 0 || level < 0 || level > 100) return { success: false, error: "Volume must be 0-100" };
138
+ await runAppleScript(`tell application "Spotify" to set sound volume to ${level}`);
139
+ return { success: true, data: { volume: level } };
140
+ }
141
+ case "shuffle": {
142
+ const enabled = params.enabled;
143
+ await runAppleScript(`tell application "Spotify" to set shuffling to ${enabled ? "true" : "false"}`);
144
+ return { success: true, data: { shuffling: enabled } };
145
+ }
146
+ case "repeat": {
147
+ const mode = params.mode;
148
+ if (!mode) return { success: false, error: "Missing mode (off, context, track)" };
149
+ const repeatMap = { off: "off", context: "context", track: "track" };
150
+ const val = repeatMap[mode];
151
+ if (!val) return { success: false, error: "Mode must be: off, context, or track" };
152
+ await runAppleScript(`tell application "Spotify" to set repeating to ${val !== "off"}`);
153
+ return { success: true, data: { repeating: mode } };
117
154
  }
118
155
  default:
119
156
  return { success: false, error: `Unknown Spotify action: ${action}` };
@@ -177,6 +214,8 @@ async function handleCommand(command, params) {
177
214
  }
178
215
  var ws = null;
179
216
  var reconnectTimer = null;
217
+ var heartbeatTimer = null;
218
+ var HEARTBEAT_INTERVAL = 3e4;
180
219
  function connect() {
181
220
  console.log("\u{1F50C} Connecting to Pulso...");
182
221
  console.log(` ${WS_URL.replace(/token=.*/, "token=***")}`);
@@ -199,6 +238,12 @@ function connect() {
199
238
  console.log(` Access: ${ACCESS_LEVEL === "full" ? "\u{1F513} Full (unrestricted)" : "\u{1F512} Sandboxed (safe dirs only)"}`);
200
239
  console.log(" Waiting for commands from Pulso agent...");
201
240
  ws.send(JSON.stringify({ type: "extension_ready" }));
241
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
242
+ heartbeatTimer = setInterval(() => {
243
+ if (ws && ws.readyState === WebSocket.OPEN) {
244
+ ws.send(JSON.stringify({ type: "ping" }));
245
+ }
246
+ }, HEARTBEAT_INTERVAL);
202
247
  });
203
248
  ws.on("message", async (raw) => {
204
249
  try {
@@ -207,6 +252,9 @@ function connect() {
207
252
  console.log(` \u2713 Server: ${msg.message}`);
208
253
  return;
209
254
  }
255
+ if (msg.type === "pong") {
256
+ return;
257
+ }
210
258
  if (msg.id && msg.command) {
211
259
  console.log(`
212
260
  \u26A1 Command: ${msg.command}`, msg.params ? JSON.stringify(msg.params).slice(0, 200) : "");
@@ -223,6 +271,10 @@ function connect() {
223
271
  ws.on("close", (code, reason) => {
224
272
  console.log(`
225
273
  \u{1F50C} Disconnected (${code}: ${reason.toString() || "unknown"})`);
274
+ if (heartbeatTimer) {
275
+ clearInterval(heartbeatTimer);
276
+ heartbeatTimer = null;
277
+ }
226
278
  scheduleReconnect();
227
279
  });
228
280
  ws.on("error", (err) => {
@@ -239,7 +291,7 @@ function scheduleReconnect() {
239
291
  }
240
292
  console.log("");
241
293
  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");
242
- console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.0 \u2551");
294
+ console.log(" \u2551 \u{1FAC0} Pulso Mac Companion v0.1.3 \u2551");
243
295
  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");
244
296
  console.log("");
245
297
  connect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulso/companion",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Pulso Companion — gives your AI agent real control over your computer",
6
6
  "bin": {