very-happy-cli 0.2.141 → 0.2.143

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 (57) hide show
  1. package/dist/{AcpBackend-CnWSwaHk.cjs → AcpBackend-B8oehrfU.cjs} +2 -2
  2. package/dist/{AcpBackend-CWmjboOm.mjs → AcpBackend-LEgn9vol.mjs} +2 -2
  3. package/dist/{AcpSessionManager-Dxt78rPO.cjs → AcpSessionManager-BomdfyhZ.cjs} +1 -1
  4. package/dist/{AcpSessionManager-CtXoi6PN.mjs → AcpSessionManager-Yfbs5bi2.mjs} +1 -1
  5. package/dist/{agentGuidance-Do7cQka3.mjs → agentGuidance-BU4hxm8M.mjs} +1 -1
  6. package/dist/{agentGuidance-B9ENxSaS.cjs → agentGuidance-CJSl54ua.cjs} +1 -1
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-Dn7nAHcA.cjs → config-CfqWoHmO.cjs} +5 -4
  10. package/dist/{config-DY57frJF.mjs → config-euZ_K5Lo.mjs} +5 -4
  11. package/dist/{index-BSVgut_Y.cjs → index-BREtikhm.cjs} +5 -4
  12. package/dist/{index-3STVDtNO.cjs → index-Bj68y6ee.cjs} +12 -40
  13. package/dist/{index-ChWePQEd.cjs → index-C510dSUn.cjs} +188 -132
  14. package/dist/{index-C55sW8E9.mjs → index-DoiG88oX.mjs} +169 -114
  15. package/dist/{index-DhfgAB8K.mjs → index-PM_SsRpw.mjs} +5 -4
  16. package/dist/{index-Dz6FgNFg.mjs → index-ZOMC8Gzn.mjs} +11 -39
  17. package/dist/index.cjs +7 -6
  18. package/dist/index.mjs +7 -6
  19. package/dist/installPiTools-BKnXjrVu.cjs +82 -0
  20. package/dist/installPiTools-C-Lxv4Bt.mjs +77 -0
  21. package/dist/{installTerminalHooks-zmtIrf3E.mjs → installTerminalHooks-BGVQqbgq.mjs} +8 -7
  22. package/dist/{installTerminalHooks--jkP17uE.cjs → installTerminalHooks-C_jraM1j.cjs} +9 -8
  23. package/dist/lib.cjs +3 -2
  24. package/dist/lib.d.cts +6 -1
  25. package/dist/lib.d.mts +6 -1
  26. package/dist/lib.mjs +3 -2
  27. package/dist/{limits-DbLWXlJm.cjs → limits-B3oVgqBJ.cjs} +29 -10
  28. package/dist/{limits-DT5Yk_UH.mjs → limits-uABU-yq3.mjs} +29 -11
  29. package/dist/{mcp-Bzy-X9kI.mjs → mcp-CfbTQXyh.mjs} +28 -14
  30. package/dist/{mcp-kE0n1dBF.cjs → mcp-G-3lL_KH.cjs} +29 -15
  31. package/dist/piExtension.d.mts +17 -0
  32. package/dist/piExtension.mjs +84 -0
  33. package/dist/piRuntime-BE6iYQwU.mjs +37 -0
  34. package/dist/piRuntime-CWE-1W-y.cjs +39 -0
  35. package/dist/piTerminal-Dyc_9pGP.cjs +132 -0
  36. package/dist/piTerminal-rqKn1_Zy.mjs +129 -0
  37. package/dist/projectPath-CSpqWdxp.mjs +10 -0
  38. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  39. package/dist/{runGemini-BmdhYEzM.mjs → runGemini-BMlXIaTL.mjs} +8 -7
  40. package/dist/{runGemini-CxUz95A1.cjs → runGemini-C6qMFzcn.cjs} +9 -8
  41. package/dist/{runOpenClaw-RLIFJx61.cjs → runOpenClaw-C2_aCP7v.cjs} +6 -5
  42. package/dist/{runOpenClaw-CYpO3Vv8.mjs → runOpenClaw-CoO5Vp3S.mjs} +6 -5
  43. package/dist/{send-DT04hV8o.mjs → send-CBrJylJc.mjs} +5 -4
  44. package/dist/{send-BGcCrMUT.cjs → send-DGVwmB-p.cjs} +5 -4
  45. package/dist/{sessions-DDWTq2AG.mjs → sessions-DbLubh2N.mjs} +7 -6
  46. package/dist/{sessions-Jt9lrcrG.cjs → sessions-DyQXMN-8.cjs} +7 -6
  47. package/dist/{spawn-CdZgvthE.cjs → spawn-DIyFOn3W.cjs} +5 -4
  48. package/dist/{spawn-Ca0A70dX.mjs → spawn-wkkxEGdF.mjs} +5 -4
  49. package/dist/{teams-CzK1zg0m.mjs → teams-BTyVFK3P.mjs} +5 -4
  50. package/dist/{teams-DfVwVZ4x.cjs → teams-QOkGFjkJ.cjs} +5 -4
  51. package/dist/terminalToolContext-CFeGeaNm.mjs +7 -0
  52. package/dist/terminalToolContext-DoV9Gs7K.cjs +9 -0
  53. package/dist/{todo-4WhOom8k.mjs → todo-DoLkqdBe.mjs} +4 -3
  54. package/dist/{todo-D9nFaYpQ.cjs → todo-Du6pjwx_.cjs} +4 -3
  55. package/dist/{types-Bk3W89Z3.cjs → types-6__wqmmP.cjs} +80 -17
  56. package/dist/{types-CLosaeZT.mjs → types-BG4gFjLL.mjs} +79 -15
  57. package/package.json +11 -7
