very-happy-cli 0.2.140 → 0.2.142

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-DsSYq8T-.mjs → AcpBackend-D2LqY2to.mjs} +2 -2
  2. package/dist/{AcpBackend-C0Lq4uoA.cjs → AcpBackend-DJsqfgjD.cjs} +2 -2
  3. package/dist/{AcpSessionManager-DIqByTH7.cjs → AcpSessionManager-CTfDxziP.cjs} +1 -1
  4. package/dist/{AcpSessionManager-BAErE9s0.mjs → AcpSessionManager-CdeM5KD9.mjs} +1 -1
  5. package/dist/{agentGuidance-Dl3apItH.cjs → agentGuidance-Bi8iBjy3.cjs} +1 -1
  6. package/dist/{agentGuidance-xk3v4_7E.mjs → agentGuidance-Cz8QVcX3.mjs} +1 -1
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-D0-jnCOC.cjs → config-BIbYBmTd.cjs} +5 -4
  10. package/dist/{config-BVoHul9V.mjs → config-ZsBeq10u.mjs} +5 -4
  11. package/dist/{index-DJvzGXVN.cjs → index-BPTPrjhL.cjs} +12 -40
  12. package/dist/{index-BuQn9kUK.cjs → index-Bom8mTuS.cjs} +5 -4
  13. package/dist/{index-BPnAbcz_.mjs → index-CNARLBax.mjs} +5 -4
  14. package/dist/{index-__h-pCTK.cjs → index-CSQZhQ0C.cjs} +198 -129
  15. package/dist/{index-BNydKYvS.mjs → index-CY0zmhIp.mjs} +11 -39
  16. package/dist/{index-D0IP7gXG.mjs → index-CYFendNf.mjs} +179 -111
  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-D2ice1a-.mjs → installTerminalHooks-BPJu2WVz.mjs} +8 -7
  22. package/dist/{installTerminalHooks-D3jfvbyY.cjs → installTerminalHooks-DA7SyFYW.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-Dbip6eVO.cjs → limits-B3hngubn.cjs} +29 -10
  28. package/dist/{limits-nhD0Wran.mjs → limits-CHsz3Ba-.mjs} +29 -11
  29. package/dist/{mcp-V26C05fN.mjs → mcp-BkWBRWFe.mjs} +28 -14
  30. package/dist/{mcp-DeapmTWN.cjs → mcp-ySrfNOYl.cjs} +29 -15
  31. package/dist/piExtension.d.mts +17 -0
  32. package/dist/piExtension.mjs +84 -0
  33. package/dist/piRuntime-DBLUyXFr.mjs +37 -0
  34. package/dist/piRuntime-uX0WHVr4.cjs +39 -0
  35. package/dist/piTerminal-Bc3jSCnY.mjs +129 -0
  36. package/dist/piTerminal-DNr3hkzY.cjs +132 -0
  37. package/dist/projectPath-CSpqWdxp.mjs +10 -0
  38. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  39. package/dist/{runGemini-D34eCFpz.cjs → runGemini-eJ08cXk7.cjs} +9 -8
  40. package/dist/{runGemini-sa5kAgPe.mjs → runGemini-rgFHTnG7.mjs} +8 -7
  41. package/dist/{runOpenClaw-B8F8D--R.cjs → runOpenClaw-D0LVzxqM.cjs} +6 -5
  42. package/dist/{runOpenClaw-Bdsq8KBa.mjs → runOpenClaw-GjLWwjas.mjs} +6 -5
  43. package/dist/{send-DP_S3wn0.cjs → send-Bg2oqt7f.cjs} +5 -4
  44. package/dist/{send-KICC_ACz.mjs → send-DV8liaRN.mjs} +5 -4
  45. package/dist/{sessions-CGBBgVZn.cjs → sessions-DPimBZ8H.cjs} +7 -6
  46. package/dist/{sessions-C6EvnC4B.mjs → sessions-JiL4aJkC.mjs} +7 -6
  47. package/dist/{spawn-BhUK0Vt2.cjs → spawn-COOcUwkG.cjs} +5 -4
  48. package/dist/{spawn-C1OqXJNb.mjs → spawn-CnMk3LFG.mjs} +5 -4
  49. package/dist/{teams-C29nEHFD.cjs → teams-DSQgHBLE.cjs} +5 -4
  50. package/dist/{teams-Dm5HGpNz.mjs → teams-uHZcdX9j.mjs} +5 -4
  51. package/dist/terminalToolContext-CFeGeaNm.mjs +7 -0
  52. package/dist/terminalToolContext-DoV9Gs7K.cjs +9 -0
  53. package/dist/{todo-CeJX0z_2.cjs → todo-DeauuUd6.cjs} +4 -3
  54. package/dist/{todo-C-vovA0N.mjs → todo-oHWNpEx_.mjs} +4 -3
  55. package/dist/{types-thyvQB2a.mjs → types-Dnoxgb7G.mjs} +30 -13
  56. package/dist/{types-DCVS050m.cjs → types-Xd_LJRJN.cjs} +31 -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-DCVS050m.cjs');
