@zonease/happy 0.6.42 → 0.6.43

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 (88) hide show
  1. package/dist/{api-CIE4_cWd.cjs → api-B-PlVQY6.cjs} +49 -3
  2. package/dist/{api-Ew6xr81i.mjs → api-BUCtd3cB.mjs} +49 -3
  3. package/dist/{auth-G9fZpVJ2.cjs → auth-BqGkUXvq.cjs} +2 -2
  4. package/dist/{auth-BzYevJo2.mjs → auth-DQH6q_q-.mjs} +4 -4
  5. package/dist/{auth-Dor79zTL.cjs → auth-OL3nNcDX.cjs} +4 -4
  6. package/dist/{auth-Q2STUNI-.mjs → auth-qM2eojWx.mjs} +2 -2
  7. package/dist/{browser-qDIIAL7S.cjs → browser-CY7VNU0C.cjs} +1 -1
  8. package/dist/{browser-BI7s-V8E.mjs → browser-D5RN_Axa.mjs} +1 -1
  9. package/dist/{claudeLocal-Db2tDC4X.mjs → claudeLocal-BXK_vQXS.mjs} +1 -1
  10. package/dist/{claudeLocal-f4uBLJaM.cjs → claudeLocal-HAXk0deK.cjs} +1 -1
  11. package/dist/{config-CHjDs8AS.mjs → config-2wMjzcHl.mjs} +1 -1
  12. package/dist/{config-CSBlEik9.cjs → config-CaKxWGVe.cjs} +1 -1
  13. package/dist/{connect-O_nA5BOC.mjs → connect-BAunPyp_.mjs} +4 -4
  14. package/dist/{connect-CJoTliii.cjs → connect-Dt1vY1BC.cjs} +4 -4
  15. package/dist/{controlClient-DoS9JqQC.mjs → controlClient-BzzTNSfl.mjs} +1 -1
  16. package/dist/{controlClient-CEz_p1zc.cjs → controlClient-yTkpwcOq.cjs} +1 -1
  17. package/dist/{cursorApprovalHook-DRAHfTXU.cjs → cursorApprovalHook-B2chnVni.cjs} +29 -11
  18. package/dist/{cursorApprovalHook-B0v1kGa7.mjs → cursorApprovalHook-BDLPzI5l.mjs} +29 -11
  19. package/dist/{cursorApprovalServer-JjPkdjGR.mjs → cursorApprovalServer-Ba1yOUCA.mjs} +109 -10
  20. package/dist/{cursorApprovalServer-DzY8yJlI.cjs → cursorApprovalServer-xwxVGL3u.cjs} +109 -8
  21. package/dist/{deviceKeys-B4aqjnE6.mjs → deviceKeys-CQyFFPtt.mjs} +2 -2
  22. package/dist/{deviceKeys-DyUn0bEx.cjs → deviceKeys-CdW-HPnz.cjs} +2 -2
  23. package/dist/{doctor-BJSuVRUZ.cjs → doctor-D4xWXiE7.cjs} +2 -2
  24. package/dist/{doctor-DRnft_5-.mjs → doctor-DDT2TXHH.mjs} +2 -2
  25. package/dist/{downloadImage-Cijmufpr.cjs → downloadImage-3A6zY5AX.cjs} +1 -372
  26. package/dist/downloadImage-BqNp0l7y.mjs +485 -0
  27. package/dist/{enroll-CysDL6ee.mjs → enroll-BlZ0giJI.mjs} +3 -3
  28. package/dist/{enroll-C2gunHdt.cjs → enroll-DeYs89Ic.cjs} +3 -3
  29. package/dist/{evidence-DsQ-h-_W.cjs → evidence-Bn1JXtdz.cjs} +1 -1
  30. package/dist/{evidence-DPutCuUr.mjs → evidence-PUnN9yPK.mjs} +1 -1
  31. package/dist/{formatImageMessage-6aV9YFWr.mjs → formatImageMessage-BRHeLf0-.mjs} +1 -1
  32. package/dist/{formatImageMessage-BPK_Ivfe.cjs → formatImageMessage-bVFGFDJl.cjs} +1 -1
  33. package/dist/index.cjs +50 -46
  34. package/dist/index.mjs +50 -46
  35. package/dist/{install-BsTA2tZn.mjs → install-BLPNuSeM.mjs} +2 -2
  36. package/dist/{install-DBg0xQ3g.mjs → install-BOIcR_Wr.mjs} +1 -1
  37. package/dist/{install-Tyhs6rUp.cjs → install-Jrs6YUu8.cjs} +1 -1
  38. package/dist/{install-DlOppA-e.cjs → install-Q-YMwwwg.cjs} +2 -2
  39. package/dist/lib.cjs +3 -3
  40. package/dist/lib.d.cts +1 -0
  41. package/dist/lib.d.mts +1 -0
  42. package/dist/lib.mjs +3 -3
  43. package/dist/{loadableToolOutput-DPGTtKJM.cjs → loadableToolOutput-B3qpMqXT.cjs} +2 -2
  44. package/dist/{loadableToolOutput-2dMPAp4N.mjs → loadableToolOutput-DzReWITb.mjs} +2 -2
  45. package/dist/{persistence-BcQmjobZ.cjs → persistence-D7zNFI-d.cjs} +1 -1
  46. package/dist/{persistence-D9tMm6bj.mjs → persistence-DbLMvLvk.mjs} +1 -1
  47. package/dist/{push-Cnm6JQMD.cjs → push-RT6Y0Hr3.cjs} +6 -6
  48. package/dist/{push-Dxsugkes.mjs → push-z5wQ4Ki_.mjs} +6 -6
  49. package/dist/registerKillSessionHandler-DmfnJ0zq.mjs +407 -0
  50. package/dist/registerKillSessionHandler-MoLdSQxM.cjs +411 -0
  51. package/dist/{run-BEflQaQ2.mjs → run-BFnSUK1o.mjs} +10 -8
  52. package/dist/{run-CiPWWhXt.cjs → run-C5StYRIB.cjs} +10 -8
  53. package/dist/{runClaude-BVXsyCHb.cjs → runClaude-ChRp6f5G.cjs} +26 -26
  54. package/dist/{runClaude-sPkSqHWY.mjs → runClaude-CjENBdrX.mjs} +21 -21
  55. package/dist/{runCodex-C4rHkIyw.mjs → runCodex-BzQWlRoj.mjs} +26 -26
  56. package/dist/{runCodex-BAwhdmSA.cjs → runCodex-DcQGyotH.cjs} +28 -28
  57. package/dist/{runCursor-DiHLWMs8.mjs → runCursor-B73z6MCN.mjs} +210 -34
  58. package/dist/{runCursor-CD5wC3gA.cjs → runCursor-BlPAxaTy.cjs} +210 -34
  59. package/dist/{runGemini-B-93gOqB.mjs → runGemini-BPv4UzuF.mjs} +17 -17
  60. package/dist/{runGemini-Cto-adir.cjs → runGemini-JFqaIC3u.cjs} +19 -19
  61. package/dist/{runOneShot-BfIzE9W4.cjs → runOneShot-2iV11e9L.cjs} +2 -2
  62. package/dist/{runOneShot-DGjlYVhR.mjs → runOneShot-DTukim2A.mjs} +2 -2
  63. package/dist/{serve-CqL6NWDr.cjs → serve-BDwuAIK4.cjs} +6 -6
  64. package/dist/{serve-ttlqF5jn.mjs → serve-BnlYj5ek.mjs} +6 -6
  65. package/dist/{session-BZhGpQUJ.cjs → session-Cp9LEESM.cjs} +5 -5
  66. package/dist/{session-Co0REU9e.mjs → session-gfeFU8CA.mjs} +5 -5
  67. package/dist/{sessionKeyCache-BwFUGgMs.cjs → sessionKeyCache-3Pme04yL.cjs} +1 -1
  68. package/dist/{sessionKeyCache-pXTjpETo.mjs → sessionKeyCache-DtBS_Sb3.mjs} +1 -1
  69. package/dist/{sessionList-DaufR-sA.mjs → sessionList-B1w4BUdv.mjs} +2 -2
  70. package/dist/{sessionList-fG5uAPzU.cjs → sessionList-BR_5UDZJ.cjs} +2 -2
  71. package/dist/{sessionSummary-CjZPt6nr.cjs → sessionSummary-DLweYgHO.cjs} +3 -3
  72. package/dist/{sessionSummary-Dv6dPABg.mjs → sessionSummary-E-a2bg8W.mjs} +3 -3
  73. package/dist/{setupOfflineReconnection-DW5il97B.mjs → setupOfflineReconnection-DHwthFtd.mjs} +5 -3
  74. package/dist/{setupOfflineReconnection-B6coH9QN.cjs → setupOfflineReconnection-Dkp00dC9.cjs} +7 -5
  75. package/dist/{spawnHappyCLI-Cfu7C1uM.cjs → spawnHappyCLI-C2uY9WV-.cjs} +1 -1
  76. package/dist/{spawnHappyCLI-DhUeycct.mjs → spawnHappyCLI-Dt7fbmbk.mjs} +1 -1
  77. package/dist/{ssh-8tPIKk-i.mjs → ssh-C-nEZiGP.mjs} +5 -5
  78. package/dist/{ssh-C4YTSHXL.cjs → ssh-CsnS89Lr.cjs} +5 -5
  79. package/dist/{task-CH-oidFy.mjs → task-B-8_QGGX.mjs} +6 -6
  80. package/dist/{task-DFW-PecP.cjs → task-lrLb4FCL.cjs} +6 -6
  81. package/dist/{uninstall-CeioRZNI.mjs → uninstall-CirMt_WC.mjs} +2 -2
  82. package/dist/{uninstall-yTSL3X6Y.cjs → uninstall-DMjUlEXT.cjs} +2 -2
  83. package/dist/{update-Derd2hoX.mjs → update-CfXbYshs.mjs} +3 -3
  84. package/dist/{update-Bju28vNP.cjs → update-mx2p28IP.cjs} +3 -3
  85. package/package.json +1 -1
  86. package/dist/downloadImage-BpVF7A79.mjs +0 -854
  87. package/dist/gitWorktree-C6jLoaMw.cjs +0 -39
  88. package/dist/gitWorktree-CzJDU2BV.mjs +0 -37