@@ -1,10 +1,10 @@
1
- import { r as registerTeamsTools } from './agentGuidance-Do7cQka3.mjs';
1
+ import { r as registerTeamsTools, P as PREVIEW_TOOL_NAME, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION } from './agentGuidance-BU4hxm8M.mjs';
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
- import { f as pushClipboardViaDaemon, g as setTerminalTitleViaDaemon } from './types-CLosaeZT.mjs';
6
- import { l as logger, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES } from './limits-DT5Yk_UH.mjs';
7
- import { h as registerAssistantSessionTools } from './index-C55sW8E9.mjs';
5
+ import { p as pushClipboardViaDaemon, f as pushFilePreviewViaDaemon, g as setTerminalTitleViaDaemon } from './types-BG4gFjLL.mjs';
6
+ import { l as logger, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES } from './limits-uABU-yq3.mjs';
7
+ import { h as checkPreviewPath, j as registerAssistantSessionTools } from './index-DoiG88oX.mjs';
8
8
  import 'node:fs/promises';
9
9
  import 'node:os';
10
10
  import 'node:crypto';
@@ -14,6 +14,7 @@ import 'socket.io-client';
14
14
  import 'node:child_process';
15
15
  import 'node:fs';
16
16
  import 'node:path';
17
+ import { r as resolveMcpTerminalId } from './terminalToolContext-CFeGeaNm.mjs';
17
18
  import 'node:events';
18
19
  import '@paralleldrive/cuid2';
19
20
  import 'child_process';
@@ -22,6 +23,7 @@ import 'fs/promises';
22
23
  import 'crypto';
23
24
  import 'path';
24
25
  import 'cross-spawn';
26
+ import './projectPath-CSpqWdxp.mjs';
25
27
  import 'url';
26
28
  import 'os';
27
29
  import 'node-pty';
@@ -48,11 +50,6 @@ import 'node:url';
48
50
  import 'node:readline/promises';
49
51
 
50
52
  const TERMINAL_TITLE_TOOL_NAME = "change_title";
51
- function resolveMcpTerminalId(env) {
52
- if (env.HAPPY_MCP_URL) return null;
53
- const id = env.VH_TERMINAL_ID;
54
- return id && /^[a-zA-Z0-9_-]{1,64}$/.test(id) ? id : null;
55
- }
56
53
  function resolveMcpToolSurface(env) {
57
54
  if (env.HAPPY_SESSION_VARIANT !== "assistant") return "clipboard";
58
55
  if (env.HAPPY_MANAGED === "1") return "clipboard";
@@ -68,13 +65,13 @@ function registerMcpTools(server, surface, terminalId = null) {
68
65
  }
69
66
  }, async (args) => {
70
67
  logger.debug(`[MCP] copy_to_clipboard called (${args.text.length} chars)`);
71
- const result = await pushClipboardViaDaemon(args.text);
68
+ const result = await pushClipboardViaDaemon(args.text, terminalId ?? void 0);
72
69
  if (result.delivered) {
73
70
  const note = result.truncated ? ` (truncated to ${CLIPBOARD_MAX_BYTES / 1024}KB \u2014 original was ${result.totalBytes} bytes)` : "";
74
71
  return {
75
72
  content: [{
76
73
  type: "text",
77
- text: `Sent to the user's clipboard on their currently open device(s)${note}. If the page was not focused, they may need to tap a confirmation button.`
74
+ text: `Queued a clipboard request for the user's open Very Happy device(s)${note}. This does not confirm that a browser wrote the clipboard; the user may need to tap a confirmation button.`
78
75
  }],
79
76
  isError: false
80
77
  };
@@ -88,6 +85,22 @@ function registerMcpTools(server, surface, terminalId = null) {
88
85
  };
89
86
  });