7
- var persistence = require('./limits-Dbip6eVO.cjs');
6
+ var api = require('./types-Xd_LJRJN.cjs');
7
+ var persistence = require('./limits-B3hngubn.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-Dl3apItH.cjs');
12
+ var projectPath = require('./projectPath-ChLhdOyt.cjs');
13
+ var agentGuidance = require('./agentGuidance-Bi8iBjy3.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
  }
@@ -1304,7 +1305,21 @@ function createRuntimeControls(isThinking) {
1304
1305
  });
1305
1306
  return { operationId: op.id };
1306
1307
  }
1307
- return { setQuery, observe, request };
1308
+ async function stopRunningTasks() {
1309
+ const q = query, runGeneration = generation;
1310
+ if (!q || !q.canControl()) return [];
1311
+ const running = [...tasks.values()].filter((task) => task.status === "running" && text(task.id)).map((task) => String(task.id)).slice(0, 20);
1312
+ const stopped = [];
1313
+ await Promise.all(running.map(async (id) => {
1314
+ try {
1315
+ await q.stopTask(id);
1316
+ if (query === q && generation === runGeneration) stopped.push(id);
1317
+ } catch {
1318
+ }
1319
+ }));
1320
+ return stopped;
1321
+ }
1322
+ return { setQuery, observe, request, stopRunningTasks };
1308
1323
  }
1309
1324
 