@@ -0,0 +1,411 @@
1
+ 'use strict';
2
+
3
+ var path = require('node:path');
4
+ var projectPath = require('./projectPath-ChLhdOyt.cjs');
5
+ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
6
+ var node_http = require('node:http');
7
+ var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
8
+ var types_js = require('@modelcontextprotocol/sdk/types.js');
9
+ var z = require('zod');
10
+ var logger = require('./persistence-D7zNFI-d.cjs');
11
+ var deviceKeys = require('./deviceKeys-CdW-HPnz.cjs');
12
+ var node_crypto = require('node:crypto');
13
+ var prompt = require('./prompt-BTWDZZO0.cjs');
14
+ require('./mcpToolSchemas-DK42yoZ_.cjs');
15
+ require('./index-BeKrqPYR.cjs');
16
+ var node_child_process = require('node:child_process');
17
+ require('axios');
18
+ require('node:events');
19
+ require('socket.io-client');
20
+ require('./api-B-PlVQY6.cjs');
21
+ require('tweetnacl');
22
+ require('ws');
23
+ require('crypto');
24
+ require('@stablelib/base64');
25
+ require('node:os');
26
+ require('node:fs/promises');
27
+ require('node:fs');
28
+ require('node:readline');
29
+ require('expo-server-sdk');
30
+ require('chalk');
31
+ require('./types-DWeB7aqh.cjs');
32
+
33
+ function createMcpServer(client, options) {
34
+ const mcp = new mcp_js.McpServer({
35
+ name: "Happy MCP",
36
+ version: "1.0.0"
37
+ });
38
+ const handler = async (title) => {
39
+ logger.logger.debug("[happyMCP] Changing title to:", title);
40
+ if (client.isTitlePinned) {
41
+ return { success: false, error: "Title is pinned by user and cannot be changed automatically" };
42
+ }
43
+ try {
44
+ client.sendClaudeSessionMessage({
45
+ type: "summary",
46
+ summary: title,
47
+ leafUuid: node_crypto.randomUUID()
48
+ });
49
+ return { success: true };
50
+ } catch (error) {
51
+ return { success: false, error: String(error) };
52
+ }
53
+ };
54
+ mcp.registerTool("device_list", {
55
+ description: "List enrolled Happy devices (servers and computers) with their online state. Use before device_exec when you need a device id.",
56
+ title: "List Devices",
57
+ inputSchema: {}
58
+ }, async () => {
59
+ const credentials = await logger.readCredentials();
60
+ if (!credentials) {
61
+ return { content: [{ type: "text", text: "No Happy credentials on this machine." }] };
62
+ }
63
+ try {
64
+ const devices = await deviceKeys.listDevices(credentials);
65
+ const target = client.targetDevices.map((device) => device.id);
66
+ const lines = devices.map((device) => {
67
+ const marks = [device.active ? "online" : "offline"];
68
+ if (target.includes(device.id)) marks.push("session default");
69
+ return `${device.id} ${device.name}${device.platform ? ` (${device.platform})` : ""} [${marks.join(", ")}]${device.description ? ` \u2014 ${device.description}` : ""}`;
70
+ });
71
+ return { content: [{ type: "text", text: lines.length ? lines.join("\n") : "No devices enrolled yet." }] };
72
+ } catch (error) {
73
+ return { content: [{ type: "text", text: `Failed to list devices: ${error instanceof Error ? error.message : String(error)}` }] };
74
+ }
75
+ });
76
+ mcp.registerTool("device_exec", {
77
+ description: "Run a shell command on an enrolled device (a server or computer registered in Happy). Omit deviceId to use the device the user selected for this session. Returns stdout, stderr and exit code.",
78
+ title: "Run Command on Device",
79
+ inputSchema: {
80
+ command: z.z.string().describe("Shell command to run on the device"),
81
+ deviceId: z.z.string().optional().describe("Target device id. Defaults to the session's selected device."),
82
+ cwd: z.z.string().optional().describe("Working directory on the device"),
83
+ timeout: z.z.number().optional().describe("Timeout in milliseconds (default 60000)")
84
+ }
85
+ }, async (args) => {
86
+ const credentials = await logger.readCredentials();
87
+ if (!credentials) {
88
+ return { content: [{ type: "text", text: "No Happy credentials on this machine." }] };
89
+ }
90
+ const targets = client.targetDevices;
91
+ const deviceId = args.deviceId || (targets.length === 1 ? targets[0].id : null);
92
+ if (!deviceId && targets.length > 1) {
93
+ return { content: [{ type: "text", text: `This session targets several devices \u2014 pass deviceId: ${targets.map((device) => `${device.name} (${device.id})`).join(", ")}` }] };
94
+ }
95
+ if (!deviceId) {
96
+ return { content: [{ type: "text", text: "No device selected for this session. Pick one in the app or pass deviceId (see device_list)." }] };
97
+ }
98
+ try {
99
+ const result = await deviceKeys.deviceExec(client.rpcSocket, credentials, deviceId, args.command, {
100
+ cwd: args.cwd,
101
+ timeout: args.timeout,
102
+ deviceKeyBase64: targets.find((device) => device.id === deviceId)?.key ?? null
103
+ });
104
+ const parts = [`exit code: ${result.exitCode}`];
105
+ if (result.stdout) parts.push(`stdout:
106
+ ${result.stdout}`);
107
+ if (result.stderr) parts.push(`stderr:
108
+ ${result.stderr}`);
109
+ return { content: [{ type: "text", text: parts.join("\n\n") }] };
110
+ } catch (error) {
111
+ return { content: [{ type: "text", text: `Device command failed: ${error instanceof Error ? error.message : String(error)}` }] };
112
+ }
113
+ });
114
+ mcp.registerTool("change_title", {
115
+ description: "Change the title of the current chat session",
116
+ title: "Change Chat Title",
117
+ inputSchema: {
118
+ title: z.z.string().describe("The new title for the chat session")
119
+ }
120
+ }, async (args) => {
121
+ const response = await handler(args.title);
122
+ logger.logger.debug("[happyMCP] Response:", response);
123
+ if (response.success) {
124
+ return {
125
+ content: [
126
+ {
127
+ type: "text",
128
+ text: `Successfully changed chat title to: "${args.title}"`
129
+ }
130
+ ],
131
+ isError: false
132
+ };
133
+ } else {
134
+ return {
135
+ content: [
136
+ {
137
+ type: "text",
138
+ text: `Failed to change chat title: ${response.error || "Unknown error"}`
139
+ }
140
+ ],
141
+ isError: true
142
+ };
143
+ }
144
+ });
145
+ mcp.registerTool("preview_html", {
146
+ description: "Preview an HTML page in the client app. The HTML must be a complete, self-contained document with all CSS and JS inlined.",
147
+ title: "Preview HTML",
148
+ inputSchema: {
149
+ html: z.z.string().describe("Complete self-contained HTML document string"),
150
+ title: z.z.string().optional().describe("Display title for the preview")
151
+ }
152
+ }, async (args) => {
153
+ logger.logger.debug("[happyMCP] Preview HTML:", args.title || "Untitled");
154
+ return {
155
+ content: [{
156
+ type: "text",
157
+ text: `HTML preview ready: ${args.title || "Untitled"}`
158
+ }],
159
+ isError: false
160
+ };
161
+ });
162
+ return mcp;
163
+ }
164
+ async function startHappyServer(client) {
165
+ const transports = /* @__PURE__ */ new Map();
166
+ const enableOrchestratorTools = prompt.shouldEnableOrchestratorTools();
167
+ const toolNames = ["change_title", "preview_html", "device_list", "device_exec"];
168
+ const originalConsoleError = console.error;
169
+ console.error = (...args) => {
170
+ logger.logger.debug("[happyMCP] console.error:", ...args);
171
+ originalConsoleError.apply(console, args);
172
+ };
173
+ const server = node_http.createServer(async (req, res) => {
174
+ const sessionId = req.headers["mcp-session-id"];
175
+ logger.logger.debug("[happyMCP] Received request:", req.method, req.url, "sessionId:", sessionId);
176
+ try {
177
+ if (req.method === "POST") {
178
+ const body = await readRequestBody(req);
179
+ const parsedBody = JSON.parse(body);
180
+ let transport;
181
+ if (sessionId && transports.has(sessionId)) {
182
+ transport = transports.get(sessionId);
183
+ logger.logger.debug("[happyMCP] Reusing transport for session:", sessionId);
184
+ } else if (!sessionId && types_js.isInitializeRequest(parsedBody)) {
185
+ logger.logger.debug("[happyMCP] New initialize request, creating transport");
186
+ transport = new streamableHttp_js.StreamableHTTPServerTransport({
187
+ sessionIdGenerator: () => node_crypto.randomUUID(),
188
+ onsessioninitialized: (newSessionId) => {
189
+ logger.logger.debug("[happyMCP] Session initialized:", newSessionId);
190
+ transports.set(newSessionId, transport);
191
+ }
192
+ });
193
+ transport.onclose = () => {
194
+ const sid = transport.sessionId;
195
+ if (sid && transports.has(sid)) {
196
+ logger.logger.debug("[happyMCP] Transport closed, removing session:", sid);
197
+ transports.delete(sid);
198
+ }
199
+ };
200
+ transport.onerror = (error) => {
201
+ logger.logger.debug("[happyMCP] Transport error:", error);
202
+ };
203
+ const mcp = createMcpServer(client, { enableOrchestratorTools });
204
+ await mcp.connect(transport);
205
+ await transport.handleRequest(req, res, parsedBody);
206
+ logger.logger.debug("[happyMCP] Initialize request handled successfully");
207
+ return;
208
+ } else {
209
+ logger.logger.debug("[happyMCP] Bad request: no session ID and not initialize");
210
+ res.writeHead(400, { "Content-Type": "application/json" });
211
+ res.end(JSON.stringify({
212
+ jsonrpc: "2.0",
213
+ error: {
214
+ code: -32e3,
215
+ message: "Bad Request: No valid session ID provided"
216
+ },
217
+ id: null
218
+ }));
219
+ return;
220
+ }
221
+ await transport.handleRequest(req, res, parsedBody);
222
+ logger.logger.debug("[happyMCP] Request handled successfully");
223
+ } else if (req.method === "GET" || req.method === "DELETE") {
224
+ if (!sessionId || !transports.has(sessionId)) {
225
+ logger.logger.debug("[happyMCP] Bad request: invalid session for GET/DELETE");
226
+ res.writeHead(400, { "Content-Type": "text/plain" });
227
+ res.end("Invalid or missing session ID");
228
+ return;
229
+ }
230
+ const transport = transports.get(sessionId);
231
+ await transport.handleRequest(req, res);
232
+ logger.logger.debug("[happyMCP] GET/DELETE request handled successfully");
233
+ } else {
234
+ res.writeHead(405, { "Content-Type": "text/plain" });
235
+ res.end("Method not allowed");
236
+ }
237
+ } catch (error) {
238
+ logger.logger.debug("[happyMCP] Error handling request:", error);
239
+ if (!res.headersSent) {
240
+ res.writeHead(500).end();
241
+ }
242
+ }
243
+ });
244
+ const baseUrl = await new Promise((resolve) => {
245
+ server.listen(0, "127.0.0.1", () => {
246
+ const addr = server.address();
247
+ resolve(new URL(`http://127.0.0.1:${addr.port}`));
248
+ });
249
+ });
250
+ logger.logger.debug("[happyMCP] Server started at:", baseUrl.toString());
251
+ return {
252
+ url: baseUrl.toString(),
253
+ toolNames,
254
+ stop: async () => {
255
+ logger.logger.debug("[happyMCP] Stopping server");
256
+ for (const [sessionId, transport] of transports) {
257
+ logger.logger.debug("[happyMCP] Closing transport for session:", sessionId);
258
+ try {
259
+ await transport.close();
260
+ } catch (error) {
261
+ logger.logger.debug("[happyMCP] Error closing transport:", error);
262
+ }
263
+ }
264
+ transports.clear();
265
+ server.close();
266
+ }
267
+ };
268
+ }
269
+ function readRequestBody(req) {
270
+ return new Promise((resolve, reject) => {
271
+ let body = "";
272
+ req.on("data", (chunk) => body += chunk);
273
+ req.on("end", () => resolve(body));
274
+ req.on("error", reject);
275
+ });
276
+ }
277
+
278
+ const getBridgeCommand = () => path.join(projectPath.projectPath(), "bin", "happy-mcp.mjs");
279
+ function adaptForClaude(def) {
280
+ return { type: "http", url: def.url, ...def.headers && { headers: def.headers } };
281
+ }
282
+ function adaptForStdio(def) {
283
+ const args = ["--url", def.url];
284
+ if (def.headers) {
285
+ for (const [k, v] of Object.entries(def.headers)) {
286
+ args.push("--header", `${k}: ${v}`);
287
+ }
288
+ }
289
+ return { command: getBridgeCommand(), args };
290
+ }
291
+ function adaptForHttp(def) {
292
+ return { type: "http", url: def.url, headers: def.headers || {} };
293
+ }
294
+ async function createMcpContext(session) {
295
+ const happyServer = await startHappyServer(session);
296
+ const servers = {
297
+ happy: {
298
+ url: happyServer.url,
299
+ toolNames: happyServer.toolNames,
300
+ stop: () => happyServer.stop()
301
+ }
302
+ };
303
+ const extraServersJson = process.env.HAPPY_EXTRA_MCP_SERVERS;
304
+ if (extraServersJson) {
305
+ try {
306
+ const extraServers = JSON.parse(extraServersJson);
307
+ for (const srv of extraServers) {
308
+ servers[srv.name] = {
309
+ url: srv.url,
310
+ toolNames: [],
311
+ // External MCP — tools discovered by agent, not whitelisted
312
+ headers: srv.headers,
313
+ stop: () => {
314
+ }
315
+ // No lifecycle management for external servers
316
+ };
317
+ }
318
+ } catch (e) {
319
+ console.warn("Failed to parse HAPPY_EXTRA_MCP_SERVERS:", e);
320
+ }
321
+ }
322
+ return {
323
+ configForClaude() {
324
+ const result = {};
325
+ for (const [name, def] of Object.entries(servers)) {
326
+ result[name] = adaptForClaude(def);
327
+ }
328
+ return result;
329
+ },
330
+ configForStdio() {
331
+ const result = {};
332
+ for (const [name, def] of Object.entries(servers)) {
333
+ result[name] = adaptForStdio(def);
334
+ }
335
+ return result;
336
+ },
337
+ configForHttp() {
338
+ const result = {};
339
+ for (const [name, def] of Object.entries(servers)) {
340
+ result[name] = adaptForHttp(def);
341
+ }
342
+ return result;
343
+ },
344
+ allowedToolNames() {
345
+ return Object.entries(servers).flatMap(
346
+ ([name, def]) => def.toolNames.map((tool) => `mcp__${name}__${tool}`)
347
+ );
348
+ },
349
+ normalizeToolName(rawName) {
350
+ for (const [name, def] of Object.entries(servers)) {
351
+ if (def.toolNames.includes(rawName)) {
352
+ return `mcp:${name}:${rawName}`;
353
+ }
354
+ }
355
+ return rawName;
356
+ },
357
+ stop() {
358
+ for (const def of Object.values(servers)) {
359
+ def.stop();
360
+ }
361
+ }
362
+ };
363
+ }
364
+
365
+ function detectGitWorktree(cwd) {
366
+ try {
367
+ const gitDir = node_child_process.execSync("git rev-parse --git-dir", {
368
+ cwd,
369
+ encoding: "utf8",
370
+ stdio: ["ignore", "pipe", "ignore"]
371
+ }).trim();
372
+ const gitCommonDir = node_child_process.execSync("git rev-parse --git-common-dir", {
373
+ cwd,
374
+ encoding: "utf8",
375
+ stdio: ["ignore", "pipe", "ignore"]
376
+ }).trim();
377
+ const absGitDir = path.resolve(cwd, gitDir);
378
+ const absCommonDir = path.resolve(cwd, gitCommonDir);
379
+ if (absGitDir === absCommonDir) {
380
+ return { isWorktree: false };
381
+ }
382
+ const basePath = path.dirname(absCommonDir);
383
+ const branchName = node_child_process.execSync("git rev-parse --abbrev-ref HEAD", {
384
+ cwd,
385
+ encoding: "utf8",
386
+ stdio: ["ignore", "pipe", "ignore"]
387
+ }).trim();
388
+ return {
389
+ isWorktree: true,
390
+ worktreeBasePath: basePath,
391
+ worktreeBranchName: branchName || void 0
392
+ };
393
+ } catch {
394
+ return { isWorktree: false };
395
+ }
396
+ }
397
+
398
+ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
399
+ rpcHandlerManager.registerHandler("killSession", async () => {
400
+ logger.logger.debug("Kill session request received");
401
+ void killThisHappy();
402
+ return {
403
+ success: true,
404
+ message: "Killing happy-cli process"
405
+ };
406
+ });
407
+ }
408
+
409
+ exports.createMcpContext = createMcpContext;
410
+ exports.detectGitWorktree = detectGitWorktree;
411
+ exports.registerKillSessionHandler = registerKillSessionHandler;
@@ -1,15 +1,15 @@
1
1
  import fs from 'fs/promises';
