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
@@ -3,13 +3,14 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var api = require('./types-Bk3W89Z3.cjs');
7
- var persistence = require('./limits-DbLWXlJm.cjs');
6
+ var api = require('./types-6__wqmmP.cjs');
7
+ var persistence = require('./limits-B3oVgqBJ.cjs');
8
8
  var spawn = require('cross-spawn');
9
9
  var path = require('node:path');
10
10
  var node_readline = require('node:readline');
11
11
  var node_fs = require('node:fs');
12
- var agentGuidance = require('./agentGuidance-B9ENxSaS.cjs');
12
+ var projectPath = require('./projectPath-ChLhdOyt.cjs');
13
+ var agentGuidance = require('./agentGuidance-CJSl54ua.cjs');
13
14
  var promises = require('node:fs/promises');
14
15
  var fs = require('fs/promises');
15
16
  var ink = require('ink');
@@ -505,7 +506,7 @@ class ExitCodeError extends Error {
505
506
  this.exitCode = exitCode;
506
507
  }
507
508
  }
508
- const claudeCliPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_local_launcher.cjs"));
509
+ const claudeCliPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_local_launcher.cjs"));
509
510
  function quoteShellArg(value) {
510
511
  return `'${value.replace(/'/g, `'\\''`)}'`;
511
512
  }
@@ -4851,8 +4852,8 @@ function createSessionMetadata(opts) {
4851
4852
  ...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
4852
4853
  homeDir: os.homedir(),
4853
4854
  happyHomeDir: persistence.configuration.happyHomeDir,
4854
- happyLibDir: api.projectPath(),
4855
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
4855
+ happyLibDir: projectPath.projectPath(),
4856
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
4856
4857
  startedFromDaemon: opts.startedBy === "daemon",
4857
4858
  hostPid: process.pid,
4858
4859
  startedBy: opts.startedBy || "terminal",
@@ -5252,7 +5253,7 @@ ${typeLabels[type] || type}:`));
5252
5253
  console.log(chalk.yellow("No log files found"));
5253
5254
  }
5254
5255
  console.log(chalk.bold("\n\u{1F527} Daemon Spawn Diagnostics"));
5255
- const projectRoot = api.projectPath();
5256
+ const projectRoot = projectPath.projectPath();
5256
5257
  const wrapperPath = path.join(projectRoot, "bin", "very-happy.mjs");
5257
5258
  const cliEntrypoint = path.join(projectRoot, "dist", "index.mjs");
5258
5259
  console.log(`Project Root: ${chalk.blue(projectRoot)}`);
