very-happy-cli 0.2.141 → 0.2.143

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/{AcpBackend-CnWSwaHk.cjs → AcpBackend-B8oehrfU.cjs} +2 -2
  2. package/dist/{AcpBackend-CWmjboOm.mjs → AcpBackend-LEgn9vol.mjs} +2 -2
  3. package/dist/{AcpSessionManager-Dxt78rPO.cjs → AcpSessionManager-BomdfyhZ.cjs} +1 -1
  4. package/dist/{AcpSessionManager-CtXoi6PN.mjs → AcpSessionManager-Yfbs5bi2.mjs} +1 -1
  5. package/dist/{agentGuidance-Do7cQka3.mjs → agentGuidance-BU4hxm8M.mjs} +1 -1
  6. package/dist/{agentGuidance-B9ENxSaS.cjs → agentGuidance-CJSl54ua.cjs} +1 -1
  7. package/dist/codex/happyMcpStdioBridge.cjs +2 -2
  8. package/dist/codex/happyMcpStdioBridge.mjs +2 -2
  9. package/dist/{config-Dn7nAHcA.cjs → config-CfqWoHmO.cjs} +5 -4
  10. package/dist/{config-DY57frJF.mjs → config-euZ_K5Lo.mjs} +5 -4
  11. package/dist/{index-BSVgut_Y.cjs → index-BREtikhm.cjs} +5 -4
  12. package/dist/{index-3STVDtNO.cjs → index-Bj68y6ee.cjs} +12 -40
  13. package/dist/{index-ChWePQEd.cjs → index-C510dSUn.cjs} +188 -132
  14. package/dist/{index-C55sW8E9.mjs → index-DoiG88oX.mjs} +169 -114
  15. package/dist/{index-DhfgAB8K.mjs → index-PM_SsRpw.mjs} +5 -4
  16. package/dist/{index-Dz6FgNFg.mjs → index-ZOMC8Gzn.mjs} +11 -39
  17. package/dist/index.cjs +7 -6
  18. package/dist/index.mjs +7 -6
  19. package/dist/installPiTools-BKnXjrVu.cjs +82 -0
  20. package/dist/installPiTools-C-Lxv4Bt.mjs +77 -0
  21. package/dist/{installTerminalHooks-zmtIrf3E.mjs → installTerminalHooks-BGVQqbgq.mjs} +8 -7
  22. package/dist/{installTerminalHooks--jkP17uE.cjs → installTerminalHooks-C_jraM1j.cjs} +9 -8
  23. package/dist/lib.cjs +3 -2
  24. package/dist/lib.d.cts +6 -1
  25. package/dist/lib.d.mts +6 -1
  26. package/dist/lib.mjs +3 -2
  27. package/dist/{limits-DbLWXlJm.cjs → limits-B3oVgqBJ.cjs} +29 -10
  28. package/dist/{limits-DT5Yk_UH.mjs → limits-uABU-yq3.mjs} +29 -11
  29. package/dist/{mcp-Bzy-X9kI.mjs → mcp-CfbTQXyh.mjs} +28 -14
  30. package/dist/{mcp-kE0n1dBF.cjs → mcp-G-3lL_KH.cjs} +29 -15
  31. package/dist/piExtension.d.mts +17 -0
  32. package/dist/piExtension.mjs +84 -0
  33. package/dist/piRuntime-BE6iYQwU.mjs +37 -0
  34. package/dist/piRuntime-CWE-1W-y.cjs +39 -0
  35. package/dist/piTerminal-Dyc_9pGP.cjs +132 -0
  36. package/dist/piTerminal-rqKn1_Zy.mjs +129 -0
  37. package/dist/projectPath-CSpqWdxp.mjs +10 -0
  38. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  39. package/dist/{runGemini-BmdhYEzM.mjs → runGemini-BMlXIaTL.mjs} +8 -7
  40. package/dist/{runGemini-CxUz95A1.cjs → runGemini-C6qMFzcn.cjs} +9 -8
  41. package/dist/{runOpenClaw-RLIFJx61.cjs → runOpenClaw-C2_aCP7v.cjs} +6 -5
  42. package/dist/{runOpenClaw-CYpO3Vv8.mjs → runOpenClaw-CoO5Vp3S.mjs} +6 -5
  43. package/dist/{send-DT04hV8o.mjs → send-CBrJylJc.mjs} +5 -4
  44. package/dist/{send-BGcCrMUT.cjs → send-DGVwmB-p.cjs} +5 -4
  45. package/dist/{sessions-DDWTq2AG.mjs → sessions-DbLubh2N.mjs} +7 -6
  46. package/dist/{sessions-Jt9lrcrG.cjs → sessions-DyQXMN-8.cjs} +7 -6
  47. package/dist/{spawn-CdZgvthE.cjs → spawn-DIyFOn3W.cjs} +5 -4
  48. package/dist/{spawn-Ca0A70dX.mjs → spawn-wkkxEGdF.mjs} +5 -4
  49. package/dist/{teams-CzK1zg0m.mjs → teams-BTyVFK3P.mjs} +5 -4
  50. package/dist/{teams-DfVwVZ4x.cjs → teams-QOkGFjkJ.cjs} +5 -4
  51. package/dist/terminalToolContext-CFeGeaNm.mjs +7 -0
  52. package/dist/terminalToolContext-DoV9Gs7K.cjs +9 -0
  53. package/dist/{todo-4WhOom8k.mjs → todo-DoLkqdBe.mjs} +4 -3
  54. package/dist/{todo-D9nFaYpQ.cjs → todo-Du6pjwx_.cjs} +4 -3
  55. package/dist/{types-Bk3W89Z3.cjs → types-6__wqmmP.cjs} +80 -17
  56. package/dist/{types-CLosaeZT.mjs → types-BG4gFjLL.mjs} +79 -15
  57. package/package.json +11 -7
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
- var persistence = require('./limits-DbLWXlJm.cjs');
4
+ var persistence = require('./limits-B3oVgqBJ.cjs');
5
5
  var node_events = require('node:events');