2
2
  import os from 'os';
3
3
  import * as tmp from 'tmp';
4
- import { A as ApiClient } from './api-Ew6xr81i.mjs';
5
- import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, e as encodeBase64, x as writeDaemonState, m as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-D9tMm6bj.mjs';
6
- import { authAndSetupMachineIfNeeded } from './auth-Q2STUNI-.mjs';
4
+ import { A as ApiClient } from './api-BUCtd3cB.mjs';
5
+ import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, e as encodeBase64, x as writeDaemonState, m as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-DbLMvLvk.mjs';
6
+ import { authAndSetupMachineIfNeeded } from './auth-qM2eojWx.mjs';
7
7
  import { spawn, execSync, exec } from 'child_process';
8
- import { getEnvironmentInfo } from './doctor-DRnft_5-.mjs';
9
- import { s as spawnHappyCLI } from './spawnHappyCLI-DhUeycct.mjs';
10
- import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-DoS9JqQC.mjs';
11
- import { a as readCachedDeviceKey, d as deviceExec } from './deviceKeys-B4aqjnE6.mjs';
12
- import { P as PLIST_LABEL } from './install-DBg0xQ3g.mjs';
8
+ import { getEnvironmentInfo } from './doctor-DDT2TXHH.mjs';
9
+ import { s as spawnHappyCLI } from './spawnHappyCLI-Dt7fbmbk.mjs';
10
+ import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-BzzTNSfl.mjs';
11
+ import { a as readCachedDeviceKey, d as deviceExec } from './deviceKeys-CQyFFPtt.mjs';
12
+ import { P as PLIST_LABEL } from './install-BOIcR_Wr.mjs';
13
13
  import fastify from 'fastify';