@@ -5857,7 +5858,7 @@ function getRuntime() {
5857
5858
  const isBun = () => getRuntime() === "bun";
5858
5859
 
5859
5860
  function spawnHappyCLI(args, options = {}) {
5860
- const projectRoot = api.projectPath();
5861
+ const projectRoot = projectPath.projectPath();
5861
5862
  const entrypoint = path.join(projectRoot, "dist", "index.mjs");
5862
5863
  let directory;
5863
5864
  if ("cwd" in options) {
@@ -6104,6 +6105,83 @@ function isAuthorizedDaemonControlRequest(authorization, controlToken) {
6104
6105
  return configuredTokenIsUsable && node_crypto.timingSafeEqual(expectedDigest, candidateDigest);
6105
6106
  }
6106
6107
 
6108
+ const DENIED_HOME_DIRS = [
6109
+ ".secrets",
6110
+ ".ssh",
6111
+ ".gnupg",
6112
+ ".aws",
6113
+ ".kube",
6114
+ ".config/rbw"
6115
+ ];
6116
+ const DENIED_HOME_FILES = [
6117
+ ".claude.json",
6118
+ ".claude/.credentials.json",
6119
+ ".netrc",
6120
+ ".pgpass"
6121
+ ];
6122
+ const DENIED_BASENAME_PATTERNS = [
6123
+ /^\.env$/i,
6124
+ /^\.env\./i,
6125
+ /\.env$/i,
6126
+ /\.pem$/i,
6127
+ /\.key$/i,
6128
+ /\.p12$/i,
6129
+ /\.pfx$/i,
6130
+ /^id_(rsa|dsa|ecdsa|ed25519)$/i,
6131
+ /^\.htpasswd$/i
6132
+ ];
6133
+ const DENIED_PATH_SUFFIXES = [
6134
+ `${path.sep}.git${path.sep}config`
6135
+ ];
6136
+ function expandHome(input, home) {
6137
+ if (input === "~") return home;
6138
+ if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
6139
+ return path.resolve(home, input.slice(2));
6140
+ }
6141
+ return input;
6142
+ }
6143
+ function basenameOf(p) {
6144
+ const parts = p.split(path.sep);
6145
+ return parts[parts.length - 1] ?? "";
6146
+ }
6147
+ function isUnder(child, parent) {
6148
+ return child === parent || child.startsWith(parent.endsWith(path.sep) ? parent : parent + path.sep);
6149
+ }
6150
+ function checkPreviewPath(input, opts) {
6151
+ const home = os.homedir();
6152
+ const cwd = process.cwd();
6153
+ if (typeof input !== "string" || !input.trim()) {
6154
+ return { resolved: "", deniedReason: "path must be a non-empty string" };
6155
+ }
6156
+ if (input.includes("\0")) {
6157
+ return { resolved: "", deniedReason: "path contains a NUL byte" };
6158
+ }
6159
+ const expanded = expandHome(input.trim(), home);
6160
+ const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
6161
+ for (const dir of DENIED_HOME_DIRS) {
6162
+ if (isUnder(resolved, path.resolve(home, dir))) {
6163
+ return { resolved, deniedReason: `refusing to preview anything under ~/${dir} (credential material)` };
6164
+ }
6165
+ }
6166
+ for (const file of DENIED_HOME_FILES) {
6167
+ if (resolved === path.resolve(home, file)) {
6168
+ return { resolved, deniedReason: `refusing to preview ~/${file} (credential material)` };
6169
+ }
6170
+ }
6171
+ const base = basenameOf(resolved);
6172
+ for (const pattern of DENIED_BASENAME_PATTERNS) {
6173
+ if (pattern.test(base)) {
6174
+ return { resolved, deniedReason: `refusing to preview "${base}" (looks like credential material)` };
6175
+ }
6176
+ }
6177
+ for (const suffix of DENIED_PATH_SUFFIXES) {
6178
+ if (resolved.endsWith(suffix)) {
6179
+ return { resolved, deniedReason: `refusing to preview ${suffix} (may contain credentials)` };
6180
+ }
6181
+ }
6182
+ return { resolved, deniedReason: null };
6183
+ }
6184
+
6107
6185
  function startDaemonControlServer({
6108
6186
  controlToken,
6109
6187
  getChildren,
@@ -6115,6 +6193,7 @@ function startDaemonControlServer({
6115
6193
  onClaudeAuthFailed,
6116
6194
  onSessionTurnEvent,
6117
6195
  pushClipboard,
6196
+ pushFilePreview,
6118
6197
  onTerminalHook,
6119
6198
  setTerminalTitle
6120
6199
  }) {
@@ -6329,7 +6408,8 @@ function startDaemonControlServer({
6329
6408
  typed.post("/clipboard", {
6330
6409
  schema: {
6331
6410
  body: z.z.object({
6332
- text: z.z.string()
6411
+ text: z.z.string(),
6412
+ terminalId: z.z.string().regex(/^[a-zA-Z0-9_-]{1,64}$/).optional()
6333
6413
  }),
6334
6414
  response: {
6335
6415
  200: z.z.object({
@@ -6341,9 +6421,25 @@ function startDaemonControlServer({
6341
6421
  }
6342
6422
  }
6343
6423
  }, async (request) => {
6344
- const { text } = request.body;
6424
+ const { text, terminalId } = request.body;
6345
6425
  persistence.logger.debug(`[CONTROL SERVER] Clipboard push request (${text.length} chars)`);
6346
- return pushClipboard(text);
6426
+ return terminalId ? pushClipboard(text, terminalId) : pushClipboard(text);
6427
+ });
6428
+ typed.post("/file-preview", {
6429
+ schema: {
6430
+ body: z.z.object({
6431
+ terminalId: z.z.string().regex(/^[a-zA-Z0-9_-]{1,64}$/),
6432
+ path: z.z.string().min(1),
6433
+ mode: z.z.enum(["file", "diff"]).optional()
6434
+ }),
6435
+ response: { 200: z.z.object({ delivered: z.z.boolean(), error: z.z.string().optional() }) }
6436
+ }
6437
+ }, async (request) => {
6438
+ const { terminalId, path, mode } = request.body;
6439
+ const verdict = checkPreviewPath(path);
6440
+ if (verdict.deniedReason) return { delivered: false, error: verdict.deniedReason };
6441
+ if (!pushFilePreview) return { delivered: false, error: "daemon is still starting up" };
6442
+ return pushFilePreview(terminalId, verdict.resolved, mode ?? "file");
6347
6443
  });
6348
6444
  typed.post("/terminal-title", {
6349
6445
  schema: {
@@ -7892,7 +7988,7 @@ function parseJsonObject(text) {
7892
7988
  return null;
7893
7989
  }
7894
7990
  }
7895
- function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-ChWePQEd.cjs', document.baseURI).href))).resolve(s)) {
7991
+ function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-C510dSUn.cjs', document.baseURI).href))).resolve(s)) {
7896
7992
  const platformArch = `${platform}-${arch}`;
7897
7993
  const candidates = [
7898
7994
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -8605,8 +8701,8 @@ function createMirrorManager(deps) {
8605
8701
  machineId: deps.machineId,
8606
8702
  homeDir: os.homedir(),
8607
8703
  happyHomeDir: persistence.configuration.happyHomeDir,
8608
- happyLibDir: api.projectPath(),
8609
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
8704
+ happyLibDir: projectPath.projectPath(),
8705
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
8610
8706
  startedBy: "terminal",
8611
8707
  lifecycleState: "running",
8612
8708
  lifecycleStateSince: Date.now(),
@@ -9066,14 +9162,15 @@ On restart inspect authoritative state and messages before taking new actions. D
9066
9162
  `;
9067
9163
  const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
9068
9164
  export default async function(pi) {
9069
- const endpoint = process.env.HAPPY_MCP_URL;
9165
+ const managed = !!process.env.HAPPY_MCP_URL;
9166
+ const endpoint = process.env.HAPPY_MCP_URL || process.env.HAPPY_TERMINAL_MCP_URL;
9070
9167
  if (!endpoint) return;
9071
9168
  const url = new URL(endpoint);
9072
9169
  if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
9073
9170
  const {readFile} = await import('node:fs/promises');
9074
9171
  const {join} = await import('node:path');
9075
9172
  const {homedir} = await import('node:os');
9076
- const contextEndpoint = process.env.HAPPY_CONTEXT_USAGE_URL;
9173
+ const contextEndpoint = managed && process.env.HAPPY_CONTEXT_USAGE_URL;
9077
9174
  if (contextEndpoint) {
9078
9175
  const target = new URL(contextEndpoint);
9079
9176
  if (target.origin !== url.origin || !target.pathname.startsWith('/runtime-context/')) throw new Error('Very Happy context must be local');
@@ -9091,7 +9188,7 @@ export default async function(pi) {
9091
9188
  });
9092
9189
  }
9093
9190
  }
9094
- pi.on('tool_call', async (event, ctx) => {
9191
+ if (managed) pi.on('tool_call', async (event, ctx) => {
9095
9192
  let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
9096
9193
  const sid = process.env.HAPPY_SESSION_ID;
9097
9194
  if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
@@ -9130,7 +9227,11 @@ export default async function(pi) {
9130
9227
  const available = await rpc('tools/list', {});
9131
9228
  for (const tool of available.tools || []) {
9132
9229
  pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
9133
- async execute(_id, args) { const result = await rpc('tools/call', {name:tool.name,arguments:args}); return {content:result.content,details:{isError:!!result.isError}}; }
9230
+ async execute(_id, args) {
9231
+ const result = await rpc('tools/call', {name:tool.name,arguments:args});
9232
+ if (result.isError) throw new Error((result.content || []).filter(block => block.type === 'text').map(block => block.text).join('\\n') || 'Very Happy tool failed');
9233
+ return {content:result.content,details:{isError:false}};
9234
+ }
9134
9235
  });
9135
9236
  }
9136
9237
  }
@@ -9937,6 +10038,10 @@ Use the teams tools to inspect current task state before acting. This message is
9937
10038
  } };
9938
10039
  }
9939
10040
 
10041
+ function daemonExitCode(source) {
10042
+ return source === "exception" ? 1 : 0;
10043
+ }
10044
+
9940
10045
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9941
10046
  function sanitizeImportTitle(value) {
9942
10047
  if (typeof value !== "string") return null;
@@ -9954,7 +10059,7 @@ const initialMachineMetadata = {
9954
10059
  happyCliVersion: persistence.packageJson.version,
9955
10060
  homeDir: os$1.homedir(),
9956
10061
  happyHomeDir: persistence.configuration.happyHomeDir,
9957
- happyLibDir: api.projectPath(),
10062
+ happyLibDir: projectPath.projectPath(),
9958
10063
  cliAvailability: startupCliAvailability,
9959
10064
  resumeSupport: { ...api.detectResumeSupport(), rpcAvailable: true }
9960
10065
  };
@@ -10016,9 +10121,9 @@ async function startDaemon() {
10016
10121
  requestShutdown = (source, errorMessage) => {
10017
10122
  persistence.logger.debug(`[DAEMON RUN] Requesting shutdown (source: ${source}, errorMessage: ${errorMessage})`);
10018
10123
  setTimeout(async () => {
10019
- persistence.logger.debug("[DAEMON RUN] Startup malfunctioned, forcing exit with code 1");
10124
+ persistence.logger.debug(`[DAEMON RUN] Shutdown did not complete in time, forcing exit with code ${daemonExitCode(source)}`);
10020
10125
  await new Promise((resolve2) => setTimeout(resolve2, 100));
10021
- process.exit(1);
10126
+ process.exit(daemonExitCode(source));
10022
10127
  }, 1e3);
10023
10128
  resolve({ source, errorMessage });
10024
10129
  };
@@ -10402,7 +10507,7 @@ async function startDaemon() {
10402
10507
  const sessionDesc = tmuxSessionName || "current/most recent session";
10403
10508
  persistence.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
10404
10509
  const tmux = getTmuxUtilities(tmuxSessionName);
10405
- const cliPath = path$1.join(api.projectPath(), "dist", "index.mjs");
10510
+ const cliPath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
10406
10511
  const agent = options.agent ?? "claude";
10407
10512
  if (!isSpawnAgent(agent)) {
10408
10513
  return {
@@ -11006,11 +11111,15 @@ async function startDaemon() {
11006
11111
  onSessionStateEvent,
11007
11112
  onClaudeAuthFailed: (sessionId) => claudeAuthServiceRef?.signalAuthFailed(sessionId),
11008
11113
  onSessionTurnEvent: (sessionId, event) => turnActivity.apply(sessionId, event),
11009
- pushClipboard: (text) => {
11114
+ pushClipboard: (text, terminalId) => {
11010
11115
  if (!apiMachineRef) {
11011
11116
  return { delivered: false, truncated: false, totalBytes: 0, error: "daemon is still starting up" };
11012
11117
  }
11013
- return apiMachineRef.pushClipboard(text);
11118
+ return apiMachineRef.pushClipboard(text, terminalId);
11119
+ },
11120
+ pushFilePreview: (terminalId, path, mode) => {
11121
+ if (!apiMachineRef) return { delivered: false, error: "daemon is still starting up" };
11122
+ return apiMachineRef.pushFilePreview(terminalId, path, mode);
11014
11123
  },
11015
11124
  onTerminalHook: (body) => {
11016
11125
  mirrorManagerRef?.handleHookPayload(body);
@@ -11034,7 +11143,7 @@ async function startDaemon() {
11034
11143
  };
11035
11144
  persistence.writeDaemonState(fileState);
11036
11145
  persistence.logger.debug("[DAEMON RUN] Daemon state written");
11037
- const bundlePath = path$1.join(api.projectPath(), "dist", "index.mjs");
11146
+ const bundlePath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
11038
11147
  let initialBundleMtimeMs = 0;
11039
11148
  try {
11040
11149
  initialBundleMtimeMs = fs$1.statSync(bundlePath).mtimeMs;
@@ -11090,7 +11199,7 @@ async function startDaemon() {
11090
11199
  });
11091
11200
  const claudeAuthService = new ClaudeAuthService({
11092
11201
  happyHomeDir: persistence.configuration.happyHomeDir,
11093
- happyLibDir: api.projectPath(),
11202
+ happyLibDir: projectPath.projectPath(),
11094
11203
  credentialSource: daemonClaudeCredentials.source,
11095
11204
  getCredentialStore: () => claudeCredentialStoreSetting,
11096
11205
  setCredentialStore: async (store) => {
@@ -11272,8 +11381,8 @@ async function startDaemon() {
11272
11381
  await api.cleanupDaemonState();
11273
11382
  await stopCaffeinate();
11274
11383
  await persistence.releaseDaemonLock(daemonLockHandle);
11275
- persistence.logger.debug("[DAEMON RUN] Cleanup completed, exiting process");
11276
- process.exit(0);
11384
+ persistence.logger.debug(`[DAEMON RUN] Cleanup completed, exiting process with code ${daemonExitCode(source)}`);
11385
+ process.exit(daemonExitCode(source));
11277
11386
  };
11278
11387
  persistence.logger.debug("[DAEMON RUN] Daemon started successfully, waiting for shutdown request");
11279
11388
  const shutdownRequest = await resolvesWhenShutdownRequested;
@@ -11747,83 +11856,6 @@ function normalizeProgress(value) {
11747
11856
  return flat.length > PROGRESS_MAX_CHARS$1 ? flat.slice(0, PROGRESS_MAX_CHARS$1) : flat;
11748
11857
  }
11749
11858
 
11750
- const DENIED_HOME_DIRS = [
11751
- ".secrets",
11752
- ".ssh",
11753
- ".gnupg",
11754
- ".aws",
11755
- ".kube",
11756
- ".config/rbw"
11757
- ];
11758
- const DENIED_HOME_FILES = [
11759
- ".claude.json",
11760
- ".claude/.credentials.json",
11761
- ".netrc",
11762
- ".pgpass"
11763
- ];
11764
- const DENIED_BASENAME_PATTERNS = [
11765
- /^\.env$/i,
11766
- /^\.env\./i,
11767
- /\.env$/i,
11768
- /\.pem$/i,
11769
- /\.key$/i,
11770
- /\.p12$/i,
11771
- /\.pfx$/i,
11772
- /^id_(rsa|dsa|ecdsa|ed25519)$/i,
11773
- /^\.htpasswd$/i
11774
- ];
11775
- const DENIED_PATH_SUFFIXES = [
11776
- `${path.sep}.git${path.sep}config`
11777
- ];
11778
- function expandHome(input, home) {
11779
- if (input === "~") return home;
11780
- if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
11781
- return path.resolve(home, input.slice(2));
11782
- }
11783
- return input;
11784
- }
11785
- function basenameOf(p) {
11786
- const parts = p.split(path.sep);
11787
- return parts[parts.length - 1] ?? "";
11788
- }
11789
- function isUnder(child, parent) {
11790
- return child === parent || child.startsWith(parent.endsWith(path.sep) ? parent : parent + path.sep);
11791
- }
11792
- function checkPreviewPath(input, opts) {
11793
- const home = os.homedir();
11794
- const cwd = process.cwd();
11795
- if (typeof input !== "string" || !input.trim()) {
11796
- return { resolved: "", deniedReason: "path must be a non-empty string" };
11797
- }
11798
- if (input.includes("\0")) {
11799
- return { resolved: "", deniedReason: "path contains a NUL byte" };
11800
- }
11801
- const expanded = expandHome(input.trim(), home);
11802
- const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
11803
- for (const dir of DENIED_HOME_DIRS) {
11804
- if (isUnder(resolved, path.resolve(home, dir))) {
11805
- return { resolved, deniedReason: `refusing to preview anything under ~/${dir} (credential material)` };
11806
- }
11807
- }
11808
- for (const file of DENIED_HOME_FILES) {
11809
- if (resolved === path.resolve(home, file)) {
11810
- return { resolved, deniedReason: `refusing to preview ~/${file} (credential material)` };
11811
- }
11812
- }
11813
- const base = basenameOf(resolved);
11814
- for (const pattern of DENIED_BASENAME_PATTERNS) {
11815
- if (pattern.test(base)) {
11816
- return { resolved, deniedReason: `refusing to preview "${base}" (looks like credential material)` };
11817
- }
11818
- }
11819
- for (const suffix of DENIED_PATH_SUFFIXES) {
11820
- if (resolved.endsWith(suffix)) {
11821
- return { resolved, deniedReason: `refusing to preview ${suffix} (may contain credentials)` };
11822
- }
11823
- }
11824
- return { resolved, deniedReason: null };
11825
- }
11826
-
11827
11859
  function createMcpServer(handlers, options, sessionId) {
11828
11860
  const mcp = new mcp_js.McpServer({
11829
11861
  name: "Very Happy Tools",
@@ -11875,7 +11907,7 @@ function createMcpServer(handlers, options, sessionId) {
11875
11907
  return {
11876
11908
  content: [{
11877
11909
  type: "text",
11878
- 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.`
11910
+ 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.`
11879
11911
  }],
11880
11912
  isError: false
11881
11913
  };
@@ -12171,7 +12203,7 @@ function generateHookSettingsFile(port) {
12171
12203
  node_fs.mkdirSync(hooksDir, { recursive: true });
12172
12204
  const filename = `session-hook-${process.pid}.json`;
12173
12205
  const filepath = path.join(hooksDir, filename);
12174
- const forwarderScript = path.resolve(api.projectPath(), "scripts", "session_hook_forwarder.cjs");
12206
+ const forwarderScript = path.resolve(projectPath.projectPath(), "scripts", "session_hook_forwarder.cjs");
12175
12207
  const hookCommand = `node "${forwarderScript}" ${port}`;
12176
12208
  const settings = {
12177
12209
  hooks: {
@@ -12344,7 +12376,7 @@ function registerSideQuestionHandler(rpc, deps) {
12344
12376
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12345
12377
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12346
12378
  const run = deps.run ?? (async (input) => {
12347
- const { query } = await Promise.resolve().then(function () { return require('./index-BSVgut_Y.cjs'); });
12379
+ const { query } = await Promise.resolve().then(function () { return require('./index-BREtikhm.cjs'); });
12348
12380
  return runSideQuestion(query, input);
12349
12381
  });
12350
12382
  const slots = /* @__PURE__ */ new Map();
@@ -12446,8 +12478,8 @@ const MAX_TITLE_CHARS = 60;
12446
12478
  const GENERATION_TIMEOUT_MS = 3e4;
12447
12479
  function resolveClaudeBinary() {
12448
12480
  try {
12449
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-ChWePQEd.cjs', document.baseURI).href)));
12450
- const utilsPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_version_utils.cjs"));
12481
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-C510dSUn.cjs', document.baseURI).href)));
12482
+ const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
12451
12483
  const { getClaudeCliPath } = require$1(utilsPath);
12452
12484
  const path$1 = getClaudeCliPath();
12453
12485
  if (typeof path$1 === "string" && path$1.length > 0) return path$1;
@@ -12893,8 +12925,8 @@ async function runClaude(credentials, options = {}) {
12893
12925
  machineId,
12894
12926
  homeDir: os.homedir(),
12895
12927
  happyHomeDir: persistence.configuration.happyHomeDir,
12896
- happyLibDir: api.projectPath(),
12897
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
12928
+ happyLibDir: projectPath.projectPath(),
12929
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
12898
12930
  startedFromDaemon: options.startedBy === "daemon",
12899
12931
  hostPid: process.pid,
12900
12932
  startedBy: options.startedBy || "terminal",
@@ -14672,9 +14704,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
14672
14704
  };
14673
14705
  }
14674
14706
 
14675
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-ChWePQEd.cjs', document.baseURI).href)));
14707
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-C510dSUn.cjs', document.baseURI).href)));
14676
14708
  const __dirname$1 = path.dirname(__filename$1);
14677
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-ChWePQEd.cjs', document.baseURI).href)));
14709
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-C510dSUn.cjs', document.baseURI).href)));
14678
14710
  const PRISMA_QUERY_ENGINE_FILES = {
14679
14711
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
14680
14712
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -17172,7 +17204,7 @@ async function runCodex(opts) {
17172
17204
  }
17173
17205
  });
17174
17206
  const happyServer = await startHappyServer(session);
17175
- const bridgeEntrypoint = path.join(api.projectPath(), "bin", "very-happy-mcp.mjs");
17207
+ const bridgeEntrypoint = path.join(projectPath.projectPath(), "bin", "very-happy-mcp.mjs");
17176
17208
  const mcpServers = {
17177
17209
  happy: {
17178
17210
  command: process.execPath,
@@ -17578,9 +17610,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17578
17610
  } else if (subcommand === "bye") {
17579
17611
  console.log("Bye!");
17580
17612
  process.exit(0);
17613
+ } else if (subcommand === "install-pi-tools") {
17614
+ try {
17615
+ const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-BKnXjrVu.cjs'); });
17616
+ await handleInstallPiTools(args.slice(1));
17617
+ } catch (error) {
17618
+ console.error(error instanceof Error ? error.message : "Pi tools installation failed");
17619
+ process.exitCode = 1;
17620
+ }
17621
+ return;
17581
17622
  } else if (subcommand === "install-terminal-hooks") {
17582
17623
  try {
17583
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks--jkP17uE.cjs'); });
17624
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-C_jraM1j.cjs'); });
17584
17625
  const command = parseTerminalHooksArgs(args.slice(1));
17585
17626
  if (command.action === "help") {
17586
17627
  console.log(TERMINAL_HOOKS_HELP);
@@ -17597,7 +17638,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17597
17638
  }
17598
17639
  } else if (subcommand === "spawn") {
17599
17640
  try {
17600
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CdZgvthE.cjs'); });
17641
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-DIyFOn3W.cjs'); });
17601
17642
  await handleSpawnCommand(args.slice(1));
17602
17643
  } catch (error) {
17603
17644
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17609,7 +17650,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17609
17650
  return;
17610
17651
  } else if (subcommand === "sessions") {
17611
17652
  try {
17612
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-Jt9lrcrG.cjs'); });
17653
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-DyQXMN-8.cjs'); });
17613
17654
  await handleSessionsCommand(args.slice(1));
17614
17655
  } catch (error) {
17615
17656
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17621,7 +17662,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17621
17662
  return;
17622
17663
  } else if (subcommand === "send") {
17623
17664
  try {
17624
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-BGcCrMUT.cjs'); });
17665
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DGVwmB-p.cjs'); });
17625
17666
  await handleSendCommand(args.slice(1));
17626
17667
  } catch (error) {
17627
17668
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17727,9 +17768,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17727
17768
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
17728
17769
  const projectId = args[3];
17729
17770
  try {
17730
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-Dn7nAHcA.cjs'); });
17731
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DbLWXlJm.cjs'); }).then(function (n) { return n.persistence; });
17732
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-Bk3W89Z3.cjs'); }).then(function (n) { return n.api; });
17771
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-CfqWoHmO.cjs'); });
17772
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-B3oVgqBJ.cjs'); }).then(function (n) { return n.persistence; });
17773
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-6__wqmmP.cjs'); }).then(function (n) { return n.api; });
17733
17774
  let userEmail = void 0;
17734
17775
  try {
17735
17776
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -17760,7 +17801,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17760
17801
  }
17761
17802
  if (geminiSubcommand === "project" && args[2] === "get") {
17762
17803
  try {
17763
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-Dn7nAHcA.cjs'); });
17804
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-CfqWoHmO.cjs'); });
17764
17805
  const config = readGeminiLocalConfig();
17765
17806
  if (config.googleCloudProject) {
17766
17807
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17800,7 +17841,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17800
17841
  process.exit(0);
17801
17842
  }
17802
17843
  try {
17803
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-CxUz95A1.cjs'); });
17844
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-C6qMFzcn.cjs'); });
17804
17845
  let startedBy = void 0;
17805
17846
  for (let i = 1; i < args.length; i++) {
17806
17847
  if (args[i] === "--started-by") {
@@ -17821,6 +17862,16 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17821
17862
  }
17822
17863
  return;
17823
17864
  } else if (subcommand === "pi") {
17865
+ if (args[1] === "--terminal") {
17866
+ try {
17867
+ const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-Dyc_9pGP.cjs'); });
17868
+ process.exitCode = await runPiTerminal(args.slice(2));
17869
+ } catch (error) {
17870
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
17871
+ process.exitCode = 1;
17872
+ }
17873
+ return;
17874
+ }
17824
17875
  let hintPrinted = false;
17825
17876
  const printHint = (detail) => {
17826
17877
  const hint = piAdapterMissingHint(detail);
@@ -17830,7 +17881,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17830
17881
  }
17831
17882
  };
17832
17883
  try {
17833
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-3STVDtNO.cjs'); });
17884
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-Bj68y6ee.cjs'); });
17834
17885
  const parsed = parsePiRunnerArgs(args.slice(1));
17835
17886
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17836
17887
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17858,7 +17909,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17858
17909
  return;
17859
17910
  } else if (subcommand === "acp") {
17860
17911
  try {
17861
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-3STVDtNO.cjs'); });
17912
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-Bj68y6ee.cjs'); });
17862
17913
  let startedBy = void 0;
17863
17914
  let verbose = false;
17864
17915
  const acpArgs = [];
@@ -17898,7 +17949,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17898
17949
  return;
17899
17950
  } else if (subcommand === "openclaw") {
17900
17951
  try {
17901
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-RLIFJx61.cjs'); });
17952
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-C2_aCP7v.cjs'); });
17902
17953
  let startedBy = void 0;
17903
17954
  let verbose = false;
17904
17955
  let gatewayUrl;
@@ -17949,7 +18000,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17949
18000
  return;
17950
18001
  } else if (subcommand === "todo") {
17951
18002
  try {
17952
- const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-D9nFaYpQ.cjs'); });
18003
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-Du6pjwx_.cjs'); });
17953
18004
  await handleTodoCommand(args.slice(1));
17954
18005
  } catch (error) {
17955
18006
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -17958,17 +18009,17 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17958
18009
  return;
17959
18010
  } else if (subcommand === "teams") {
17960
18011
  try {
17961
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-DfVwVZ4x.cjs'); });
18012
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-QOkGFjkJ.cjs'); });
17962
18013
  await handleTeamsCommand(args.slice(1));
17963
18014
  } catch (error) {
17964
18015
  console.error(error instanceof Error ? error.message : "Teams command failed");
17965
18016
  process.exitCode = 1;
17966
18017
  }
17967
18018
  return;
17968
- } else if (subcommand === "mcp" && args.length === 1) {
18019
+ } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
17969
18020
  try {
17970
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-kE0n1dBF.cjs'); });
17971
- await handleMcpCommand();
18021
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-G-3lL_KH.cjs'); });
18022
+ await handleMcpCommand(args[1] === "--terminal-tools");
17972
18023
  } catch (error) {
17973
18024
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
17974
18025
  `);
@@ -18188,6 +18239,10 @@ ${chalk.bold("Usage:")}
18188
18239
  very-happy codex Start Codex mode
18189
18240
  very-happy gemini Start Gemini mode (ACP)
18190
18241
  very-happy pi Start pi mode (ACP via the pi-acp adapter)
18242
+ very-happy pi --terminal Run native pi with title, clipboard and preview tools
18243
+ inside a Very Happy terminal
18244
+ very-happy install-pi-tools Install tools for directly launched pi sessions
18245
+ (add --remove to uninstall)
18191
18246
  very-happy acp Start a generic ACP-compatible agent
18192
18247
  very-happy openclaw Connect through a configured OpenClaw gateway
18193
18248
  very-happy install-terminal-hooks
@@ -18349,6 +18404,7 @@ exports.TEAM_SKILL = TEAM_SKILL;
18349
18404
  exports.TitleGenerator = TitleGenerator;
18350
18405
  exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
18351
18406
  exports.archiveSession = archiveSession;
18407
+ exports.checkPreviewPath = checkPreviewPath;
18352
18408
  exports.createSessionMetadata = createSessionMetadata;
18353
18409
  exports.hashObject = hashObject;
18354
18410
  exports.initialMachineMetadata = initialMachineMetadata;