1310
1325
  class MessageBuffer {
@@ -3919,6 +3934,9 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
3919
3934
  persistence.logger.debug("[remote]: doAbort");
3920
3935
  session.markAborting();
3921
3936
  session.onAbort();
3937
+ void runtimeControls.stopRunningTasks().then((ids) => {
3938
+ if (ids.length > 0) persistence.logger.debug(`[remote]: stopped ${ids.length} background task(s) on abort: ${ids.join(", ")}`);
3939
+ }).catch((error) => persistence.logger.debug("[remote]: stopping background tasks on abort failed", error));
3922
3940
  if (session.thinking) {
3923
3941
  const interrupted = await turnSteering.interruptTurn().catch((e) => {
3924
3942
  persistence.logger.debug("[remote]: interruptTurn threw, falling back to hard abort", e);
@@ -4834,8 +4852,8 @@ function createSessionMetadata(opts) {
4834
4852
  ...opts.teamOperationId ? { teamOperationId: opts.teamOperationId } : {},
4835
4853
  homeDir: os.homedir(),
4836
4854
  happyHomeDir: persistence.configuration.happyHomeDir,
4837
- happyLibDir: api.projectPath(),
4838
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
4855
+ happyLibDir: projectPath.projectPath(),
4856
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
4839
4857
  startedFromDaemon: opts.startedBy === "daemon",
4840
4858
  hostPid: process.pid,
4841
4859
  startedBy: opts.startedBy || "terminal",
@@ -5235,7 +5253,7 @@ ${typeLabels[type] || type}:`));
5235
5253
  console.log(chalk.yellow("No log files found"));
5236
5254
  }
5237
5255
  console.log(chalk.bold("\n\u{1F527} Daemon Spawn Diagnostics"));
5238
- const projectRoot = api.projectPath();
5256
+ const projectRoot = projectPath.projectPath();
5239
5257
  const wrapperPath = path.join(projectRoot, "bin", "very-happy.mjs");
5240
5258
  const cliEntrypoint = path.join(projectRoot, "dist", "index.mjs");
5241
5259
  console.log(`Project Root: ${chalk.blue(projectRoot)}`);
@@ -5840,7 +5858,7 @@ function getRuntime() {
5840
5858
  const isBun = () => getRuntime() === "bun";
5841
5859
 
5842
5860
  function spawnHappyCLI(args, options = {}) {
5843
- const projectRoot = api.projectPath();
5861
+ const projectRoot = projectPath.projectPath();
5844
5862
  const entrypoint = path.join(projectRoot, "dist", "index.mjs");
5845
5863
  let directory;
5846
5864
  if ("cwd" in options) {
@@ -6087,6 +6105,83 @@ function isAuthorizedDaemonControlRequest(authorization, controlToken) {
6087
6105
  return configuredTokenIsUsable && node_crypto.timingSafeEqual(expectedDigest, candidateDigest);
6088
6106
  }
6089
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
+
6090
6185
  function startDaemonControlServer({
6091
6186
  controlToken,
6092
6187
  getChildren,
@@ -6098,6 +6193,7 @@ function startDaemonControlServer({
6098
6193
  onClaudeAuthFailed,
6099
6194
  onSessionTurnEvent,
6100
6195
  pushClipboard,
6196
+ pushFilePreview,
6101
6197
  onTerminalHook,
6102
6198
  setTerminalTitle
6103
6199
  }) {
@@ -6312,7 +6408,8 @@ function startDaemonControlServer({
6312
6408
  typed.post("/clipboard", {
6313
6409
  schema: {
6314
6410
  body: z.z.object({
6315
- text: z.z.string()
6411
+ text: z.z.string(),
6412
+ terminalId: z.z.string().regex(/^[a-zA-Z0-9_-]{1,64}$/).optional()
6316
6413
  }),
6317
6414
  response: {
6318
6415
  200: z.z.object({
@@ -6324,9 +6421,25 @@ function startDaemonControlServer({
6324
6421
  }
6325
6422
  }
6326
6423
  }, async (request) => {
6327
- const { text } = request.body;
6424
+ const { text, terminalId } = request.body;
6328
6425
  persistence.logger.debug(`[CONTROL SERVER] Clipboard push request (${text.length} chars)`);
6329
- 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");
6330
6443
  });
6331
6444
  typed.post("/terminal-title", {
6332
6445
  schema: {
@@ -7875,7 +7988,7 @@ function parseJsonObject(text) {
7875
7988
  return null;
7876
7989
  }
7877
7990
  }
7878
- 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-__h-pCTK.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-CSQZhQ0C.cjs', document.baseURI).href))).resolve(s)) {
7879
7992
  const platformArch = `${platform}-${arch}`;
7880
7993
  const candidates = [
7881
7994
  `@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
@@ -8588,8 +8701,8 @@ function createMirrorManager(deps) {
8588
8701
  machineId: deps.machineId,
8589
8702
  homeDir: os.homedir(),
8590
8703
  happyHomeDir: persistence.configuration.happyHomeDir,
8591
- happyLibDir: api.projectPath(),
8592
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
8704
+ happyLibDir: projectPath.projectPath(),
8705
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
8593
8706
  startedBy: "terminal",
8594
8707
  lifecycleState: "running",
8595
8708
  lifecycleStateSince: Date.now(),
@@ -9049,14 +9162,15 @@ On restart inspect authoritative state and messages before taking new actions. D
9049
9162
  `;
9050
9163
  const PI_TEAMS_EXTENSION = `// Managed by Very Happy Agent Teams.
9051
9164
  export default async function(pi) {
9052
- 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;
9053
9167
  if (!endpoint) return;
9054
9168
  const url = new URL(endpoint);
9055
9169
  if (!['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.protocol !== 'http:') throw new Error('Very Happy MCP must be local');
9056
9170
  const {readFile} = await import('node:fs/promises');
9057
9171
  const {join} = await import('node:path');
9058
9172
  const {homedir} = await import('node:os');
9059
- const contextEndpoint = process.env.HAPPY_CONTEXT_USAGE_URL;
9173
+ const contextEndpoint = managed && process.env.HAPPY_CONTEXT_USAGE_URL;
9060
9174
  if (contextEndpoint) {
9061
9175
  const target = new URL(contextEndpoint);
9062
9176
  if (target.origin !== url.origin || !target.pathname.startsWith('/runtime-context/')) throw new Error('Very Happy context must be local');
@@ -9074,7 +9188,7 @@ export default async function(pi) {
9074
9188
  });
9075
9189
  }
9076
9190
  }
9077
- pi.on('tool_call', async (event, ctx) => {
9191
+ if (managed) pi.on('tool_call', async (event, ctx) => {
9078
9192
  let mode = process.env.HAPPY_PERMISSION_MODE || 'default';
9079
9193
  const sid = process.env.HAPPY_SESSION_ID;
9080
9194
  if (sid && /^[a-zA-Z0-9_-]+$/.test(sid)) {
@@ -9113,7 +9227,11 @@ export default async function(pi) {
9113
9227
  const available = await rpc('tools/list', {});
9114
9228
  for (const tool of available.tools || []) {
9115
9229
  pi.registerTool({name:tool.name,label:tool.name,description:tool.description || tool.name,parameters:tool.inputSchema,
9116
- 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
+ }
9117
9235
  });
9118
9236
  }
9119
9237
  }
@@ -9937,7 +10055,7 @@ const initialMachineMetadata = {
9937
10055
  happyCliVersion: persistence.packageJson.version,
9938
10056
  homeDir: os$1.homedir(),
9939
10057
  happyHomeDir: persistence.configuration.happyHomeDir,
9940
- happyLibDir: api.projectPath(),
10058
+ happyLibDir: projectPath.projectPath(),
9941
10059
  cliAvailability: startupCliAvailability,
9942
10060
  resumeSupport: { ...api.detectResumeSupport(), rpcAvailable: true }
9943
10061
  };
@@ -10385,7 +10503,7 @@ async function startDaemon() {
10385
10503
  const sessionDesc = tmuxSessionName || "current/most recent session";
10386
10504
  persistence.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
10387
10505
  const tmux = getTmuxUtilities(tmuxSessionName);
10388
- const cliPath = path$1.join(api.projectPath(), "dist", "index.mjs");
10506
+ const cliPath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
10389
10507
  const agent = options.agent ?? "claude";
10390
10508
  if (!isSpawnAgent(agent)) {
10391
10509
  return {
@@ -10989,11 +11107,15 @@ async function startDaemon() {
10989
11107
  onSessionStateEvent,
10990
11108
  onClaudeAuthFailed: (sessionId) => claudeAuthServiceRef?.signalAuthFailed(sessionId),
10991
11109
  onSessionTurnEvent: (sessionId, event) => turnActivity.apply(sessionId, event),
10992
- pushClipboard: (text) => {
11110
+ pushClipboard: (text, terminalId) => {
10993
11111
  if (!apiMachineRef) {
10994
11112
  return { delivered: false, truncated: false, totalBytes: 0, error: "daemon is still starting up" };
10995
11113
  }
10996
- return apiMachineRef.pushClipboard(text);
11114
+ return apiMachineRef.pushClipboard(text, terminalId);
11115
+ },
11116
+ pushFilePreview: (terminalId, path, mode) => {
11117
+ if (!apiMachineRef) return { delivered: false, error: "daemon is still starting up" };
11118
+ return apiMachineRef.pushFilePreview(terminalId, path, mode);
10997
11119
  },
10998
11120
  onTerminalHook: (body) => {
10999
11121
  mirrorManagerRef?.handleHookPayload(body);
@@ -11017,7 +11139,7 @@ async function startDaemon() {
11017
11139
  };
11018
11140
  persistence.writeDaemonState(fileState);
11019
11141
  persistence.logger.debug("[DAEMON RUN] Daemon state written");
11020
- const bundlePath = path$1.join(api.projectPath(), "dist", "index.mjs");
11142
+ const bundlePath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
11021
11143
  let initialBundleMtimeMs = 0;
11022
11144
  try {
11023
11145
  initialBundleMtimeMs = fs$1.statSync(bundlePath).mtimeMs;
@@ -11073,7 +11195,7 @@ async function startDaemon() {
11073
11195
  });
11074
11196
  const claudeAuthService = new ClaudeAuthService({
11075
11197
  happyHomeDir: persistence.configuration.happyHomeDir,
11076
- happyLibDir: api.projectPath(),
11198
+ happyLibDir: projectPath.projectPath(),
11077
11199
  credentialSource: daemonClaudeCredentials.source,
11078
11200
  getCredentialStore: () => claudeCredentialStoreSetting,
11079
11201
  setCredentialStore: async (store) => {
@@ -11730,83 +11852,6 @@ function normalizeProgress(value) {
11730
11852
  return flat.length > PROGRESS_MAX_CHARS$1 ? flat.slice(0, PROGRESS_MAX_CHARS$1) : flat;
11731
11853
  }
11732
11854
 
11733
- const DENIED_HOME_DIRS = [
11734
- ".secrets",
11735
- ".ssh",
11736
- ".gnupg",
11737
- ".aws",
11738
- ".kube",
11739
- ".config/rbw"
11740
- ];
11741
- const DENIED_HOME_FILES = [
11742
- ".claude.json",
11743
- ".claude/.credentials.json",
11744
- ".netrc",
11745
- ".pgpass"
11746
- ];
11747
- const DENIED_BASENAME_PATTERNS = [
11748
- /^\.env$/i,
11749
- /^\.env\./i,
11750
- /\.env$/i,
11751
- /\.pem$/i,
11752
- /\.key$/i,
11753
- /\.p12$/i,
11754
- /\.pfx$/i,
11755
- /^id_(rsa|dsa|ecdsa|ed25519)$/i,
11756
- /^\.htpasswd$/i
11757
- ];
11758
- const DENIED_PATH_SUFFIXES = [
11759
- `${path.sep}.git${path.sep}config`
11760
- ];
11761
- function expandHome(input, home) {
11762
- if (input === "~") return home;
11763
- if (input.startsWith(`~${path.sep}`) || input.startsWith("~/")) {
11764
- return path.resolve(home, input.slice(2));
11765
- }
11766
- return input;
11767
- }
11768
- function basenameOf(p) {
11769
- const parts = p.split(path.sep);
11770
- return parts[parts.length - 1] ?? "";
11771
- }
11772
- function isUnder(child, parent) {
11773
- return child === parent || child.startsWith(parent.endsWith(path.sep) ? parent : parent + path.sep);
11774
- }
11775
- function checkPreviewPath(input, opts) {
11776
- const home = os.homedir();
11777
- const cwd = process.cwd();
11778
- if (typeof input !== "string" || !input.trim()) {
11779
- return { resolved: "", deniedReason: "path must be a non-empty string" };
11780
- }
11781
- if (input.includes("\0")) {
11782
- return { resolved: "", deniedReason: "path contains a NUL byte" };
11783
- }
11784
- const expanded = expandHome(input.trim(), home);
11785
- const resolved = path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(cwd, expanded);
11786
- for (const dir of DENIED_HOME_DIRS) {
11787
- if (isUnder(resolved, path.resolve(home, dir))) {
11788
- return { resolved, deniedReason: `refusing to preview anything under ~/${dir} (credential material)` };
11789
- }
11790
- }
11791
- for (const file of DENIED_HOME_FILES) {
11792
- if (resolved === path.resolve(home, file)) {
11793
- return { resolved, deniedReason: `refusing to preview ~/${file} (credential material)` };
11794
- }
11795
- }
11796
- const base = basenameOf(resolved);
11797
- for (const pattern of DENIED_BASENAME_PATTERNS) {
11798
- if (pattern.test(base)) {
11799
- return { resolved, deniedReason: `refusing to preview "${base}" (looks like credential material)` };
11800
- }
11801
- }
11802
- for (const suffix of DENIED_PATH_SUFFIXES) {
11803
- if (resolved.endsWith(suffix)) {
11804
- return { resolved, deniedReason: `refusing to preview ${suffix} (may contain credentials)` };
11805
- }
11806
- }
11807
- return { resolved, deniedReason: null };
11808
- }
11809
-
11810
11855
  function createMcpServer(handlers, options, sessionId) {
11811
11856
  const mcp = new mcp_js.McpServer({
11812
11857
  name: "Very Happy Tools",
@@ -11858,7 +11903,7 @@ function createMcpServer(handlers, options, sessionId) {
11858
11903
  return {
11859
11904
  content: [{
11860
11905
  type: "text",
11861
- 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.`
11906
+ 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.`
11862
11907
  }],
11863
11908
  isError: false
11864
11909
  };
@@ -12154,7 +12199,7 @@ function generateHookSettingsFile(port) {
12154
12199
  node_fs.mkdirSync(hooksDir, { recursive: true });
12155
12200
  const filename = `session-hook-${process.pid}.json`;
12156
12201
  const filepath = path.join(hooksDir, filename);
12157
- const forwarderScript = path.resolve(api.projectPath(), "scripts", "session_hook_forwarder.cjs");
12202
+ const forwarderScript = path.resolve(projectPath.projectPath(), "scripts", "session_hook_forwarder.cjs");
12158
12203
  const hookCommand = `node "${forwarderScript}" ${port}`;
12159
12204
  const settings = {
12160
12205
  hooks: {
@@ -12327,7 +12372,7 @@ function registerSideQuestionHandler(rpc, deps) {
12327
12372
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12328
12373
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12329
12374
  const run = deps.run ?? (async (input) => {
12330
- const { query } = await Promise.resolve().then(function () { return require('./index-BuQn9kUK.cjs'); });
12375
+ const { query } = await Promise.resolve().then(function () { return require('./index-Bom8mTuS.cjs'); });
12331
12376
  return runSideQuestion(query, input);
12332
12377
  });
12333
12378
  const slots = /* @__PURE__ */ new Map();
@@ -12429,8 +12474,8 @@ const MAX_TITLE_CHARS = 60;
12429
12474
  const GENERATION_TIMEOUT_MS = 3e4;
12430
12475
  function resolveClaudeBinary() {
12431
12476
  try {
12432
- 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-__h-pCTK.cjs', document.baseURI).href)));
12433
- const utilsPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_version_utils.cjs"));
12477
+ 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-CSQZhQ0C.cjs', document.baseURI).href)));
12478
+ const utilsPath = path.resolve(path.join(projectPath.projectPath(), "scripts", "claude_version_utils.cjs"));
12434
12479
  const { getClaudeCliPath } = require$1(utilsPath);
12435
12480
  const path$1 = getClaudeCliPath();
12436
12481
  if (typeof path$1 === "string" && path$1.length > 0) return path$1;
@@ -12876,8 +12921,8 @@ async function runClaude(credentials, options = {}) {
12876
12921
  machineId,
12877
12922
  homeDir: os.homedir(),
12878
12923
  happyHomeDir: persistence.configuration.happyHomeDir,
12879
- happyLibDir: api.projectPath(),
12880
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
12924
+ happyLibDir: projectPath.projectPath(),
12925
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
12881
12926
  startedFromDaemon: options.startedBy === "daemon",
12882
12927
  hostPid: process.pid,
12883
12928
  startedBy: options.startedBy || "terminal",
@@ -14655,9 +14700,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
14655
14700
  };
14656
14701
  }
14657
14702
 
14658
- 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-__h-pCTK.cjs', document.baseURI).href)));
14703
+ 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-CSQZhQ0C.cjs', document.baseURI).href)));
14659
14704
  const __dirname$1 = path.dirname(__filename$1);
14660
- const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-__h-pCTK.cjs', document.baseURI).href)));
14705
+ const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-CSQZhQ0C.cjs', document.baseURI).href)));
14661
14706
  const PRISMA_QUERY_ENGINE_FILES = {
14662
14707
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
14663
14708
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -17155,7 +17200,7 @@ async function runCodex(opts) {
17155
17200
  }
17156
17201
  });
17157
17202
  const happyServer = await startHappyServer(session);
17158
- const bridgeEntrypoint = path.join(api.projectPath(), "bin", "very-happy-mcp.mjs");
17203
+ const bridgeEntrypoint = path.join(projectPath.projectPath(), "bin", "very-happy-mcp.mjs");
17159
17204
  const mcpServers = {
17160
17205
  happy: {
17161
17206
  command: process.execPath,
@@ -17561,9 +17606,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17561
17606
  } else if (subcommand === "bye") {
17562
17607
  console.log("Bye!");
17563
17608
  process.exit(0);
17609
+ } else if (subcommand === "install-pi-tools") {
17610
+ try {
17611
+ const { handleInstallPiTools } = await Promise.resolve().then(function () { return require('./installPiTools-BKnXjrVu.cjs'); });
17612
+ await handleInstallPiTools(args.slice(1));
17613
+ } catch (error) {
17614
+ console.error(error instanceof Error ? error.message : "Pi tools installation failed");
17615
+ process.exitCode = 1;
17616
+ }
17617
+ return;
17564
17618
  } else if (subcommand === "install-terminal-hooks") {
17565
17619
  try {
17566
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-D3jfvbyY.cjs'); });
17620
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-DA7SyFYW.cjs'); });
17567
17621
  const command = parseTerminalHooksArgs(args.slice(1));
17568
17622
  if (command.action === "help") {
17569
17623
  console.log(TERMINAL_HOOKS_HELP);
@@ -17580,7 +17634,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17580
17634
  }
17581
17635
  } else if (subcommand === "spawn") {
17582
17636
  try {
17583
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-BhUK0Vt2.cjs'); });
17637
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-COOcUwkG.cjs'); });
17584
17638
  await handleSpawnCommand(args.slice(1));
17585
17639
  } catch (error) {
17586
17640
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17592,7 +17646,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17592
17646
  return;
17593
17647
  } else if (subcommand === "sessions") {
17594
17648
  try {
17595
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-CGBBgVZn.cjs'); });
17649
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-DPimBZ8H.cjs'); });
17596
17650
  await handleSessionsCommand(args.slice(1));
17597
17651
  } catch (error) {
17598
17652
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17604,7 +17658,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17604
17658
  return;
17605
17659
  } else if (subcommand === "send") {
17606
17660
  try {
17607
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DP_S3wn0.cjs'); });
17661
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-Bg2oqt7f.cjs'); });
17608
17662
  await handleSendCommand(args.slice(1));
17609
17663
  } catch (error) {
17610
17664
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17710,9 +17764,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17710
17764
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
17711
17765
  const projectId = args[3];
17712
17766
  try {
17713
- const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-D0-jnCOC.cjs'); });
17714
- const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-Dbip6eVO.cjs'); }).then(function (n) { return n.persistence; });
17715
- const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-DCVS050m.cjs'); }).then(function (n) { return n.api; });
17767
+ const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config-BIbYBmTd.cjs'); });
17768
+ const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-B3hngubn.cjs'); }).then(function (n) { return n.persistence; });
17769
+ const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-Xd_LJRJN.cjs'); }).then(function (n) { return n.api; });
17716
17770
  let userEmail = void 0;
17717
17771
  try {
17718
17772
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -17743,7 +17797,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17743
17797
  }
17744
17798
  if (geminiSubcommand === "project" && args[2] === "get") {
17745
17799
  try {
17746
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-D0-jnCOC.cjs'); });
17800
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-BIbYBmTd.cjs'); });
17747
17801
  const config = readGeminiLocalConfig();
17748
17802
  if (config.googleCloudProject) {
17749
17803
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17783,7 +17837,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17783
17837
  process.exit(0);
17784
17838
  }
17785
17839
  try {
17786
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-D34eCFpz.cjs'); });
17840
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-eJ08cXk7.cjs'); });
17787
17841
  let startedBy = void 0;
17788
17842
  for (let i = 1; i < args.length; i++) {
17789
17843
  if (args[i] === "--started-by") {
@@ -17804,6 +17858,16 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17804
17858
  }
17805
17859
  return;
17806
17860
  } else if (subcommand === "pi") {
17861
+ if (args[1] === "--terminal") {
17862
+ try {
17863
+ const { runPiTerminal } = await Promise.resolve().then(function () { return require('./piTerminal-DNr3hkzY.cjs'); });
17864
+ process.exitCode = await runPiTerminal(args.slice(2));
17865
+ } catch (error) {
17866
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
17867
+ process.exitCode = 1;
17868
+ }
17869
+ return;
17870
+ }
17807
17871
  let hintPrinted = false;
17808
17872
  const printHint = (detail) => {
17809
17873
  const hint = piAdapterMissingHint(detail);
@@ -17813,7 +17877,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17813
17877
  }
17814
17878
  };
17815
17879
  try {
17816
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DJvzGXVN.cjs'); });
17880
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPTPrjhL.cjs'); });
17817
17881
  const parsed = parsePiRunnerArgs(args.slice(1));
17818
17882
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17819
17883
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17841,7 +17905,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17841
17905
  return;
17842
17906
  } else if (subcommand === "acp") {
17843
17907
  try {
17844
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-DJvzGXVN.cjs'); });
17908
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPTPrjhL.cjs'); });
17845
17909
  let startedBy = void 0;
17846
17910
  let verbose = false;
17847
17911
  const acpArgs = [];
@@ -17881,7 +17945,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17881
17945
  return;
17882
17946
  } else if (subcommand === "openclaw") {
17883
17947
  try {
17884
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-B8F8D--R.cjs'); });
17948
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-D0LVzxqM.cjs'); });
17885
17949
  let startedBy = void 0;
17886
17950
  let verbose = false;
17887
17951
  let gatewayUrl;
@@ -17932,7 +17996,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17932
17996
  return;
17933
17997
  } else if (subcommand === "todo") {
17934
17998
  try {
17935
- const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-CeJX0z_2.cjs'); });
17999
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-DeauuUd6.cjs'); });
17936
18000
  await handleTodoCommand(args.slice(1));
17937
18001
  } catch (error) {
17938
18002
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -17941,17 +18005,17 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17941
18005
  return;
17942
18006
  } else if (subcommand === "teams") {
17943
18007
  try {
17944
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-C29nEHFD.cjs'); });
18008
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-DSQgHBLE.cjs'); });
17945
18009
  await handleTeamsCommand(args.slice(1));
17946
18010
  } catch (error) {
17947
18011
  console.error(error instanceof Error ? error.message : "Teams command failed");
17948
18012
  process.exitCode = 1;
17949
18013
  }
17950
18014
  return;
17951
- } else if (subcommand === "mcp" && args.length === 1) {
18015
+ } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
17952
18016
  try {
17953
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-DeapmTWN.cjs'); });
17954
- await handleMcpCommand();
18017
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-ySrfNOYl.cjs'); });
18018
+ await handleMcpCommand(args[1] === "--terminal-tools");
17955
18019
  } catch (error) {
17956
18020
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
17957
18021
  `);
@@ -18171,6 +18235,10 @@ ${chalk.bold("Usage:")}
18171
18235
  very-happy codex Start Codex mode
18172
18236
  very-happy gemini Start Gemini mode (ACP)
18173
18237
  very-happy pi Start pi mode (ACP via the pi-acp adapter)
18238
+ very-happy pi --terminal Run native pi with title, clipboard and preview tools
18239
+ inside a Very Happy terminal
18240
+ very-happy install-pi-tools Install tools for directly launched pi sessions
18241
+ (add --remove to uninstall)
18174
18242
  very-happy acp Start a generic ACP-compatible agent
18175
18243
  very-happy openclaw Connect through a configured OpenClaw gateway
18176
18244
  very-happy install-terminal-hooks
@@ -18332,6 +18400,7 @@ exports.TEAM_SKILL = TEAM_SKILL;
18332
18400
  exports.TitleGenerator = TitleGenerator;
18333
18401
  exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
18334
18402
  exports.archiveSession = archiveSession;
18403
+ exports.checkPreviewPath = checkPreviewPath;
18335
18404
  exports.createSessionMetadata = createSessionMetadata;
18336
18405
  exports.hashObject = hashObject;
18337
18406
  exports.initialMachineMetadata = initialMachineMetadata;