14
14
  import { createReadStream } from 'node:fs';
15
15
  import { stat } from 'node:fs/promises';
@@ -1851,6 +1851,8 @@ ${error.message}
1851
1851
  if (!skipForkSession) {
1852
1852
  args.push("--fork-session");
1853
1853
  }
1854
+ } else if (resumeSessionId && options.agent === "cursor") {
1855
+ args.push("--resume", resumeSessionId);
1854
1856
  }
1855
1857
  const happyProcess = spawnHappyCLI(args, {
1856
1858
  cwd: directory,
@@ -3,15 +3,15 @@
3
3
  var fs$1 = require('fs/promises');
4
4
  var os = require('os');
5
5
  var tmp = require('tmp');
6
- var api = require('./api-CIE4_cWd.cjs');
7
- var logger = require('./persistence-BcQmjobZ.cjs');
8
- var auth = require('./auth-G9fZpVJ2.cjs');
6
+ var api = require('./api-B-PlVQY6.cjs');
7
+ var logger = require('./persistence-D7zNFI-d.cjs');
8
+ var auth = require('./auth-BqGkUXvq.cjs');
9
9
  var child_process = require('child_process');
10
- var doctor = require('./doctor-BJSuVRUZ.cjs');
11
- var spawnHappyCLI = require('./spawnHappyCLI-Cfu7C1uM.cjs');
12
- var controlClient = require('./controlClient-CEz_p1zc.cjs');
13
- var deviceKeys = require('./deviceKeys-DyUn0bEx.cjs');
14
- var install = require('./install-Tyhs6rUp.cjs');
10
+ var doctor = require('./doctor-D4xWXiE7.cjs');
11
+ var spawnHappyCLI = require('./spawnHappyCLI-C2uY9WV-.cjs');
12
+ var controlClient = require('./controlClient-yTkpwcOq.cjs');
13
+ var deviceKeys = require('./deviceKeys-CdW-HPnz.cjs');
14
+ var install = require('./install-Jrs6YUu8.cjs');
15
15
  var fastify = require('fastify');
16
16
  var fs = require('node:fs');
17
17
  var promises = require('node:fs/promises');
@@ -1872,6 +1872,8 @@ ${error.message}
1872
1872
  if (!skipForkSession) {
1873
1873
  args.push("--fork-session");
1874
1874
  }
1875
+ } else if (resumeSessionId && options.agent === "cursor") {
1876
+ args.push("--resume", resumeSessionId);
1875
1877
  }
1876
1878
  const happyProcess = spawnHappyCLI.spawnHappyCLI(args, {
1877
1879
  cwd: directory,
@@ -4,20 +4,20 @@ var os = require('node:os');
4
4
  var node_crypto = require('node:crypto');
5
5
  var fs$1 = require('node:fs');
6
6
  var promises = require('node:fs/promises');
7
- var api = require('./api-CIE4_cWd.cjs');
8
- var logger = require('./persistence-BcQmjobZ.cjs');
9
- var claudeLocal = require('./claudeLocal-f4uBLJaM.cjs');
7
+ var api = require('./api-B-PlVQY6.cjs');
8
+ var logger = require('./persistence-D7zNFI-d.cjs');
9
+ var claudeLocal = require('./claudeLocal-HAXk0deK.cjs');
10
10
  var types = require('./types-DWeB7aqh.cjs');
11
11
  var path = require('node:path');
12
12
  var time = require('./time-Bj2HRkKH.cjs');
13
13
  var fs = require('fs/promises');
14
14
  var ink = require('ink');
15
- var downloadImage = require('./downloadImage-Cijmufpr.cjs');
15
+ var downloadImage = require('./downloadImage-3A6zY5AX.cjs');
16
16
  var React = require('react');
17
17
  var node_child_process = require('node:child_process');
18
18
  var node_readline = require('node:readline');
19
19
  var node_url = require('node:url');
20
- var spawnHappyCLI = require('./spawnHappyCLI-Cfu7C1uM.cjs');
20
+ var spawnHappyCLI = require('./spawnHappyCLI-C2uY9WV-.cjs');
21
21
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
22
22
  require('axios');
23
23
  require('node:events');
@@ -28,13 +28,13 @@ require('crypto');
28
28
  require('@stablelib/base64');
29
29
  require('expo-server-sdk');
30
30
  require('chalk');
31
- var formatImageMessage = require('./formatImageMessage-BPK_Ivfe.cjs');
31
+ var formatImageMessage = require('./formatImageMessage-bVFGFDJl.cjs');
32
32
  var node_util = require('node:util');
33
- var run = require('./run-CiPWWhXt.cjs');
34
- var doctor = require('./doctor-BJSuVRUZ.cjs');
35
- var controlClient = require('./controlClient-CEz_p1zc.cjs');
33
+ var run = require('./run-C5StYRIB.cjs');
34
+ var doctor = require('./doctor-D4xWXiE7.cjs');
35
+ var controlClient = require('./controlClient-yTkpwcOq.cjs');
36
+ var registerKillSessionHandler = require('./registerKillSessionHandler-MoLdSQxM.cjs');
36
37
  var node_http = require('node:http');
37
- var gitWorktree = require('./gitWorktree-C6jLoaMw.cjs');
38
38
  require('zod');
39
39
  require('./index-BeKrqPYR.cjs');
40
40
  require('@paralleldrive/cuid2');
@@ -42,29 +42,29 @@ require('child_process');
42
42
  require('util');
43
43
  require('path');
44
44
  require('node:module');
45
- require('./sessionKeyCache-BwFUGgMs.cjs');
45
+ require('./sessionKeyCache-3Pme04yL.cjs');
46
46
  require('fs');
47
47
  require('./prompt-BTWDZZO0.cjs');
48
48
  require('./trimIdent-4gvk9Wk6.cjs');
49
49
  require('./common-CZvSRU4p.cjs');
50
- require('@modelcontextprotocol/sdk/server/mcp.js');
51
- require('@modelcontextprotocol/sdk/server/streamableHttp.js');
52
- require('@modelcontextprotocol/sdk/types.js');
53
- require('./deviceKeys-DyUn0bEx.cjs');
54
- require('./auth-G9fZpVJ2.cjs');
55
- require('qrcode-terminal');
56
- require('./browser-qDIIAL7S.cjs');
57
- require('open');
58
- require('./mcpToolSchemas-DK42yoZ_.cjs');
59
50
  require('url');
60
51
  require('os');
61
52
  require('tmp');
62
- require('./install-Tyhs6rUp.cjs');
53
+ require('./auth-BqGkUXvq.cjs');
54
+ require('qrcode-terminal');
55
+ require('./browser-CY7VNU0C.cjs');
56
+ require('open');
57
+ require('./deviceKeys-CdW-HPnz.cjs');
58
+ require('./install-Jrs6YUu8.cjs');
63
59
  require('fastify');
64
60
  require('fastify-type-provider-zod');
65
61
  require('./doctor-C08m5Z9f.cjs');
66
62
  require('ps-list');
67
63
  require('cross-spawn');
64
+ require('@modelcontextprotocol/sdk/server/mcp.js');
65
+ require('@modelcontextprotocol/sdk/server/streamableHttp.js');
66
+ require('@modelcontextprotocol/sdk/types.js');
67
+ require('./mcpToolSchemas-DK42yoZ_.cjs');
68
68
 
69
69
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
70
70
  class Session {
@@ -732,7 +732,7 @@ class AbortError extends Error {
732
732
  }
733
733
  }
734
734
 
735
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-BVXsyCHb.cjs', document.baseURI).href)));
735
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-ChRp6f5G.cjs', document.baseURI).href)));
736
736
  const __dirname$1 = path.join(__filename$1, "..");
737
737
  function getGlobalClaudeVersion() {
738
738
  try {
@@ -3288,7 +3288,7 @@ function detectWorktreeMetadata(cwd) {
3288
3288
  worktreeBranchName: process.env.HAPPY_WORKTREE_BRANCH_NAME
3289
3289
  };
3290
3290
  }
3291
- const info = gitWorktree.detectGitWorktree(cwd);
3291
+ const info = registerKillSessionHandler.detectGitWorktree(cwd);
3292
3292
  if (info.isWorktree) {
3293
3293
  return {
3294
3294
  isWorktree: true,
@@ -3302,7 +3302,7 @@ function detectWorktreeMetadata(cwd) {
3302
3302
  for (const entry of entries) {
3303
3303
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
3304
3304
  const subDir = path.join(cwd, entry.name);
3305
- const sub = gitWorktree.detectGitWorktree(subDir);
3305
+ const sub = registerKillSessionHandler.detectGitWorktree(subDir);
3306
3306
  if (sub.isWorktree && sub.worktreeBasePath && sub.worktreeBranchName) {
3307
3307
  repos.push({
3308
3308
  path: subDir,
@@ -3464,7 +3464,7 @@ async function runClaude(credentials, options = {}) {
3464
3464
  }));
3465
3465
  logger.logger.debug(`[START] Injected ${injectedMemoryIds.length} memories into session`);
3466
3466
  }
3467
- const mcp = await downloadImage.createMcpContext(session);
3467
+ const mcp = await registerKillSessionHandler.createMcpContext(session);
3468
3468
  logger.logger.debug(`[START] MCP context created`);
3469
3469
  let currentSession = null;
3470
3470
  const shouldBackfill = ["1", "true", "yes"].includes(String(process.env.HAPPY_CLAUDE_BACKFILL).toLowerCase());
@@ -3722,7 +3722,7 @@ async function runClaude(credentials, options = {}) {
3722
3722
  logger.logger.debug("[START] Unhandled rejection:", reason);
3723
3723
  cleanup();
3724
3724
  });
3725
- downloadImage.registerKillSessionHandler(session.rpcHandlerManager, cleanup);
3725
+ registerKillSessionHandler.registerKillSessionHandler(session.rpcHandlerManager, cleanup);
3726
3726
  const exitCode = await loop({
3727
3727
  path: workingDirectory,
3728
3728
  model: options.model,