90
87
  if (terminalId) {
88
+ server.registerTool(PREVIEW_TOOL_NAME, {
89
+ description: PREVIEW_TOOL_DESCRIPTION,
90
+ title: PREVIEW_TOOL_TITLE,
91
+ inputSchema: {
92
+ path: z.string().min(1).describe("Absolute path or path relative to the current working directory"),
93
+ mode: z.enum(["file", "diff"]).optional().describe("'file' (default) previews the file; 'diff' is reserved and currently falls back to 'file'")
94
+ }
95
+ }, async (args) => {
96
+ const verdict = checkPreviewPath(args.path);
97
+ if (verdict.deniedReason) return { content: [{ type: "text", text: verdict.deniedReason }], isError: true };
98
+ const result = await pushFilePreviewViaDaemon(terminalId, verdict.resolved, args.mode ?? "file");
99
+ return {
100
+ content: [{ type: "text", text: result.delivered ? `Queued a preview request for ${verdict.resolved}. This does not confirm that a browser opened it.` : `Failed to request preview: ${result.error || "unknown error"}` }],
101
+ isError: !result.delivered
102
+ };
103
+ });
91
104
  server.registerTool(TERMINAL_TITLE_TOOL_NAME, {
92
105
  description: "Change the title of the very-happy web terminal this agent is running in",
93
106
  title: "Change Terminal Title",
@@ -116,15 +129,16 @@ function registerMcpTools(server, surface, terminalId = null) {
116
129
  registerAssistantSessionTools(server);
117
130
  }
118
131
  }
119
- async function handleMcpCommand() {
132
+ async function handleMcpCommand(terminalToolsOnly = false) {
120
133
  const server = new McpServer({
121
134
  name: "very-happy",
122
135
  version: "1.0.0"
123
136
  });
124
- const surface = resolveMcpToolSurface(process.env);
137
+ const surface = terminalToolsOnly ? "clipboard" : resolveMcpToolSurface(process.env);
125
138
  const terminalId = resolveMcpTerminalId(process.env);
139
+ if (terminalToolsOnly && !terminalId) throw new Error("Very Happy terminal context is required");
126
140
  registerMcpTools(server, surface, terminalId);
127
- if (process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
141
+ if (!terminalToolsOnly && process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
128
142
  const transport = new StdioServerTransport();
129
143
  await server.connect(transport);
130
144
  logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var agentGuidance = require('./agentGuidance-B9ENxSaS.cjs');
3
+ var agentGuidance = require('./agentGuidance-CJSl54ua.cjs');
4
4
  var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
5
5
  var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
6
6
  var z = require('zod');
7
- var api = require('./types-Bk3W89Z3.cjs');
8
- var persistence = require('./limits-DbLWXlJm.cjs');
9
- var index = require('./index-ChWePQEd.cjs');
7
+ var api = require('./types-6__wqmmP.cjs');
8
+ var persistence = require('./limits-B3oVgqBJ.cjs');
9
+ var index = require('./index-C510dSUn.cjs');
10
10
  require('node:fs/promises');
11
11
  require('node:os');
12
12
  require('node:crypto');
@@ -16,6 +16,7 @@ require('socket.io-client');
16
16
  require('node:child_process');
17
17
  require('node:fs');
18
18
  require('node:path');
19
+ var terminalToolContext = require('./terminalToolContext-DoV9Gs7K.cjs');
19
20
  require('node:events');
20
21
  require('@paralleldrive/cuid2');
21
22
  require('child_process');
@@ -24,6 +25,7 @@ require('fs/promises');
24
25
  require('crypto');
25
26
  require('path');
26
27
  require('cross-spawn');
28
+ require('./projectPath-ChLhdOyt.cjs');
27
29
  require('url');
28
30
  require('os');
29
31
  require('node-pty');
@@ -50,11 +52,6 @@ require('node:url');
50
52
  require('node:readline/promises');
51
53
 
52
54
  const TERMINAL_TITLE_TOOL_NAME = "change_title";
53
- function resolveMcpTerminalId(env) {
54
- if (env.HAPPY_MCP_URL) return null;
55
- const id = env.VH_TERMINAL_ID;
56
- return id && /^[a-zA-Z0-9_-]{1,64}$/.test(id) ? id : null;
57
- }
58
55
  function resolveMcpToolSurface(env) {
59
56
  if (env.HAPPY_SESSION_VARIANT !== "assistant") return "clipboard";
60
57
  if (env.HAPPY_MANAGED === "1") return "clipboard";
@@ -70,13 +67,13 @@ function registerMcpTools(server, surface, terminalId = null) {
70
67
  }
71
68
  }, async (args) => {
72
69
  persistence.logger.debug(`[MCP] copy_to_clipboard called (${args.text.length} chars)`);
73
- const result = await api.pushClipboardViaDaemon(args.text);
70
+ const result = await api.pushClipboardViaDaemon(args.text, terminalId ?? void 0);
74
71
  if (result.delivered) {
75
72
  const note = result.truncated ? ` (truncated to ${persistence.CLIPBOARD_MAX_BYTES / 1024}KB \u2014 original was ${result.totalBytes} bytes)` : "";
76
73
  return {
77
74
  content: [{
78
75
  type: "text",
79
- text: `Sent to the user's clipboard on their currently open device(s)${note}. If the page was not focused, they may need to tap a confirmation button.`
76
+ text: `Queued a clipboard request for the user's open Very Happy device(s)${note}. This does not confirm that a browser wrote the clipboard; the user may need to tap a confirmation button.`
80
77
  }],
81
78
  isError: false
82
79
  };
@@ -90,6 +87,22 @@ function registerMcpTools(server, surface, terminalId = null) {
90
87
  };
91
88
  });
92
89
  if (terminalId) {
90
+ server.registerTool(agentGuidance.PREVIEW_TOOL_NAME, {
91
+ description: agentGuidance.PREVIEW_TOOL_DESCRIPTION,
92
+ title: agentGuidance.PREVIEW_TOOL_TITLE,
93
+ inputSchema: {
94
+ path: z.z.string().min(1).describe("Absolute path or path relative to the current working directory"),
95
+ mode: z.z.enum(["file", "diff"]).optional().describe("'file' (default) previews the file; 'diff' is reserved and currently falls back to 'file'")
96
+ }
97
+ }, async (args) => {
98
+ const verdict = index.checkPreviewPath(args.path);
99
+ if (verdict.deniedReason) return { content: [{ type: "text", text: verdict.deniedReason }], isError: true };
100
+ const result = await api.pushFilePreviewViaDaemon(terminalId, verdict.resolved, args.mode ?? "file");
101
+ return {
102
+ content: [{ type: "text", text: result.delivered ? `Queued a preview request for ${verdict.resolved}. This does not confirm that a browser opened it.` : `Failed to request preview: ${result.error || "unknown error"}` }],
103
+ isError: !result.delivered
104
+ };
105
+ });
93
106
  server.registerTool(TERMINAL_TITLE_TOOL_NAME, {
94
107
  description: "Change the title of the very-happy web terminal this agent is running in",
95
108
  title: "Change Terminal Title",
@@ -118,15 +131,16 @@ function registerMcpTools(server, surface, terminalId = null) {
118
131
  index.registerAssistantSessionTools(server);
119
132
  }
120
133
  }
121
- async function handleMcpCommand() {
134
+ async function handleMcpCommand(terminalToolsOnly = false) {
122
135
  const server = new mcp_js.McpServer({
123
136
  name: "very-happy",
124
137
  version: "1.0.0"
125
138
  });
126
- const surface = resolveMcpToolSurface(process.env);
127
- const terminalId = resolveMcpTerminalId(process.env);
139
+ const surface = terminalToolsOnly ? "clipboard" : resolveMcpToolSurface(process.env);
140
+ const terminalId = terminalToolContext.resolveMcpTerminalId(process.env);
141
+ if (terminalToolsOnly && !terminalId) throw new Error("Very Happy terminal context is required");
128
142
  registerMcpTools(server, surface, terminalId);
129
- if (process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) agentGuidance.registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
143
+ if (!terminalToolsOnly && process.env.HAPPY_MANAGED !== "1" && !process.env.HAPPY_MCP_URL) agentGuidance.registerTeamsTools(server, process.env.HAPPY_SESSION_ID);
130
144
  const transport = new stdio_js.StdioServerTransport();
131
145
  await server.connect(transport);
132
146
  persistence.logger.debug(`[MCP] very-happy stdio MCP server started (surface=${surface}, terminal=${terminalId ?? "none"})`);
@@ -0,0 +1,17 @@
1
+ type PiContext = {
2
+ cwd: string;
3
+ };
4
+ type PiApi = {
5
+ on(event: 'session_start' | 'session_shutdown', handler: (event: unknown, ctx: PiContext) => Promise<void>): void;
6
+ registerTool(tool: {
7
+ name: string;
8
+ label: string;
9
+ description: string;
10
+ parameters: unknown;
11
+ execute(id: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<unknown>;
12
+ }): void;
13
+ };
14
+ /** Auto-discovered native Pi extension. Managed launchers own their own bridge. */
15
+ declare function piTerminalExtension(pi: PiApi): void;
16
+
17
+ export { piTerminalExtension as default };
@@ -0,0 +1,84 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import { resolve } from 'node:path';
4
+ import { p as projectPath } from './projectPath-CSpqWdxp.mjs';
5
+ import { r as resolveMcpTerminalId } from './terminalToolContext-CFeGeaNm.mjs';
6
+ import 'path';
7
+ import 'url';
8
+
9
+ function piTerminalExtension(pi) {
10
+ if (process.env.HAPPY_MCP_URL || process.env.HAPPY_TERMINAL_MCP_URL || !resolveMcpTerminalId(process.env)) return;
11
+ let client;
12
+ let connecting;
13
+ let stopped = false;
14
+ let cwd = process.cwd();
15
+ function connect() {
16
+ if (stopped) return Promise.reject(new Error("Very Happy terminal tools have shut down"));
17
+ if (connecting) return connecting;
18
+ if (client) return Promise.resolve(client);
19
+ const current = new Client({ name: "very-happy-pi-terminal", version: "1" });
20
+ client = current;
21
+ current.onclose = () => {
22
+ if (client === current) client = void 0;
23
+ };
24
+ const env = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== void 0));
25
+ if (env.VH_HAPPY_HOME_DIR) env.HAPPY_HOME_DIR = env.VH_HAPPY_HOME_DIR;
26
+ const transport = new StdioClientTransport({
27
+ // Pi may itself be a Bun/SEA executable. Use the CLI's Node runtime
28
+ // from PATH, just like the installed very-happy shebang.
29
+ command: "node",
30
+ args: ["--no-warnings", "--no-deprecation", resolve(projectPath(), "dist/index.mjs"), "mcp", "--terminal-tools"],
31
+ env,
32
+ cwd,
33
+ stderr: "pipe"
34
+ });
35
+ transport.stderr?.on("data", () => {
36
+ });
37
+ connecting = current.connect(transport, { timeout: 1e4 }).then(async () => {
38
+ if (stopped) {
39
+ await current.close();
40
+ throw new Error("Very Happy terminal tools have shut down");
41
+ }
42
+ return current;
43
+ }).catch(async (error) => {
44
+ if (client === current) client = void 0;
45
+ await current.close();
46
+ throw error;
47
+ }).finally(() => {
48
+ connecting = void 0;
49
+ });
50
+ return connecting;
51
+ }
52
+ pi.on("session_start", async (_event, ctx) => {
53
+ cwd = ctx.cwd;
54
+ const current = await connect();
55
+ const available = await current.listTools({}, { timeout: 1e4 });
56
+ if (stopped) return;
57
+ for (const tool of available.tools) {
58
+ pi.registerTool({
59
+ name: tool.name,
60
+ label: tool.title || tool.name,
61
+ description: tool.description || tool.name,
62
+ parameters: tool.inputSchema,
63
+ async execute(_id, args, signal) {
64
+ const active = await connect();
65
+ const result = await active.callTool({ name: tool.name, arguments: args }, void 0, { signal, timeout: 3e4 });
66
+ if (result.isError) {
67
+ const content = Array.isArray(result.content) ? result.content : [];
68
+ throw new Error(content.filter((block) => block.type === "text").map((block) => block.text).join("\n") || "Very Happy tool failed");
69
+ }
70
+ return { content: result.content, details: { isError: !!result.isError } };
71
+ }
72
+ });
73
+ }
74
+ });
75
+ pi.on("session_shutdown", async () => {
76
+ stopped = true;
77
+ await connecting?.catch(() => {
78
+ });
79
+ await client?.close();
80
+ client = void 0;
81
+ });
82
+ }
83
+
84
+ export { piTerminalExtension as default };
@@ -0,0 +1,37 @@
1
+ import { mkdir, chmod, writeFile, rename } from 'node:fs/promises';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { join } from 'node:path';
4
+ import { c as configuration } from './limits-uABU-yq3.mjs';
5
+ import { P as PI_TEAMS_EXTENSION } from './index-DoiG88oX.mjs';
6
+
7
+ async function publish(path, content, mode) {
8
+ const temporary = `${path}.${randomUUID()}.new`;
9
+ await writeFile(temporary, content, { mode, flag: "wx" });
10
+ await rename(temporary, path);
11
+ }
12
+ async function preparePiTeamsRuntime() {
13
+ const directory = join(configuration.happyHomeDir, "teams", "pi-runtime");
14
+ await mkdir(directory, { recursive: true, mode: 448 });
15
+ const extension = join(directory, "very-happy-teams.mjs");
16
+ await publish(extension, PI_TEAMS_EXTENSION, 384);
17
+ const launcher = join(directory, "pi-teams.mjs");
18
+ const script = `#!${process.execPath}
19
+ import { spawn } from 'node:child_process';
20
+ const child = spawn(process.env.VH_TEAM_PI_COMMAND || 'pi', ['--extension', ${JSON.stringify(extension)}, ...process.argv.slice(2)], {stdio:'inherit',env:process.env,shell:process.platform==='win32'});
21
+ child.on('error',()=>{process.stderr.write('Very Happy could not launch pi\\n');process.exitCode=1});
22
+ child.on('exit',(code)=>{process.exitCode=code ?? 1});
23
+ for(const signal of ['SIGINT','SIGTERM'])process.on(signal,()=>child.kill(signal));
24
+ `;
25
+ await publish(launcher, script, 448);
26
+ await chmod(launcher, 448);
27
+ let command = launcher;
28
+ if (process.platform === "win32") {
29
+ command = join(directory, "pi-teams.cmd");
30
+ await publish(command, `@echo off\r
31
+ "${process.execPath}" "${launcher}" %*\r
32
+ `, 448);
33
+ }
34
+ return { PI_ACP_PI_COMMAND: command, VH_TEAM_PI_COMMAND: (process.env.PI_ACP_PI_COMMAND === command ? process.env.VH_TEAM_PI_COMMAND : process.env.PI_ACP_PI_COMMAND) || "pi" };
35
+ }
36
+
37
+ export { preparePiTeamsRuntime as p };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ var promises = require('node:fs/promises');
4
+ var node_crypto = require('node:crypto');
5
+ var path = require('node:path');
6
+ var persistence = require('./limits-B3oVgqBJ.cjs');
7
+ var index = require('./index-C510dSUn.cjs');
8
+
9
+ async function publish(path, content, mode) {
10
+ const temporary = `${path}.${node_crypto.randomUUID()}.new`;
11
+ await promises.writeFile(temporary, content, { mode, flag: "wx" });
12
+ await promises.rename(temporary, path);
13
+ }
14
+ async function preparePiTeamsRuntime() {
15
+ const directory = path.join(persistence.configuration.happyHomeDir, "teams", "pi-runtime");
16
+ await promises.mkdir(directory, { recursive: true, mode: 448 });
17
+ const extension = path.join(directory, "very-happy-teams.mjs");
18
+ await publish(extension, index.PI_TEAMS_EXTENSION, 384);
19
+ const launcher = path.join(directory, "pi-teams.mjs");
20
+ const script = `#!${process.execPath}
21
+ import { spawn } from 'node:child_process';
22
+ const child = spawn(process.env.VH_TEAM_PI_COMMAND || 'pi', ['--extension', ${JSON.stringify(extension)}, ...process.argv.slice(2)], {stdio:'inherit',env:process.env,shell:process.platform==='win32'});
23
+ child.on('error',()=>{process.stderr.write('Very Happy could not launch pi\\n');process.exitCode=1});
24
+ child.on('exit',(code)=>{process.exitCode=code ?? 1});
25
+ for(const signal of ['SIGINT','SIGTERM'])process.on(signal,()=>child.kill(signal));
26
+ `;
27
+ await publish(launcher, script, 448);
28
+ await promises.chmod(launcher, 448);
29
+ let command = launcher;
30
+ if (process.platform === "win32") {
31
+ command = path.join(directory, "pi-teams.cmd");
32
+ await publish(command, `@echo off\r
33
+ "${process.execPath}" "${launcher}" %*\r
34
+ `, 448);
35
+ }
36
+ return { PI_ACP_PI_COMMAND: command, VH_TEAM_PI_COMMAND: (process.env.PI_ACP_PI_COMMAND === command ? process.env.VH_TEAM_PI_COMMAND : process.env.PI_ACP_PI_COMMAND) || "pi" };
37
+ }
38
+
39
+ exports.preparePiTeamsRuntime = preparePiTeamsRuntime;
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ var node_child_process = require('node:child_process');
4
+ var node_crypto = require('node:crypto');
5
+ var node_http = require('node:http');
6
+ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
7
+ var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
8
+ var piRuntime = require('./piRuntime-CWE-1W-y.cjs');
9
+ var mcp = require('./mcp-G-3lL_KH.cjs');
10
+ require('./agentGuidance-CJSl54ua.cjs');
11
+ require('node:fs/promises');
12
+ require('node:os');
13
+ require('zod');
14
+ require('./limits-B3oVgqBJ.cjs');
15
+ require('axios');
16
+ require('tweetnacl');
17
+ require('socket.io-client');
18
+ require('./types-6__wqmmP.cjs');
19
+ require('node:fs');
20
+ require('node:path');
21
+ var terminalToolContext = require('./terminalToolContext-DoV9Gs7K.cjs');
22
+ require('./index-C510dSUn.cjs');
23
+ require('chalk');
24
+ require('cross-spawn');
25
+ require('node:readline');
26
+ require('./projectPath-ChLhdOyt.cjs');
27
+ require('path');
28
+ require('url');
29
+ require('fs/promises');
30
+ require('ink');
31
+ require('react');
32
+ require('@anthropic-ai/claude-agent-sdk');
33
+ require('node:events');
34
+ require('os');
35
+ require('child_process');
36
+ require('@paralleldrive/cuid2');
37
+ require('fs');
38
+ require('@anthropic-ai/sandbox-runtime');
39
+ require('expo-server-sdk');
40
+ require('crypto');
41
+ require('ps-list');
42
+ require('tmp');
43
+ require('open');
44
+ require('fastify');
45
+ require('fastify-type-provider-zod');
46
+ require('node:module');
47
+ require('node:util');
48
+ require('http');
49
+ require('util');
50
+ require('inquirer');
51
+ require('node:url');
52
+ require('node:readline/promises');
53
+ require('@modelcontextprotocol/sdk/server/stdio.js');
54
+ require('node-pty');
55
+
56
+ async function startPiTerminalTools(terminalId) {
57
+ const path = `/terminal-tools/${node_crypto.randomUUID()}`;
58
+ const server = node_http.createServer(async (req, res) => {
59
+ if (req.url !== path || req.headers.origin || req.method !== "POST") {
60
+ res.writeHead(403).end();
61
+ return;
62
+ }
63
+ const mcp$1 = new mcp_js.McpServer({ name: "Very Happy Terminal Tools", version: "1.0.0" });
64
+ mcp.registerMcpTools(mcp$1, "clipboard", terminalId);
65
+ const transport = new streamableHttp_js.StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
66
+ res.on("close", () => {
67
+ void transport.close();
68
+ void mcp$1.close();
69
+ });
70
+ try {
71
+ await mcp$1.connect(transport);
72
+ await transport.handleRequest(req, res);
73
+ } catch {
74
+ if (!res.headersSent) res.writeHead(500).end();
75
+ void mcp$1.close();
76
+ }
77
+ });
78
+ await new Promise((resolve, reject) => {
79
+ server.once("error", reject);
80
+ server.listen(0, "127.0.0.1", () => {
81
+ server.off("error", reject);
82
+ resolve();
83
+ });
84
+ });
85
+ return {
86
+ url: `http://127.0.0.1:${server.address().port}${path}`,
87
+ stop: () => new Promise((resolve, reject) => {
88
+ server.close((error) => error ? reject(error) : resolve());
89
+ server.closeAllConnections();
90
+ })
91
+ };
92
+ }
93
+ async function runPiTerminal(args) {
94
+ const terminalId = terminalToolContext.resolveMcpTerminalId({ VH_TERMINAL_ID: process.env.VH_TERMINAL_ID });
95
+ if (!terminalId) throw new Error("Run `very-happy pi --terminal` inside a Very Happy terminal (VH_TERMINAL_ID is required).");
96
+ const runtime = await piRuntime.preparePiTeamsRuntime();
97
+ const bridge = process.env.HAPPY_MCP_URL ? null : await startPiTerminalTools(terminalId);
98
+ try {
99
+ return await new Promise((resolve, reject) => {
100
+ const child = node_child_process.spawn(runtime.PI_ACP_PI_COMMAND, args, {
101
+ stdio: "inherit",
102
+ env: { ...process.env, ...runtime, ...bridge ? { HAPPY_TERMINAL_MCP_URL: bridge.url } : {} },
103
+ shell: process.platform === "win32"
104
+ });
105
+ const interrupt = () => {
106
+ child.kill("SIGINT");
107
+ };
108
+ const terminate = () => {
109
+ child.kill("SIGTERM");
110
+ };
111
+ const cleanup = () => {
112
+ process.off("SIGINT", interrupt);
113
+ process.off("SIGTERM", terminate);
114
+ };
115
+ process.on("SIGINT", interrupt);
116
+ process.on("SIGTERM", terminate);
117
+ child.once("error", (error) => {
118
+ cleanup();
119
+ reject(new Error(`Could not launch native pi: ${error.message}`));
120
+ });
121
+ child.once("exit", (code, signal) => {
122
+ cleanup();
123
+ resolve(code ?? (signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1));
124
+ });
125
+ });
126
+ } finally {
127
+ await bridge?.stop();
128
+ }
129
+ }
130
+
131
+ exports.runPiTerminal = runPiTerminal;
132
+ exports.startPiTerminalTools = startPiTerminalTools;
@@ -0,0 +1,129 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { createServer } from 'node:http';
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
6
+ import { p as preparePiTeamsRuntime } from './piRuntime-BE6iYQwU.mjs';
7
+ import { registerMcpTools } from './mcp-CfbTQXyh.mjs';
8
+ import './agentGuidance-BU4hxm8M.mjs';
9
+ import 'node:fs/promises';
10
+ import 'node:os';
11
+ import 'zod';
12
+ import './limits-uABU-yq3.mjs';
13
+ import 'axios';
14
+ import 'tweetnacl';
15
+ import 'socket.io-client';
16
+ import './types-BG4gFjLL.mjs';
17
+ import 'node:fs';
18
+ import 'node:path';
19
+ import { r as resolveMcpTerminalId } from './terminalToolContext-CFeGeaNm.mjs';
20
+ import './index-DoiG88oX.mjs';
21
+ import 'chalk';
22
+ import 'cross-spawn';
23
+ import 'node:readline';
24
+ import './projectPath-CSpqWdxp.mjs';
25
+ import 'path';
26
+ import 'url';
27
+ import 'fs/promises';
28
+ import 'ink';
29
+ import 'react';
30
+ import '@anthropic-ai/claude-agent-sdk';
31
+ import 'node:events';
32
+ import 'os';
33
+ import 'child_process';
34
+ import '@paralleldrive/cuid2';
35
+ import 'fs';
36
+ import '@anthropic-ai/sandbox-runtime';
37
+ import 'expo-server-sdk';
38
+ import 'crypto';
39
+ import 'ps-list';
40
+ import 'tmp';
41
+ import 'open';
42
+ import 'fastify';
43
+ import 'fastify-type-provider-zod';
44
+ import 'node:module';
45
+ import 'node:util';
46
+ import 'http';
47
+ import 'util';
48
+ import 'inquirer';
49
+ import 'node:url';
50
+ import 'node:readline/promises';
51
+ import '@modelcontextprotocol/sdk/server/stdio.js';
52
+ import 'node-pty';
53
+
54
+ async function startPiTerminalTools(terminalId) {
55
+ const path = `/terminal-tools/${randomUUID()}`;
56
+ const server = createServer(async (req, res) => {
57
+ if (req.url !== path || req.headers.origin || req.method !== "POST") {
58
+ res.writeHead(403).end();
59
+ return;
60
+ }
61
+ const mcp = new McpServer({ name: "Very Happy Terminal Tools", version: "1.0.0" });
62
+ registerMcpTools(mcp, "clipboard", terminalId);
63
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
64
+ res.on("close", () => {
65
+ void transport.close();
66
+ void mcp.close();
67
+ });
68
+ try {
69
+ await mcp.connect(transport);
70
+ await transport.handleRequest(req, res);
71
+ } catch {
72
+ if (!res.headersSent) res.writeHead(500).end();
73
+ void mcp.close();
74
+ }
75
+ });
76
+ await new Promise((resolve, reject) => {
77
+ server.once("error", reject);
78
+ server.listen(0, "127.0.0.1", () => {
79
+ server.off("error", reject);
80
+ resolve();
81
+ });
82
+ });
83
+ return {
84
+ url: `http://127.0.0.1:${server.address().port}${path}`,
85
+ stop: () => new Promise((resolve, reject) => {
86
+ server.close((error) => error ? reject(error) : resolve());
87
+ server.closeAllConnections();
88
+ })
89
+ };
90
+ }
91
+ async function runPiTerminal(args) {
92
+ const terminalId = resolveMcpTerminalId({ VH_TERMINAL_ID: process.env.VH_TERMINAL_ID });
93
+ if (!terminalId) throw new Error("Run `very-happy pi --terminal` inside a Very Happy terminal (VH_TERMINAL_ID is required).");
94
+ const runtime = await preparePiTeamsRuntime();
95
+ const bridge = process.env.HAPPY_MCP_URL ? null : await startPiTerminalTools(terminalId);
96
+ try {
97
+ return await new Promise((resolve, reject) => {
98
+ const child = spawn(runtime.PI_ACP_PI_COMMAND, args, {
99
+ stdio: "inherit",
100
+ env: { ...process.env, ...runtime, ...bridge ? { HAPPY_TERMINAL_MCP_URL: bridge.url } : {} },
101
+ shell: process.platform === "win32"
102
+ });
103
+ const interrupt = () => {
104
+ child.kill("SIGINT");
105
+ };
106
+ const terminate = () => {
107
+ child.kill("SIGTERM");
108
+ };
109
+ const cleanup = () => {
110
+ process.off("SIGINT", interrupt);
111
+ process.off("SIGTERM", terminate);
112
+ };
113
+ process.on("SIGINT", interrupt);
114
+ process.on("SIGTERM", terminate);
115
+ child.once("error", (error) => {
116
+ cleanup();
117
+ reject(new Error(`Could not launch native pi: ${error.message}`));
118
+ });
119
+ child.once("exit", (code, signal) => {
120
+ cleanup();
121
+ resolve(code ?? (signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1));
122
+ });
123
+ });
124
+ } finally {
125
+ await bridge?.stop();
126
+ }
127
+ }
128
+
129
+ export { runPiTerminal, startPiTerminalTools };
@@ -0,0 +1,10 @@
1
+ import { dirname, resolve } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
5
+ function projectPath() {
6
+ const path = resolve(__dirname$1, "..");
7
+ return path;
8
+ }
9
+
10
+ export { projectPath as p };
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var path = require('path');
4
+ var url = require('url');
5
+
6
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
7
+ const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('projectPath-ChLhdOyt.cjs', document.baseURI).href))));
8
+ function projectPath() {
9
+ const path$1 = path.resolve(__dirname$1, "..");
10
+ return path$1;
11
+ }
12
+
13
+ exports.projectPath = projectPath;