6
6
  var socket_ioClient = require('socket.io-client');
7
7
  var z = require('zod');
@@ -13,7 +13,7 @@ var fs = require('fs/promises');
13
13
  var crypto$1 = require('crypto');
14
14
  var path = require('path');
15
15
  var spawn = require('cross-spawn');
16
- var url = require('url');
16
+ var projectPath = require('./projectPath-ChLhdOyt.cjs');
17
17
  var os = require('os');
18
18
  var promises = require('node:fs/promises');
19
19
  var path$1 = require('node:path');
@@ -28,7 +28,6 @@ var sandboxRuntime = require('@anthropic-ai/sandbox-runtime');
28
28
  var expoServerSdk = require('expo-server-sdk');
29
29
  var chalk = require('chalk');
30
30
 
31
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
32
31
  function _interopNamespaceDefault(e) {
33
32
  var n = Object.create(null);
34
33
  if (e) {
@@ -1378,14 +1377,42 @@ class RpcHandlerManager {
1378
1377
  }
1379
1378
  }
1380
1379
 
1381
- const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-Bk3W89Z3.cjs', document.baseURI).href))));
1382
- function projectPath() {
1383
- const path$1 = path.resolve(__dirname$1, "..");
1384
- return path$1;
1380
+ function describeSocketAckDrop(drop) {
1381
+ switch (drop.reason) {
1382
+ case "no-ack":
1383
+ return "no ack callback on the frame (re-delivered after a transport close?); response dropped";
1384
+ case "handler-threw":
1385
+ return `handler threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
1386
+ case "ack-threw":
1387
+ return `ack callback threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
1388
+ }
1389
+ }
1390
+ function safeAck(ack, onDrop) {
1391
+ return (value) => {
1392
+ if (typeof ack !== "function") {
1393
+ onDrop({ reason: "no-ack" });
1394
+ return;
1395
+ }
1396
+ try {
1397
+ ack(value);
1398
+ } catch (error) {
1399
+ onDrop({ reason: "ack-threw", error });
1400
+ }
1401
+ };
1402
+ }
1403
+ async function answerSocketRequest(ack, run, onDrop) {
1404
+ let value;
1405
+ try {
1406
+ value = await run();
1407
+ } catch (error) {
1408
+ onDrop({ reason: "handler-threw", error });
1409
+ return;
1410
+ }
1411
+ safeAck(ack, onDrop)(value);
1385
1412
  }
1386
1413
 
1387
1414
  function run$1(args, options) {
1388
- const RUNNER_PATH = path.resolve(path.join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
1415
+ const RUNNER_PATH = path.resolve(path.join(projectPath.projectPath(), "scripts", "ripgrep_launcher.cjs"));
1389
1416
  return new Promise((resolve2, reject) => {
1390
1417
  const child = spawn.spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
1391
1418
  stdio: ["pipe", "pipe", "pipe"],
@@ -1416,7 +1443,7 @@ function run$1(args, options) {
1416
1443
  function getBinaryPath() {
1417
1444
  const platformName = os.platform();
1418
1445
  const binaryName = platformName === "win32" ? "difft.exe" : "difft";
1419
- return path.resolve(path.join(projectPath(), "tools", "unpacked", binaryName));
1446
+ return path.resolve(path.join(projectPath.projectPath(), "tools", "unpacked", binaryName));
1420
1447
  }
1421
1448
  function run(args, options) {
1422
1449
  const binaryPath = getBinaryPath();
@@ -3074,13 +3101,18 @@ async function notifyDaemonTurnEvent(sessionId, event) {
3074
3101
  async function stopDaemonHttp() {
3075
3102
  await daemonPost("/stop");
3076
3103
  }
3077
- async function pushClipboardViaDaemon(text) {
3078
- const result = await daemonPost("/clipboard", { text });
3104
+ async function pushClipboardViaDaemon(text, terminalId) {
3105
+ const result = await daemonPost("/clipboard", { text, ...terminalId ? { terminalId } : {} });
3079
3106
  if (result?.error && result.delivered === void 0) {
3080
3107
  return { delivered: false, error: result.error };
3081
3108
  }
3082
3109
  return result;
3083
3110
  }
3111
+ async function pushFilePreviewViaDaemon(terminalId, path, mode = "file") {
3112
+ const result = await daemonPost("/file-preview", { terminalId, path, mode });
3113
+ if (result?.error) return { delivered: false, error: result.error };
3114
+ return { delivered: result?.delivered === true, ...result?.delivered === true ? {} : { error: "preview was not delivered" } };
3115
+ }
3084
3116
  async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
3085
3117
  const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
3086
3118
  if (result?.error) {
@@ -3281,7 +3313,11 @@ class ApiSessionClient extends node_events.EventEmitter {
3281
3313
  void this.connectSessionRelay();
3282
3314
  });
3283
3315
  this.socket.on("rpc-request", async (data, callback) => {
3284
- callback(await this.rpcHandlerManager.handleRequest(data));
3316
+ await answerSocketRequest(
3317
+ callback,
3318
+ () => this.rpcHandlerManager.handleRequest(data),
3319
+ (drop) => persistence.logger.debug(`[API] RPC ${data?.method} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
3320
+ );
3285
3321
  });
3286
3322
  this.socket.on("disconnect", (reason) => {
3287
3323
  persistence.logger.debug(`[API] Socket disconnected: ${reason}`);
@@ -3717,9 +3753,14 @@ class ApiSessionClient extends node_events.EventEmitter {
3717
3753
  persistence.logger.debug(`[API] Session connected to regional relay ${assignment.relayId}`);
3718
3754
  });
3719
3755
  relaySocket.on("rpc-request", async (data, callback) => {
3720
- callback(await this.rpcHandlerManager.handleRequest(data));
3756
+ await answerSocketRequest(
3757
+ callback,
3758
+ () => this.rpcHandlerManager.handleRequest(data),
3759
+ (drop) => persistence.logger.debug(`[API] Relay RPC ${data?.method} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
3760
+ );
3721
3761
  });
3722
- relaySocket.on("session-message-deliver", async (data, callback) => {
3762
+ relaySocket.on("session-message-deliver", async (data, rawCallback) => {
3763
+ const callback = safeAck(rawCallback, (drop) => persistence.logger.debug(`[API] session-message-deliver unanswered \u2014 ${describeSocketAckDrop(drop)}`));
3723
3764
  if (data?.sessionId !== this.sessionId || !Array.isArray(data.messages) || data.messages.length === 0 || data.messages.length > 50) {
3724
3765
  callback({ ok: false, error: "Invalid session message request" });
3725
3766
  return;
@@ -4020,12 +4061,15 @@ class ApiSessionClient extends node_events.EventEmitter {
4020
4061
  */
4021
4062
  pushClipboard(text) {
4022
4063
  const prepared = persistence.prepareClipboardText(text);
4064
+ const history = persistence.prepareClipboardHistoryText(text);
4023
4065
  if (!this.socket.connected) {
4024
4066
  return { delivered: false, truncated: prepared.truncated, totalBytes: prepared.totalBytes };
4025
4067
  }
4026
4068
  this.socket.emit("clipboard-push", {
4027
4069
  payload: persistence.encodeBase64(persistence.encrypt(this.encryptionKey, this.encryptionVariant, prepared.text)),
4028
4070
  enc: true,
4071
+ historyPayload: persistence.encodeBase64(persistence.encrypt(this.encryptionKey, this.encryptionVariant, history.text)),
4072
+ historyTruncated: history.truncated,
4029
4073
  truncated: prepared.truncated,
4030
4074
  totalBytes: prepared.totalBytes
4031
4075
  });
@@ -14840,19 +14884,34 @@ class ApiMachineClient {
14840
14884
  * (terminal-path claude → `very-happy mcp` → daemon /clipboard → here).
14841
14885
  * Encrypted with the per-machine key; the server relays without reading.
14842
14886
  */
14843
- pushClipboard(text) {
14887
+ pushClipboard(text, terminalId) {
14844
14888
  const prepared = persistence.prepareClipboardText(text);
14889
+ const history = persistence.prepareClipboardHistoryText(text);
14845
14890
  if (!this.socket?.connected) {
14846
14891
  return { delivered: false, truncated: prepared.truncated, totalBytes: prepared.totalBytes, error: "daemon is not connected to the server" };
14847
14892
  }
14848
14893
  this.socket.emit("clipboard-push", {
14894
+ ...terminalId ? { terminalId } : {},
14849
14895
  payload: persistence.encodeBase64(persistence.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, prepared.text)),
14850
14896
  enc: true,
14897
+ historyPayload: persistence.encodeBase64(persistence.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, history.text)),
14898
+ historyTruncated: history.truncated,
14851
14899
  truncated: prepared.truncated,
14852
14900
  totalBytes: prepared.totalBytes
14853
14901
  });
14854
14902
  return { delivered: true, truncated: prepared.truncated, totalBytes: prepared.totalBytes };
14855
14903
  }
14904
+ /** Queue a terminal preview with the machine key. Delivery does not prove a browser opened it. */
14905
+ pushFilePreview(terminalId, path, mode = "file") {
14906
+ if (!this.socket?.connected) return { delivered: false, error: "daemon is not connected to the server" };
14907
+ this.socket.emit("file-preview-push", {
14908
+ terminalId,
14909
+ payload: persistence.encodeBase64(persistence.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, path)),
14910
+ enc: true,
14911
+ mode
14912
+ });
14913
+ return { delivered: true };
14914
+ }
14856
14915
  /** Encrypt one base64 terminal payload with the per-machine key (same scheme
14857
14916
  * as the live output stream) → base64 ciphertext. Used for both live output
14858
14917
  * and the open-terminal snapshot/replay payloads. */
@@ -15734,7 +15793,11 @@ class ApiMachineClient {
15734
15793
  bindRpcRequestHandler(socket, transport) {
15735
15794
  socket.on("rpc-request", async (data, callback) => {
15736
15795
  persistence.logger.debugLargeJson(`[API MACHINE] Received RPC request via ${transport}:`, data);
15737
- callback(await this.rpcHandlerManager.handleRequest(data));
15796
+ await answerSocketRequest(
15797
+ callback,
15798
+ () => this.rpcHandlerManager.handleRequest(data),
15799
+ (drop) => persistence.logger.debug(`[API MACHINE] RPC ${data?.method} via ${transport} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
15800
+ );
15738
15801
  });
15739
15802
  }
15740
15803
  bindMachineCommandHandlers(socket) {
@@ -16989,8 +17052,8 @@ exports.notifyDaemonSessionEvent = notifyDaemonSessionEvent;
16989
17052
  exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
16990
17053
  exports.parseExactVersion = parseExactVersion;
16991
17054
  exports.parseTerminalHookPayload = parseTerminalHookPayload;
16992
- exports.projectPath = projectPath;
16993
17055
  exports.pushClipboardViaDaemon = pushClipboardViaDaemon;
17056
+ exports.pushFilePreviewViaDaemon = pushFilePreviewViaDaemon;
16994
17057
  exports.readLocalHappyAgentCredentials = readLocalHappyAgentCredentials;
16995
17058
  exports.readVhTerminal = readVhTerminal;
16996
17059
  exports.resolveCliUpdateCheckInterval = resolveCliUpdateCheckInterval;
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as readDaemonState, q as clearDaemonState, s as decryptBlob, t as prepareClipboardText, g as readSettings, r as readPersistedSessions, u as credentialRelayProblem, p as packageJson, v as readTrackedClaudeSessionIds, x as readTrackedCodexThreadIds, y as libsodiumEncryptForPublicKey, z as libsodiumPublicKeyFromSecretKey, A as getRandomBytes, B as redactLogValue } from './limits-DT5Yk_UH.mjs';
2
+ import { l as logger, g as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as readDaemonState, q as clearDaemonState, s as decryptBlob, t as prepareClipboardText, u as prepareClipboardHistoryText, f as readSettings, r as readPersistedSessions, v as credentialRelayProblem, p as packageJson, x as readTrackedClaudeSessionIds, y as readTrackedCodexThreadIds, z as libsodiumEncryptForPublicKey, A as libsodiumPublicKeyFromSecretKey, B as getRandomBytes, D as redactLogValue } from './limits-uABU-yq3.mjs';
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { io } from 'socket.io-client';
5
5
  import * as z from 'zod';
@@ -10,9 +10,9 @@ import { spawn as spawn$1, exec, execSync } from 'child_process';
10
10
  import { promisify } from 'util';
11
11
  import { mkdir, writeFile, appendFile, stat, rename, unlink, readdir, readFile } from 'fs/promises';
12
12
  import { randomUUID, createHash } from 'crypto';
13
- import { dirname, resolve, join, sep } from 'path';
13
+ import { resolve, join, sep } from 'path';
14
14
  import { spawn } from 'cross-spawn';
15
- import { fileURLToPath } from 'url';
15
+ import { p as projectPath } from './projectPath-CSpqWdxp.mjs';
16
16
  import os, { platform } from 'os';
17
17
  import { stat as stat$1, readdir as readdir$1, lstat, open, copyFile, unlink as unlink$1, readFile as readFile$1, rename as rename$1, writeFile as writeFile$1 } from 'node:fs/promises';
18
18
  import { join as join$1, isAbsolute, resolve as resolve$1 } from 'node:path';
@@ -1356,10 +1356,38 @@ class RpcHandlerManager {
1356
1356
  }
1357
1357
  }
1358
1358
 
1359
- const __dirname$1 = dirname(fileURLToPath(import.meta.url));
1360
- function projectPath() {
1361
- const path = resolve(__dirname$1, "..");
1362
- return path;
1359
+ function describeSocketAckDrop(drop) {
1360
+ switch (drop.reason) {
1361
+ case "no-ack":
1362
+ return "no ack callback on the frame (re-delivered after a transport close?); response dropped";
1363
+ case "handler-threw":
1364
+ return `handler threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
1365
+ case "ack-threw":
1366
+ return `ack callback threw: ${drop.error instanceof Error ? drop.error.message : String(drop.error)}`;
1367
+ }
1368
+ }
1369
+ function safeAck(ack, onDrop) {
1370
+ return (value) => {
1371
+ if (typeof ack !== "function") {
1372
+ onDrop({ reason: "no-ack" });
1373
+ return;
1374
+ }
1375
+ try {
1376
+ ack(value);
1377
+ } catch (error) {
1378
+ onDrop({ reason: "ack-threw", error });
1379
+ }
1380
+ };
1381
+ }
1382
+ async function answerSocketRequest(ack, run, onDrop) {
1383
+ let value;
1384
+ try {
1385
+ value = await run();
1386
+ } catch (error) {
1387
+ onDrop({ reason: "handler-threw", error });
1388
+ return;
1389
+ }
1390
+ safeAck(ack, onDrop)(value);
1363
1391
  }
1364
1392
 
1365
1393
  function run$1(args, options) {
@@ -3052,13 +3080,18 @@ async function notifyDaemonTurnEvent(sessionId, event) {
3052
3080
  async function stopDaemonHttp() {
3053
3081
  await daemonPost("/stop");
3054
3082
  }
3055
- async function pushClipboardViaDaemon(text) {
3056
- const result = await daemonPost("/clipboard", { text });
3083
+ async function pushClipboardViaDaemon(text, terminalId) {
3084
+ const result = await daemonPost("/clipboard", { text, ...terminalId ? { terminalId } : {} });
3057
3085
  if (result?.error && result.delivered === void 0) {
3058
3086
  return { delivered: false, error: result.error };
3059
3087
  }
3060
3088
  return result;
3061
3089
  }
3090
+ async function pushFilePreviewViaDaemon(terminalId, path, mode = "file") {
3091
+ const result = await daemonPost("/file-preview", { terminalId, path, mode });
3092
+ if (result?.error) return { delivered: false, error: result.error };
3093
+ return { delivered: result?.delivered === true, ...result?.delivered === true ? {} : { error: "preview was not delivered" } };
3094
+ }
3062
3095
  async function setTerminalTitleViaDaemon(terminalId, title, ifAbsent = false) {
3063
3096
  const result = await daemonPost("/terminal-title", { terminalId, title, ifAbsent });
3064
3097
  if (result?.error) {
@@ -3259,7 +3292,11 @@ class ApiSessionClient extends EventEmitter {
3259
3292
  void this.connectSessionRelay();
3260
3293
  });
3261
3294
  this.socket.on("rpc-request", async (data, callback) => {
3262
- callback(await this.rpcHandlerManager.handleRequest(data));
3295
+ await answerSocketRequest(
3296
+ callback,
3297
+ () => this.rpcHandlerManager.handleRequest(data),
3298
+ (drop) => logger.debug(`[API] RPC ${data?.method} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
3299
+ );
3263
3300
  });
3264
3301
  this.socket.on("disconnect", (reason) => {
3265
3302
  logger.debug(`[API] Socket disconnected: ${reason}`);
@@ -3695,9 +3732,14 @@ class ApiSessionClient extends EventEmitter {
3695
3732
  logger.debug(`[API] Session connected to regional relay ${assignment.relayId}`);
3696
3733
  });
3697
3734
  relaySocket.on("rpc-request", async (data, callback) => {
3698
- callback(await this.rpcHandlerManager.handleRequest(data));
3735
+ await answerSocketRequest(
3736
+ callback,
3737
+ () => this.rpcHandlerManager.handleRequest(data),
3738
+ (drop) => logger.debug(`[API] Relay RPC ${data?.method} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
3739
+ );
3699
3740
  });
3700
- relaySocket.on("session-message-deliver", async (data, callback) => {
3741
+ relaySocket.on("session-message-deliver", async (data, rawCallback) => {
3742
+ const callback = safeAck(rawCallback, (drop) => logger.debug(`[API] session-message-deliver unanswered \u2014 ${describeSocketAckDrop(drop)}`));
3701
3743
  if (data?.sessionId !== this.sessionId || !Array.isArray(data.messages) || data.messages.length === 0 || data.messages.length > 50) {
3702
3744
  callback({ ok: false, error: "Invalid session message request" });
3703
3745
  return;
@@ -3998,12 +4040,15 @@ class ApiSessionClient extends EventEmitter {
3998
4040
  */
3999
4041
  pushClipboard(text) {
4000
4042
  const prepared = prepareClipboardText(text);
4043
+ const history = prepareClipboardHistoryText(text);
4001
4044
  if (!this.socket.connected) {
4002
4045
  return { delivered: false, truncated: prepared.truncated, totalBytes: prepared.totalBytes };
4003
4046
  }
4004
4047
  this.socket.emit("clipboard-push", {
4005
4048
  payload: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, prepared.text)),
4006
4049
  enc: true,
4050
+ historyPayload: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, history.text)),
4051
+ historyTruncated: history.truncated,
4007
4052
  truncated: prepared.truncated,
4008
4053
  totalBytes: prepared.totalBytes
4009
4054
  });
@@ -14818,19 +14863,34 @@ class ApiMachineClient {
14818
14863
  * (terminal-path claude → `very-happy mcp` → daemon /clipboard → here).
14819
14864
  * Encrypted with the per-machine key; the server relays without reading.
14820
14865
  */
14821
- pushClipboard(text) {
14866
+ pushClipboard(text, terminalId) {
14822
14867
  const prepared = prepareClipboardText(text);
14868
+ const history = prepareClipboardHistoryText(text);
14823
14869
  if (!this.socket?.connected) {
14824
14870
  return { delivered: false, truncated: prepared.truncated, totalBytes: prepared.totalBytes, error: "daemon is not connected to the server" };
14825
14871
  }
14826
14872
  this.socket.emit("clipboard-push", {
14873
+ ...terminalId ? { terminalId } : {},
14827
14874
  payload: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, prepared.text)),
14828
14875
  enc: true,
14876
+ historyPayload: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, history.text)),
14877
+ historyTruncated: history.truncated,
14829
14878
  truncated: prepared.truncated,
14830
14879
  totalBytes: prepared.totalBytes
14831
14880
  });
14832
14881
  return { delivered: true, truncated: prepared.truncated, totalBytes: prepared.totalBytes };
14833
14882
  }
14883
+ /** Queue a terminal preview with the machine key. Delivery does not prove a browser opened it. */
14884
+ pushFilePreview(terminalId, path, mode = "file") {
14885
+ if (!this.socket?.connected) return { delivered: false, error: "daemon is not connected to the server" };
14886
+ this.socket.emit("file-preview-push", {
14887
+ terminalId,
14888
+ payload: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, path)),
14889
+ enc: true,
14890
+ mode
14891
+ });
14892
+ return { delivered: true };
14893
+ }
14834
14894
  /** Encrypt one base64 terminal payload with the per-machine key (same scheme
14835
14895
  * as the live output stream) → base64 ciphertext. Used for both live output
14836
14896
  * and the open-terminal snapshot/replay payloads. */
@@ -15712,7 +15772,11 @@ class ApiMachineClient {
15712
15772
  bindRpcRequestHandler(socket, transport) {
15713
15773
  socket.on("rpc-request", async (data, callback) => {
15714
15774
  logger.debugLargeJson(`[API MACHINE] Received RPC request via ${transport}:`, data);
15715
- callback(await this.rpcHandlerManager.handleRequest(data));
15775
+ await answerSocketRequest(
15776
+ callback,
15777
+ () => this.rpcHandlerManager.handleRequest(data),
15778
+ (drop) => logger.debug(`[API MACHINE] RPC ${data?.method} via ${transport} unanswered \u2014 ${describeSocketAckDrop(drop)}`)
15779
+ );
15716
15780
  });
15717
15781
  }
15718
15782
  bindMachineCommandHandlers(socket) {
@@ -16916,4 +16980,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
16916
16980
  }).passthrough()
16917
16981
  ]);
16918
16982
 
16919
- export { shellescape as $, ApiClient as A, BUILTIN_TODO_SKILL as B, getLocalHappyAgentCredentialPath as C, readLocalHappyAgentCredentials as D, parseExactVersion as E, compareExactVersions as F, extractCompleteLines as G, decideBackfill as H, InvalidateSync as I, parseTerminalHookPayload as J, tmuxArgs as K, scrubTmuxClientEnv as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, decideMirrorBinding as N, MIRROR_BACKFILL_LINES_DEFAULT as O, mirrorLineKey as P, mirrorLocalId as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, listDaemonSessions as T, stopDaemonSession as U, detectCLIAvailability as V, detectResumeSupport as W, isDaemonRunningCurrentlyInstalledHappyVersion as X, stopDaemon as Y, TurnActivityTracker as Z, summarizeSpawnSessionForLog as _, ApiSessionClient as a, fetchCliUpdateState as a0, resolveCliUpdateCheckInterval as a1, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as a2, cleanupDaemonState as a3, listVhTerminals as a4, isValidTerminalId as a5, readVhTerminal as a6, sendToVhTerminal as a7, ContextUsageSchema as a8, TITLE_PROMPT_PREFIX as a9, BOARD_ANALYZER_PROMPT_PREFIX as aa, startOfflineReconnection as ab, CodexAppServerClient as ac, api as ad, streamKeyOf as b, checkIfDaemonRunningAndCleanupStaleState as c, createEnvelope as d, createBuiltinTodoClient as e, pushClipboardViaDaemon as f, setTerminalTitleViaDaemon as g, errorLogMetadata as h, isValidSessionId as i, contentLogMetadata as j, connectionState as k, BUILTIN_TODO_DISCOVERY as l, delay as m, notifyDaemonSessionStarted as n, notifyDaemonSessionEvent as o, projectPath as p, getProjectPath as q, initializeSandbox as r, spawnDaemonSession as s, AsyncLock as t, notifyDaemonClaudeAuthFailed as u, commandOnPath as v, wrapCommand as w, deriveLocalCliUpdateSummary as x, AGENT_VERSION_SOURCES as y, isClaudeSessionId as z };
16983
+ export { shellescape as $, ApiClient as A, BUILTIN_TODO_SKILL as B, getLocalHappyAgentCredentialPath as C, readLocalHappyAgentCredentials as D, parseExactVersion as E, compareExactVersions as F, extractCompleteLines as G, decideBackfill as H, InvalidateSync as I, parseTerminalHookPayload as J, tmuxArgs as K, scrubTmuxClientEnv as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, decideMirrorBinding as N, MIRROR_BACKFILL_LINES_DEFAULT as O, mirrorLineKey as P, mirrorLocalId as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, listDaemonSessions as T, stopDaemonSession as U, detectCLIAvailability as V, detectResumeSupport as W, isDaemonRunningCurrentlyInstalledHappyVersion as X, stopDaemon as Y, TurnActivityTracker as Z, summarizeSpawnSessionForLog as _, ApiSessionClient as a, fetchCliUpdateState as a0, resolveCliUpdateCheckInterval as a1, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as a2, cleanupDaemonState as a3, listVhTerminals as a4, isValidTerminalId as a5, readVhTerminal as a6, sendToVhTerminal as a7, ContextUsageSchema as a8, TITLE_PROMPT_PREFIX as a9, BOARD_ANALYZER_PROMPT_PREFIX as aa, startOfflineReconnection as ab, CodexAppServerClient as ac, api as ad, streamKeyOf as b, checkIfDaemonRunningAndCleanupStaleState as c, createEnvelope as d, createBuiltinTodoClient as e, pushFilePreviewViaDaemon as f, setTerminalTitleViaDaemon as g, connectionState as h, isValidSessionId as i, BUILTIN_TODO_DISCOVERY as j, errorLogMetadata as k, contentLogMetadata as l, delay as m, notifyDaemonSessionStarted as n, notifyDaemonSessionEvent as o, pushClipboardViaDaemon as p, getProjectPath as q, initializeSandbox as r, spawnDaemonSession as s, AsyncLock as t, notifyDaemonClaudeAuthFailed as u, commandOnPath as v, wrapCommand as w, deriveLocalCliUpdateSummary as x, AGENT_VERSION_SOURCES as y, isClaudeSessionId as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "very-happy-cli",
3
- "version": "0.2.141",
3
+ "version": "0.2.143",
4
4
  "description": "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "contributors": [
@@ -25,6 +25,10 @@
25
25
  "module": "./dist/index.mjs",
26
26
  "types": "./dist/index.d.cts",
27
27
  "exports": {
28
+ "./pi-extension": {
29
+ "types": "./dist/piExtension.d.mts",
30
+ "default": "./dist/piExtension.mjs"
31
+ },
28
32
  ".": {
29
33
  "require": {
30
34
  "types": "./dist/index.d.cts",
@@ -113,12 +117,12 @@
113
117
  "zod": "^4.0.0"
114
118
  },
115
119
  "optionalDependencies": {
116
- "very-happy-tools-arm64-darwin": "0.2.141",
117
- "very-happy-tools-x64-darwin": "0.2.141",
118
- "very-happy-tools-arm64-linux": "0.2.141",
119
- "very-happy-tools-x64-linux": "0.2.141",
120
- "very-happy-tools-arm64-win32": "0.2.141",
121
- "very-happy-tools-x64-win32": "0.2.141"
120
+ "very-happy-tools-arm64-darwin": "0.2.143",
121
+ "very-happy-tools-x64-darwin": "0.2.143",
122
+ "very-happy-tools-arm64-linux": "0.2.143",
123
+ "very-happy-tools-x64-linux": "0.2.143",
124
+ "very-happy-tools-arm64-win32": "0.2.143",
125
+ "very-happy-tools-x64-win32": "0.2.143"
122
126
  },
123
127
  "devDependencies": {
124
128
  "@slopus/happy-wire": "workspace:*",