very-happy-cli 0.2.141 → 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-CWmjboOm.mjs → AcpBackend-D2LqY2to.mjs} +2 -2
  2. package/dist/{AcpBackend-CnWSwaHk.cjs → AcpBackend-DJsqfgjD.cjs} +2 -2
  3. package/dist/{AcpSessionManager-Dxt78rPO.cjs → AcpSessionManager-CTfDxziP.cjs} +1 -1
  4. package/dist/{AcpSessionManager-CtXoi6PN.mjs → AcpSessionManager-CdeM5KD9.mjs} +1 -1
  5. package/dist/{agentGuidance-B9ENxSaS.cjs → agentGuidance-Bi8iBjy3.cjs} +1 -1
  6. package/dist/{agentGuidance-Do7cQka3.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-Dn7nAHcA.cjs → config-BIbYBmTd.cjs} +5 -4
  10. package/dist/{config-DY57frJF.mjs → config-ZsBeq10u.mjs} +5 -4
  11. package/dist/{index-3STVDtNO.cjs → index-BPTPrjhL.cjs} +12 -40
  12. package/dist/{index-BSVgut_Y.cjs → index-Bom8mTuS.cjs} +5 -4
  13. package/dist/{index-DhfgAB8K.mjs → index-CNARLBax.mjs} +5 -4
  14. package/dist/{index-ChWePQEd.cjs → index-CSQZhQ0C.cjs} +180 -128
  15. package/dist/{index-Dz6FgNFg.mjs → index-CY0zmhIp.mjs} +11 -39
  16. package/dist/{index-C55sW8E9.mjs → index-CYFendNf.mjs} +161 -110
  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-BPJu2WVz.mjs} +8 -7
  22. package/dist/{installTerminalHooks--jkP17uE.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-DbLWXlJm.cjs → limits-B3hngubn.cjs} +29 -10
  28. package/dist/{limits-DT5Yk_UH.mjs → limits-CHsz3Ba-.mjs} +29 -11
  29. package/dist/{mcp-Bzy-X9kI.mjs → mcp-BkWBRWFe.mjs} +28 -14
  30. package/dist/{mcp-kE0n1dBF.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-CxUz95A1.cjs → runGemini-eJ08cXk7.cjs} +9 -8
  40. package/dist/{runGemini-BmdhYEzM.mjs → runGemini-rgFHTnG7.mjs} +8 -7
  41. package/dist/{runOpenClaw-RLIFJx61.cjs → runOpenClaw-D0LVzxqM.cjs} +6 -5
  42. package/dist/{runOpenClaw-CYpO3Vv8.mjs → runOpenClaw-GjLWwjas.mjs} +6 -5
  43. package/dist/{send-BGcCrMUT.cjs → send-Bg2oqt7f.cjs} +5 -4
  44. package/dist/{send-DT04hV8o.mjs → send-DV8liaRN.mjs} +5 -4
  45. package/dist/{sessions-Jt9lrcrG.cjs → sessions-DPimBZ8H.cjs} +7 -6
  46. package/dist/{sessions-DDWTq2AG.mjs → sessions-JiL4aJkC.mjs} +7 -6
  47. package/dist/{spawn-CdZgvthE.cjs → spawn-COOcUwkG.cjs} +5 -4
  48. package/dist/{spawn-Ca0A70dX.mjs → spawn-CnMk3LFG.mjs} +5 -4
  49. package/dist/{teams-DfVwVZ4x.cjs → teams-DSQgHBLE.cjs} +5 -4
  50. package/dist/{teams-CzK1zg0m.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-D9nFaYpQ.cjs → todo-DeauuUd6.cjs} +4 -3
  54. package/dist/{todo-4WhOom8k.mjs → todo-oHWNpEx_.mjs} +4 -3
  55. package/dist/{types-CLosaeZT.mjs → types-Dnoxgb7G.mjs} +30 -13
  56. package/dist/{types-Bk3W89Z3.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-Bk3W89Z3.cjs');
7
- var persistence = require('./limits-DbLWXlJm.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-B9ENxSaS.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
  }
@@ -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-CSQZhQ0C.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
  }
@@ -9954,7 +10055,7 @@ const initialMachineMetadata = {
9954
10055
  happyCliVersion: persistence.packageJson.version,
9955
10056
  homeDir: os$1.homedir(),
9956
10057
  happyHomeDir: persistence.configuration.happyHomeDir,
9957
- happyLibDir: api.projectPath(),
10058
+ happyLibDir: projectPath.projectPath(),
9958
10059
  cliAvailability: startupCliAvailability,
9959
10060
  resumeSupport: { ...api.detectResumeSupport(), rpcAvailable: true }
9960
10061
  };
