claude-music 1.1.0 → 1.1.1
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/__tests__/shutdown.test.js +35 -0
- package/dist/server.js +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { installShutdownHooks } from "../server.js";
|
|
4
|
+
describe("installShutdownHooks", () => {
|
|
5
|
+
it("stops the daemon when the stdio pipe closes (Claude Code exits)", () => {
|
|
6
|
+
const stop = vi.fn();
|
|
7
|
+
const exit = vi.fn();
|
|
8
|
+
const stdin = new EventEmitter();
|
|
9
|
+
installShutdownHooks(stop, { stdin, exit });
|
|
10
|
+
expect(stop).not.toHaveBeenCalled();
|
|
11
|
+
// Parent closed the pipe → stdin hits EOF.
|
|
12
|
+
stdin.emit("end");
|
|
13
|
+
expect(stop).toHaveBeenCalledOnce();
|
|
14
|
+
expect(exit).toHaveBeenCalledWith(0);
|
|
15
|
+
});
|
|
16
|
+
it("stops at most once even if several signals arrive", () => {
|
|
17
|
+
const stop = vi.fn();
|
|
18
|
+
const exit = vi.fn();
|
|
19
|
+
const stdin = new EventEmitter();
|
|
20
|
+
installShutdownHooks(stop, { stdin, exit });
|
|
21
|
+
stdin.emit("end");
|
|
22
|
+
stdin.emit("close");
|
|
23
|
+
expect(stop).toHaveBeenCalledOnce();
|
|
24
|
+
});
|
|
25
|
+
it("survives a throwing stop() without rethrowing", () => {
|
|
26
|
+
const stop = vi.fn(() => {
|
|
27
|
+
throw new Error("daemon already gone");
|
|
28
|
+
});
|
|
29
|
+
const exit = vi.fn();
|
|
30
|
+
const stdin = new EventEmitter();
|
|
31
|
+
installShutdownHooks(stop, { stdin, exit });
|
|
32
|
+
expect(() => stdin.emit("end")).not.toThrow();
|
|
33
|
+
expect(exit).toHaveBeenCalledWith(0);
|
|
34
|
+
});
|
|
35
|
+
});
|
package/dist/server.js
CHANGED
|
@@ -96,6 +96,41 @@ export function stopDaemon() {
|
|
|
96
96
|
}
|
|
97
97
|
fs.rmSync(SOCKET_PATH, { force: true });
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Tie the daemon's lifetime to this MCP server process. The daemon is spawned
|
|
101
|
+
* detached so it survives across tool calls, which also means nothing stops it
|
|
102
|
+
* when Claude Code shuts us down. We bridge that gap here: when the host closes
|
|
103
|
+
* our stdio pipe (the normal "Claude Code exited" path — stdin hits EOF) or
|
|
104
|
+
* sends us a termination signal, stop the daemon so the music doesn't outlive
|
|
105
|
+
* the session.
|
|
106
|
+
*/
|
|
107
|
+
export function installShutdownHooks(stop, opts = {}) {
|
|
108
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
109
|
+
const exit = opts.exit ?? ((code) => process.exit(code));
|
|
110
|
+
let done = false;
|
|
111
|
+
const cleanup = (shouldExit) => {
|
|
112
|
+
if (done)
|
|
113
|
+
return;
|
|
114
|
+
done = true;
|
|
115
|
+
try {
|
|
116
|
+
stop();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
/* best effort — we're on the way out regardless */
|
|
120
|
+
}
|
|
121
|
+
if (shouldExit)
|
|
122
|
+
exit(0);
|
|
123
|
+
};
|
|
124
|
+
// Parent (Claude Code) closed the stdio pipe → EOF reaches us as 'end'/'close'.
|
|
125
|
+
stdin.on("end", () => cleanup(true));
|
|
126
|
+
stdin.on("close", () => cleanup(true));
|
|
127
|
+
// Host asked us to terminate.
|
|
128
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
129
|
+
process.on(sig, () => cleanup(true));
|
|
130
|
+
}
|
|
131
|
+
// Last-ditch sync safety net for any other exit path.
|
|
132
|
+
process.on("exit", () => cleanup(false));
|
|
133
|
+
}
|
|
99
134
|
/** Build and connect the MCP stdio server. Invoked by `claude-music mcp`. */
|
|
100
135
|
export async function runServer() {
|
|
101
136
|
const server = new McpServer({ name: "mrt2-mcp", version: "1.0.0" });
|
|
@@ -142,4 +177,6 @@ export async function runServer() {
|
|
|
142
177
|
});
|
|
143
178
|
const transport = new StdioServerTransport();
|
|
144
179
|
await server.connect(transport);
|
|
180
|
+
// connect() puts stdin in flowing mode, so 'end' fires reliably on EOF.
|
|
181
|
+
installShutdownHooks(stopDaemon);
|
|
145
182
|
}
|
package/package.json
CHANGED