@@ -10402,7 +10503,7 @@ async function startDaemon() {
10402
10503
  const sessionDesc = tmuxSessionName || "current/most recent session";
10403
10504
  persistence.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
10404
10505
  const tmux = getTmuxUtilities(tmuxSessionName);
10405
- const cliPath = path$1.join(api.projectPath(), "dist", "index.mjs");
10506
+ const cliPath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
10406
10507
  const agent = options.agent ?? "claude";
10407
10508
  if (!isSpawnAgent(agent)) {
10408
10509
  return {
@@ -11006,11 +11107,15 @@ async function startDaemon() {
11006
11107
  onSessionStateEvent,
11007
11108
  onClaudeAuthFailed: (sessionId) => claudeAuthServiceRef?.signalAuthFailed(sessionId),
11008
11109
  onSessionTurnEvent: (sessionId, event) => turnActivity.apply(sessionId, event),
11009
- pushClipboard: (text) => {
11110
+ pushClipboard: (text, terminalId) => {
11010
11111
  if (!apiMachineRef) {
11011
11112
  return { delivered: false, truncated: false, totalBytes: 0, error: "daemon is still starting up" };
11012
11113
  }
11013
- 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);
11014
11119
  },
11015
11120
  onTerminalHook: (body) => {
11016
11121
  mirrorManagerRef?.handleHookPayload(body);
@@ -11034,7 +11139,7 @@ async function startDaemon() {
11034
11139
  };
11035
11140
  persistence.writeDaemonState(fileState);
11036
11141
  persistence.logger.debug("[DAEMON RUN] Daemon state written");
11037
- const bundlePath = path$1.join(api.projectPath(), "dist", "index.mjs");
11142
+ const bundlePath = path$1.join(projectPath.projectPath(), "dist", "index.mjs");
11038
11143
  let initialBundleMtimeMs = 0;
11039
11144
  try {
11040
11145
  initialBundleMtimeMs = fs$1.statSync(bundlePath).mtimeMs;
@@ -11090,7 +11195,7 @@ async function startDaemon() {
11090
11195
  });
11091
11196
  const claudeAuthService = new ClaudeAuthService({
11092
11197
  happyHomeDir: persistence.configuration.happyHomeDir,
11093
- happyLibDir: api.projectPath(),
11198
+ happyLibDir: projectPath.projectPath(),
11094
11199
  credentialSource: daemonClaudeCredentials.source,
11095
11200
  getCredentialStore: () => claudeCredentialStoreSetting,
11096
11201
  setCredentialStore: async (store) => {
@@ -11747,83 +11852,6 @@ function normalizeProgress(value) {
11747
11852
  return flat.length > PROGRESS_MAX_CHARS$1 ? flat.slice(0, PROGRESS_MAX_CHARS$1) : flat;
11748
11853
  }
11749
11854
 
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
11855
  function createMcpServer(handlers, options, sessionId) {
11828
11856
  const mcp = new mcp_js.McpServer({
11829
11857
  name: "Very Happy Tools",
@@ -11875,7 +11903,7 @@ function createMcpServer(handlers, options, sessionId) {
11875
11903
  return {
11876
11904
  content: [{
11877
11905
  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.`
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.`
11879
11907
  }],
11880
11908
  isError: false
11881
11909
  };
@@ -12171,7 +12199,7 @@ function generateHookSettingsFile(port) {
12171
12199
  node_fs.mkdirSync(hooksDir, { recursive: true });
12172
12200
  const filename = `session-hook-${process.pid}.json`;
12173
12201
  const filepath = path.join(hooksDir, filename);
12174
- const forwarderScript = path.resolve(api.projectPath(), "scripts", "session_hook_forwarder.cjs");
12202
+ const forwarderScript = path.resolve(projectPath.projectPath(), "scripts", "session_hook_forwarder.cjs");
12175
12203
  const hookCommand = `node "${forwarderScript}" ${port}`;
12176
12204
  const settings = {
12177
12205
  hooks: {
@@ -12344,7 +12372,7 @@ function registerSideQuestionHandler(rpc, deps) {
12344
12372
  const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
12345
12373
  const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
12346
12374
  const run = deps.run ?? (async (input) => {
12347
- const { query } = await Promise.resolve().then(function () { return require('./index-BSVgut_Y.cjs'); });
12375
+ const { query } = await Promise.resolve().then(function () { return require('./index-Bom8mTuS.cjs'); });
12348
12376
  return runSideQuestion(query, input);
12349
12377
  });
12350
12378
  const slots = /* @__PURE__ */ new Map();
@@ -12446,8 +12474,8 @@ const MAX_TITLE_CHARS = 60;
12446
12474
  const GENERATION_TIMEOUT_MS = 3e4;
12447
12475
  function resolveClaudeBinary() {
12448
12476
  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"));
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"));
12451
12479
  const { getClaudeCliPath } = require$1(utilsPath);
12452
12480
  const path$1 = getClaudeCliPath();
12453
12481
  if (typeof path$1 === "string" && path$1.length > 0) return path$1;
@@ -12893,8 +12921,8 @@ async function runClaude(credentials, options = {}) {
12893
12921
  machineId,
12894
12922
  homeDir: os.homedir(),
12895
12923
  happyHomeDir: persistence.configuration.happyHomeDir,
12896
- happyLibDir: api.projectPath(),
12897
- happyToolsDir: path.resolve(api.projectPath(), "tools", "unpacked"),
12924
+ happyLibDir: projectPath.projectPath(),
12925
+ happyToolsDir: path.resolve(projectPath.projectPath(), "tools", "unpacked"),
12898
12926
  startedFromDaemon: options.startedBy === "daemon",
12899
12927
  hostPid: process.pid,
12900
12928
  startedBy: options.startedBy || "terminal",
@@ -14672,9 +14700,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
14672
14700
  };
14673
14701
  }
14674
14702
 
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)));
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)));
14676
14704
  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)));
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)));
14678
14706
  const PRISMA_QUERY_ENGINE_FILES = {
14679
14707
  "arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
14680
14708
  "x64-darwin": "libquery_engine-darwin.dylib.node",
@@ -17172,7 +17200,7 @@ async function runCodex(opts) {
17172
17200
  }
17173
17201
  });
17174
17202
  const happyServer = await startHappyServer(session);
17175
- const bridgeEntrypoint = path.join(api.projectPath(), "bin", "very-happy-mcp.mjs");
17203
+ const bridgeEntrypoint = path.join(projectPath.projectPath(), "bin", "very-happy-mcp.mjs");
17176
17204
  const mcpServers = {
17177
17205
  happy: {
17178
17206
  command: process.execPath,
@@ -17578,9 +17606,18 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17578
17606
  } else if (subcommand === "bye") {
17579
17607
  console.log("Bye!");
17580
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;
17581
17618
  } else if (subcommand === "install-terminal-hooks") {
17582
17619
  try {
17583
- const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks--jkP17uE.cjs'); });
17620
+ const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-DA7SyFYW.cjs'); });
17584
17621
  const command = parseTerminalHooksArgs(args.slice(1));
17585
17622
  if (command.action === "help") {
17586
17623
  console.log(TERMINAL_HOOKS_HELP);
@@ -17597,7 +17634,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17597
17634
  }
17598
17635
  } else if (subcommand === "spawn") {
17599
17636
  try {
17600
- const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CdZgvthE.cjs'); });
17637
+ const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-COOcUwkG.cjs'); });
17601
17638
  await handleSpawnCommand(args.slice(1));
17602
17639
  } catch (error) {
17603
17640
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17609,7 +17646,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17609
17646
  return;
17610
17647
  } else if (subcommand === "sessions") {
17611
17648
  try {
17612
- const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-Jt9lrcrG.cjs'); });
17649
+ const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-DPimBZ8H.cjs'); });
17613
17650
  await handleSessionsCommand(args.slice(1));
17614
17651
  } catch (error) {
17615
17652
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17621,7 +17658,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17621
17658
  return;
17622
17659
  } else if (subcommand === "send") {
17623
17660
  try {
17624
- const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-BGcCrMUT.cjs'); });
17661
+ const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-Bg2oqt7f.cjs'); });
17625
17662
  await handleSendCommand(args.slice(1));
17626
17663
  } catch (error) {
17627
17664
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
@@ -17727,9 +17764,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17727
17764
  if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
17728
17765
  const projectId = args[3];
17729
17766
  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; });
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; });
17733
17770
  let userEmail = void 0;
17734
17771
  try {
17735
17772
  const credentials = await readCredentialsForConfiguredRelay2();
@@ -17760,7 +17797,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17760
17797
  }
17761
17798
  if (geminiSubcommand === "project" && args[2] === "get") {
17762
17799
  try {
17763
- const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-Dn7nAHcA.cjs'); });
17800
+ const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config-BIbYBmTd.cjs'); });
17764
17801
  const config = readGeminiLocalConfig();
17765
17802
  if (config.googleCloudProject) {
17766
17803
  console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
@@ -17800,7 +17837,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17800
17837
  process.exit(0);
17801
17838
  }
17802
17839
  try {
17803
- const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-CxUz95A1.cjs'); });
17840
+ const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-eJ08cXk7.cjs'); });
17804
17841
  let startedBy = void 0;
17805
17842
  for (let i = 1; i < args.length; i++) {
17806
17843
  if (args[i] === "--started-by") {
@@ -17821,6 +17858,16 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17821
17858
  }
17822
17859
  return;
17823
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
+ }
17824
17871
  let hintPrinted = false;
17825
17872
  const printHint = (detail) => {
17826
17873
  const hint = piAdapterMissingHint(detail);
@@ -17830,7 +17877,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17830
17877
  }
17831
17878
  };
17832
17879
  try {
17833
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-3STVDtNO.cjs'); });
17880
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPTPrjhL.cjs'); });
17834
17881
  const parsed = parsePiRunnerArgs(args.slice(1));
17835
17882
  const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
17836
17883
  const { credentials } = await authAndSetupMachineIfNeeded();
@@ -17858,7 +17905,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17858
17905
  return;
17859
17906
  } else if (subcommand === "acp") {
17860
17907
  try {
17861
- const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-3STVDtNO.cjs'); });
17908
+ const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPTPrjhL.cjs'); });
17862
17909
  let startedBy = void 0;
17863
17910
  let verbose = false;
17864
17911
  const acpArgs = [];
@@ -17898,7 +17945,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17898
17945
  return;
17899
17946
  } else if (subcommand === "openclaw") {
17900
17947
  try {
17901
- const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-RLIFJx61.cjs'); });
17948
+ const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-D0LVzxqM.cjs'); });
17902
17949
  let startedBy = void 0;
17903
17950
  let verbose = false;
17904
17951
  let gatewayUrl;
@@ -17949,7 +17996,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17949
17996
  return;
17950
17997
  } else if (subcommand === "todo") {
17951
17998
  try {
17952
- const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-D9nFaYpQ.cjs'); });
17999
+ const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-DeauuUd6.cjs'); });
17953
18000
  await handleTodoCommand(args.slice(1));
17954
18001
  } catch (error) {
17955
18002
  console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
@@ -17958,17 +18005,17 @@ Conversation history is preserved on the server, but in-flight tool calls are in
17958
18005
  return;
17959
18006
  } else if (subcommand === "teams") {
17960
18007
  try {
17961
- const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-DfVwVZ4x.cjs'); });
18008
+ const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-DSQgHBLE.cjs'); });
17962
18009
  await handleTeamsCommand(args.slice(1));
17963
18010
  } catch (error) {
17964
18011
  console.error(error instanceof Error ? error.message : "Teams command failed");
17965
18012
  process.exitCode = 1;
17966
18013
  }
17967
18014
  return;
17968
- } else if (subcommand === "mcp" && args.length === 1) {
18015
+ } else if (subcommand === "mcp" && (args.length === 1 || args.length === 2 && args[1] === "--terminal-tools")) {
17969
18016
  try {
17970
- const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-kE0n1dBF.cjs'); });
17971
- await handleMcpCommand();
18017
+ const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-ySrfNOYl.cjs'); });
18018
+ await handleMcpCommand(args[1] === "--terminal-tools");
17972
18019
  } catch (error) {
17973
18020
  process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
17974
18021
  `);
@@ -18188,6 +18235,10 @@ ${chalk.bold("Usage:")}
18188
18235
  very-happy codex Start Codex mode
18189
18236
  very-happy gemini Start Gemini mode (ACP)
18190
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)
18191
18242
  very-happy acp Start a generic ACP-compatible agent
18192
18243
  very-happy openclaw Connect through a configured OpenClaw gateway
18193
18244
  very-happy install-terminal-hooks
@@ -18349,6 +18400,7 @@ exports.TEAM_SKILL = TEAM_SKILL;
18349
18400
  exports.TitleGenerator = TitleGenerator;
18350
18401
  exports.appendStagedAttachmentsToPrompt = appendStagedAttachmentsToPrompt;
18351
18402
  exports.archiveSession = archiveSession;
18403
+ exports.checkPreviewPath = checkPreviewPath;
18352
18404
  exports.createSessionMetadata = createSessionMetadata;
18353
18405
  exports.hashObject = hashObject;
18354
18406
  exports.initialMachineMetadata = initialMachineMetadata;