happy-coder 0.10.0-2 → 0.10.0-3

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 (45) hide show
  1. package/bin/happy-mcp.mjs +32 -0
  2. package/dist/codex/happyMcpStdioBridge.cjs +80 -0
  3. package/dist/codex/happyMcpStdioBridge.d.cts +2 -0
  4. package/dist/codex/happyMcpStdioBridge.d.mts +2 -0
  5. package/dist/codex/happyMcpStdioBridge.mjs +78 -0
  6. package/dist/index-DPVbp4Yx.mjs +6048 -0
  7. package/dist/index-tqOLc1Il.cjs +6056 -0
  8. package/dist/index.cjs +33 -6024
  9. package/dist/index.mjs +33 -6024
  10. package/dist/lib.cjs +2 -1
  11. package/dist/lib.d.cts +12 -2
  12. package/dist/lib.d.mts +12 -2
  13. package/dist/lib.mjs +2 -1
  14. package/dist/runCodex-BxLD6H6G.cjs +1155 -0
  15. package/dist/runCodex-C07HQlsW.mjs +1153 -0
  16. package/dist/{types-WP9wteZE.cjs → types-CsJGQvQ3.cjs} +142 -45
  17. package/dist/{types-xfXKJHdM.mjs → types-xds_c-JJ.mjs} +106 -9
  18. package/package.json +18 -5
  19. package/scripts/ripgrep_launcher.cjs +2 -26
  20. package/scripts/unpack-tools.cjs +163 -0
  21. package/tools/archives/difftastic-LICENSE +21 -0
  22. package/tools/archives/difftastic-arm64-darwin.tar.gz +0 -0
  23. package/tools/archives/difftastic-arm64-linux.tar.gz +0 -0
  24. package/tools/archives/difftastic-x64-darwin.tar.gz +0 -0
  25. package/tools/archives/difftastic-x64-linux.tar.gz +0 -0
  26. package/tools/archives/difftastic-x64-win32.tar.gz +0 -0
  27. package/tools/archives/ripgrep-arm64-darwin.tar.gz +0 -0
  28. package/tools/archives/ripgrep-arm64-linux.tar.gz +0 -0
  29. package/tools/archives/ripgrep-x64-darwin.tar.gz +0 -0
  30. package/tools/archives/ripgrep-x64-linux.tar.gz +0 -0
  31. package/tools/archives/ripgrep-x64-win32.tar.gz +0 -0
  32. package/tools/licenses/difftastic-LICENSE +21 -0
  33. package/tools/licenses/ripgrep-LICENSE +3 -0
  34. package/tools/unpacked/difft +0 -0
  35. package/ripgrep/arm64-linux/rg +0 -0
  36. package/ripgrep/arm64-linux/ripgrep.node +0 -0
  37. package/ripgrep/x64-darwin/rg +0 -0
  38. package/ripgrep/x64-darwin/ripgrep.node +0 -0
  39. package/ripgrep/x64-linux/rg +0 -0
  40. package/ripgrep/x64-linux/ripgrep.node +0 -0
  41. package/ripgrep/x64-win32/rg.exe +0 -0
  42. package/ripgrep/x64-win32/ripgrep.node +0 -0
  43. /package/{ripgrep/COPYING → tools/archives/ripgrep-LICENSE} +0 -0
  44. /package/{ripgrep/arm64-darwin → tools/unpacked}/rg +0 -0
  45. /package/{ripgrep/arm64-darwin → tools/unpacked}/ripgrep.node +0 -0
@@ -2,8 +2,8 @@
2
2
 
3
3
  var axios = require('axios');
4
4
  var chalk = require('chalk');
5
- var fs = require('fs');
6
- var node_fs = require('node:fs');
5
+ var fs$1 = require('fs');
6
+ var fs = require('node:fs');
7
7
  var os = require('node:os');
8
8
  var node_path = require('node:path');
9
9
  var promises = require('node:fs/promises');
@@ -14,10 +14,11 @@ var node_events = require('node:events');
14
14
  var socket_ioClient = require('socket.io-client');
15
15
  var child_process = require('child_process');
16
16
  var util = require('util');
17
- var fs$1 = require('fs/promises');
17
+ var fs$2 = require('fs/promises');
18
18
  var crypto = require('crypto');
19
19
  var path = require('path');
20
20
  var url = require('url');
21
+ var os$1 = require('os');
21
22
  var expoServerSdk = require('expo-server-sdk');
22
23
 
23
24
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -41,8 +42,8 @@ function _interopNamespaceDefault(e) {
41
42
  var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
42
43
 
43
44
  var name = "happy-coder";
44
- var version = "0.10.0-2";
45
- var description = "Claude Code session sharing CLI";
45
+ var version = "0.10.0-3";
46
+ var description = "Mobile and Web client for Claude Code and Codex";
46
47
  var author = "Kirill Dubovitskiy";
47
48
  var license = "MIT";
48
49
  var type = "module";
@@ -50,7 +51,8 @@ var homepage = "https://github.com/slopus/happy-cli";
50
51
  var bugs = "https://github.com/slopus/happy-cli/issues";
51
52
  var repository = "slopus/happy-cli";
52
53
  var bin = {
53
- happy: "./bin/happy.mjs"
54
+ happy: "./bin/happy.mjs",
55
+ "happy-mcp": "./bin/happy-mcp.mjs"
54
56
  };
55
57
  var main = "./dist/index.cjs";
56
58
  var module$1 = "./dist/index.mjs";
@@ -75,13 +77,23 @@ var exports$1 = {
75
77
  types: "./dist/lib.d.mts",
76
78
  "default": "./dist/lib.mjs"
77
79
  }
80
+ },
81
+ "./codex/happyMcpStdioBridge": {
82
+ require: {
83
+ types: "./dist/codex/happyMcpStdioBridge.d.cts",
84
+ "default": "./dist/codex/happyMcpStdioBridge.cjs"
85
+ },
86
+ "import": {
87
+ types: "./dist/codex/happyMcpStdioBridge.d.mts",
88
+ "default": "./dist/codex/happyMcpStdioBridge.mjs"
89
+ }
78
90
  }
79
91
  };
80
92
  var files = [
81
93
  "dist",
82
94
  "bin",
83
95
  "scripts",
84
- "ripgrep",
96
+ "tools",
85
97
  "package.json"
86
98
  ];
87
99
  var scripts = {
@@ -94,7 +106,8 @@ var scripts = {
94
106
  "dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
95
107
  "dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
96
108
  prepublishOnly: "yarn build && yarn test",
97
- release: "release-it"
109
+ release: "release-it",
110
+ postinstall: "node scripts/unpack-tools.cjs"
98
111
  };
99
112
  var dependencies = {
100
113
  "@anthropic-ai/claude-code": "^1.0.102",
@@ -120,6 +133,7 @@ var dependencies = {
120
133
  "qrcode-terminal": "^0.12.0",
121
134
  react: "^19.1.1",
122
135
  "socket.io-client": "^4.8.1",
136
+ tar: "^7.4.3",
123
137
  tweetnacl: "^1.0.3",
124
138
  zod: "^3.23.8"
125
139
  };
@@ -200,11 +214,11 @@ class Configuration {
200
214
  this.daemonLockFile = node_path.join(this.happyHomeDir, "daemon.state.json.lock");
201
215
  this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
202
216
  this.currentCliVersion = packageJson.version;
203
- if (!node_fs.existsSync(this.happyHomeDir)) {
204
- node_fs.mkdirSync(this.happyHomeDir, { recursive: true });
217
+ if (!fs.existsSync(this.happyHomeDir)) {
218
+ fs.mkdirSync(this.happyHomeDir, { recursive: true });
205
219
  }
206
- if (!node_fs.existsSync(this.logsDir)) {
207
- node_fs.mkdirSync(this.logsDir, { recursive: true });
220
+ if (!fs.existsSync(this.logsDir)) {
221
+ fs.mkdirSync(this.logsDir, { recursive: true });
208
222
  }
209
223
  }
210
224
  }
@@ -251,7 +265,7 @@ const defaultSettings = {
251
265
  onboardingCompleted: false
252
266
  };
253
267
  async function readSettings() {
254
- if (!node_fs.existsSync(configuration.settingsFile)) {
268
+ if (!fs.existsSync(configuration.settingsFile)) {
255
269
  return { ...defaultSettings };
256
270
  }
257
271
  try {
@@ -271,7 +285,7 @@ async function updateSettings(updater) {
271
285
  let attempts = 0;
272
286
  while (attempts < MAX_LOCK_ATTEMPTS) {
273
287
  try {
274
- fileHandle = await promises.open(lockFile, node_fs.constants.O_CREAT | node_fs.constants.O_EXCL | node_fs.constants.O_WRONLY);
288
+ fileHandle = await promises.open(lockFile, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
275
289
  break;
276
290
  } catch (err) {
277
291
  if (err.code === "EEXIST") {
@@ -296,7 +310,7 @@ async function updateSettings(updater) {
296
310
  try {
297
311
  const current = await readSettings() || { ...defaultSettings };
298
312
  const updated = await updater(current);
299
- if (!node_fs.existsSync(configuration.happyHomeDir)) {
313
+ if (!fs.existsSync(configuration.happyHomeDir)) {
300
314
  await promises.mkdir(configuration.happyHomeDir, { recursive: true });
301
315
  }
302
316
  await promises.writeFile(tmpFile, JSON.stringify(updated, null, 2));
@@ -313,7 +327,7 @@ const credentialsSchema = z__namespace.object({
313
327
  token: z__namespace.string()
314
328
  });
315
329
  async function readCredentials() {
316
- if (!node_fs.existsSync(configuration.privateKeyFile)) {
330
+ if (!fs.existsSync(configuration.privateKeyFile)) {
317
331
  return null;
318
332
  }
319
333
  try {
@@ -328,7 +342,7 @@ async function readCredentials() {
328
342
  }
329
343
  }
330
344
  async function writeCredentials(credentials) {
331
- if (!node_fs.existsSync(configuration.happyHomeDir)) {
345
+ if (!fs.existsSync(configuration.happyHomeDir)) {
332
346
  await promises.mkdir(configuration.happyHomeDir, { recursive: true });
333
347
  }
334
348
  await promises.writeFile(configuration.privateKeyFile, JSON.stringify({
@@ -337,7 +351,7 @@ async function writeCredentials(credentials) {
337
351
  }, null, 2));
338
352
  }
339
353
  async function clearCredentials() {
340
- if (node_fs.existsSync(configuration.privateKeyFile)) {
354
+ if (fs.existsSync(configuration.privateKeyFile)) {
341
355
  await promises.unlink(configuration.privateKeyFile);
342
356
  }
343
357
  }
@@ -349,7 +363,7 @@ async function clearMachineId() {
349
363
  }
350
364
  async function readDaemonState() {
351
365
  try {
352
- if (!node_fs.existsSync(configuration.daemonStateFile)) {
366
+ if (!fs.existsSync(configuration.daemonStateFile)) {
353
367
  return null;
354
368
  }
355
369
  const content = await promises.readFile(configuration.daemonStateFile, "utf-8");
@@ -360,13 +374,13 @@ async function readDaemonState() {
360
374
  }
361
375
  }
362
376
  function writeDaemonState(state) {
363
- node_fs.writeFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2), "utf-8");
377
+ fs.writeFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2), "utf-8");
364
378
  }
365
379
  async function clearDaemonState() {
366
- if (node_fs.existsSync(configuration.daemonStateFile)) {
380
+ if (fs.existsSync(configuration.daemonStateFile)) {
367
381
  await promises.unlink(configuration.daemonStateFile);
368
382
  }
369
- if (node_fs.existsSync(configuration.daemonLockFile)) {
383
+ if (fs.existsSync(configuration.daemonLockFile)) {
370
384
  try {
371
385
  await promises.unlink(configuration.daemonLockFile);
372
386
  } catch {
@@ -378,19 +392,19 @@ async function acquireDaemonLock(maxAttempts = 5, delayIncrementMs = 200) {
378
392
  try {
379
393
  const fileHandle = await promises.open(
380
394
  configuration.daemonLockFile,
381
- node_fs.constants.O_CREAT | node_fs.constants.O_EXCL | node_fs.constants.O_WRONLY
395
+ fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY
382
396
  );
383
397
  await fileHandle.writeFile(String(process.pid));
384
398
  return fileHandle;
385
399
  } catch (error) {
386
400
  if (error.code === "EEXIST") {
387
401
  try {
388
- const lockPid = node_fs.readFileSync(configuration.daemonLockFile, "utf-8").trim();
402
+ const lockPid = fs.readFileSync(configuration.daemonLockFile, "utf-8").trim();
389
403
  if (lockPid && !isNaN(Number(lockPid))) {
390
404
  try {
391
405
  process.kill(Number(lockPid), 0);
392
406
  } catch {
393
- node_fs.unlinkSync(configuration.daemonLockFile);
407
+ fs.unlinkSync(configuration.daemonLockFile);
394
408
  continue;
395
409
  }
396
410
  }
@@ -412,8 +426,8 @@ async function releaseDaemonLock(lockHandle) {
412
426
  } catch {
413
427
  }
414
428
  try {
415
- if (node_fs.existsSync(configuration.daemonLockFile)) {
416
- node_fs.unlinkSync(configuration.daemonLockFile);
429
+ if (fs.existsSync(configuration.daemonLockFile)) {
430
+ fs.unlinkSync(configuration.daemonLockFile);
417
431
  }
418
432
  } catch {
419
433
  }
@@ -503,6 +517,13 @@ class Logger {
503
517
  this.logToConsole("info", "[DEV]", message, ...args);
504
518
  }
505
519
  }
520
+ warn(message, ...args) {
521
+ this.logToConsole("warn", "", message, ...args);
522
+ this.debug(`[WARN] ${message}`, ...args);
523
+ }
524
+ getLogPath() {
525
+ return this.logFilePath;
526
+ }
506
527
  logToConsole(level, prefix, message, ...args) {
507
528
  switch (level) {
508
529
  case "debug": {
@@ -561,7 +582,7 @@ class Logger {
561
582
  });
562
583
  }
563
584
  try {
564
- fs.appendFileSync(this.logFilePath, logLine);
585
+ fs$1.appendFileSync(this.logFilePath, logLine);
565
586
  } catch (appendError) {
566
587
  if (process.env.DEBUG) {
567
588
  console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
@@ -574,12 +595,12 @@ let logger = new Logger();
574
595
  async function listDaemonLogFiles(limit = 50) {
575
596
  try {
576
597
  const logsDir = configuration.logsDir;
577
- if (!node_fs.existsSync(logsDir)) {
598
+ if (!fs.existsSync(logsDir)) {
578
599
  return [];
579
600
  }
580
- const logs = node_fs.readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
601
+ const logs = fs.readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
581
602
  const fullPath = node_path.join(logsDir, file);
582
- const stats = node_fs.statSync(fullPath);
603
+ const stats = fs.statSync(fullPath);
583
604
  return { file, path: fullPath, modified: stats.mtime };
584
605
  }).sort((a, b) => b.modified.getTime() - a.modified.getTime());
585
606
  try {
@@ -587,8 +608,8 @@ async function listDaemonLogFiles(limit = 50) {
587
608
  if (!state) {
588
609
  return logs;
589
610
  }
590
- if (state.daemonLogPath && node_fs.existsSync(state.daemonLogPath)) {
591
- const stats = node_fs.statSync(state.daemonLogPath);
611
+ if (state.daemonLogPath && fs.existsSync(state.daemonLogPath)) {
612
+ const stats = fs.statSync(state.daemonLogPath);
592
613
  const persisted = {
593
614
  file: node_path.basename(state.daemonLogPath),
594
615
  path: state.daemonLogPath,
@@ -694,7 +715,8 @@ z.z.object({
694
715
  platform: z.z.string(),
695
716
  happyCliVersion: z.z.string(),
696
717
  homeDir: z.z.string(),
697
- happyHomeDir: z.z.string()
718
+ happyHomeDir: z.z.string(),
719
+ happyLibDir: z.z.string()
698
720
  });
699
721
  z.z.object({
700
722
  status: z.z.union([
@@ -952,13 +974,13 @@ class RpcHandlerManager {
952
974
  }
953
975
  }
954
976
 
955
- 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-WP9wteZE.cjs', document.baseURI).href))));
977
+ 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-CsJGQvQ3.cjs', document.baseURI).href))));
956
978
  function projectPath() {
957
979
  const path$1 = path.resolve(__dirname$1, "..");
958
980
  return path$1;
959
981
  }
960
982
 
961
- function run(args, options) {
983
+ function run$1(args, options) {
962
984
  const RUNNER_PATH = path.resolve(path.join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
963
985
  return new Promise((resolve2, reject) => {
964
986
  const child = child_process.spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
@@ -986,6 +1008,44 @@ function run(args, options) {
986
1008
  });
987
1009
  }
988
1010
 
1011
+ function getBinaryPath() {
1012
+ const platformName = os$1.platform();
1013
+ const binaryName = platformName === "win32" ? "difft.exe" : "difft";
1014
+ return path.resolve(path.join(projectPath(), "tools", "unpacked", binaryName));
1015
+ }
1016
+ function run(args, options) {
1017
+ const binaryPath = getBinaryPath();
1018
+ return new Promise((resolve2, reject) => {
1019
+ const child = child_process.spawn(binaryPath, args, {
1020
+ stdio: ["pipe", "pipe", "pipe"],
1021
+ cwd: options?.cwd,
1022
+ env: {
1023
+ ...process.env,
1024
+ // Force color output when needed
1025
+ FORCE_COLOR: "1"
1026
+ }
1027
+ });
1028
+ let stdout = "";
1029
+ let stderr = "";
1030
+ child.stdout.on("data", (data) => {
1031
+ stdout += data.toString();
1032
+ });
1033
+ child.stderr.on("data", (data) => {
1034
+ stderr += data.toString();
1035
+ });
1036
+ child.on("close", (code) => {
1037
+ resolve2({
1038
+ exitCode: code || 0,
1039
+ stdout,
1040
+ stderr
1041
+ });
1042
+ });
1043
+ child.on("error", (err) => {
1044
+ reject(err);
1045
+ });
1046
+ });
1047
+ }
1048
+
989
1049
  const execAsync = util.promisify(child_process.exec);
990
1050
  function registerCommonHandlers(rpcHandlerManager) {
991
1051
  rpcHandlerManager.registerHandler("bash", async (data) => {
@@ -1026,7 +1086,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1026
1086
  rpcHandlerManager.registerHandler("readFile", async (data) => {
1027
1087
  logger.debug("Read file request:", data.path);
1028
1088
  try {
1029
- const buffer = await fs$1.readFile(data.path);
1089
+ const buffer = await fs$2.readFile(data.path);
1030
1090
  const content = buffer.toString("base64");
1031
1091
  return { success: true, content };
1032
1092
  } catch (error) {
@@ -1039,7 +1099,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1039
1099
  try {
1040
1100
  if (data.expectedHash !== null && data.expectedHash !== void 0) {
1041
1101
  try {
1042
- const existingBuffer = await fs$1.readFile(data.path);
1102
+ const existingBuffer = await fs$2.readFile(data.path);
1043
1103
  const existingHash = crypto.createHash("sha256").update(existingBuffer).digest("hex");
1044
1104
  if (existingHash !== data.expectedHash) {
1045
1105
  return {
@@ -1059,7 +1119,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1059
1119
  }
1060
1120
  } else {
1061
1121
  try {
1062
- await fs$1.stat(data.path);
1122
+ await fs$2.stat(data.path);
1063
1123
  return {
1064
1124
  success: false,
1065
1125
  error: "File already exists but was expected to be new"
@@ -1072,7 +1132,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1072
1132
  }
1073
1133
  }
1074
1134
  const buffer = Buffer.from(data.content, "base64");
1075
- await fs$1.writeFile(data.path, buffer);
1135
+ await fs$2.writeFile(data.path, buffer);
1076
1136
  const hash = crypto.createHash("sha256").update(buffer).digest("hex");
1077
1137
  return { success: true, hash };
1078
1138
  } catch (error) {
@@ -1083,7 +1143,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1083
1143
  rpcHandlerManager.registerHandler("listDirectory", async (data) => {
1084
1144
  logger.debug("List directory request:", data.path);
1085
1145
  try {
1086
- const entries = await fs$1.readdir(data.path, { withFileTypes: true });
1146
+ const entries = await fs$2.readdir(data.path, { withFileTypes: true });
1087
1147
  const directoryEntries = await Promise.all(
1088
1148
  entries.map(async (entry) => {
1089
1149
  const fullPath = path.join(data.path, entry.name);
@@ -1096,7 +1156,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1096
1156
  type = "file";
1097
1157
  }
1098
1158
  try {
1099
- const stats = await fs$1.stat(fullPath);
1159
+ const stats = await fs$2.stat(fullPath);
1100
1160
  size = stats.size;
1101
1161
  modified = stats.mtime.getTime();
1102
1162
  } catch (error) {
@@ -1125,7 +1185,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1125
1185
  logger.debug("Get directory tree request:", data.path, "maxDepth:", data.maxDepth);
1126
1186
  async function buildTree(path$1, name, currentDepth) {
1127
1187
  try {
1128
- const stats = await fs$1.stat(path$1);
1188
+ const stats = await fs$2.stat(path$1);
1129
1189
  const node = {
1130
1190
  name,
1131
1191
  path: path$1,
@@ -1134,7 +1194,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1134
1194
  modified: stats.mtime.getTime()
1135
1195
  };
1136
1196
  if (stats.isDirectory() && currentDepth < data.maxDepth) {
1137
- const entries = await fs$1.readdir(path$1, { withFileTypes: true });
1197
+ const entries = await fs$2.readdir(path$1, { withFileTypes: true });
1138
1198
  const children = [];
1139
1199
  await Promise.all(
1140
1200
  entries.map(async (entry) => {
@@ -1180,7 +1240,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1180
1240
  rpcHandlerManager.registerHandler("ripgrep", async (data) => {
1181
1241
  logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
1182
1242
  try {
1183
- const result = await run(data.args, { cwd: data.cwd });
1243
+ const result = await run$1(data.args, { cwd: data.cwd });
1184
1244
  return {
1185
1245
  success: true,
1186
1246
  exitCode: result.exitCode,
@@ -1195,6 +1255,24 @@ function registerCommonHandlers(rpcHandlerManager) {
1195
1255
  };
1196
1256
  }
1197
1257
  });
1258
+ rpcHandlerManager.registerHandler("difftastic", async (data) => {
1259
+ logger.debug("Difftastic request with args:", data.args, "cwd:", data.cwd);
1260
+ try {
1261
+ const result = await run(data.args, { cwd: data.cwd });
1262
+ return {
1263
+ success: true,
1264
+ exitCode: result.exitCode,
1265
+ stdout: result.stdout.toString(),
1266
+ stderr: result.stderr.toString()
1267
+ };
1268
+ } catch (error) {
1269
+ logger.debug("Failed to run difftastic:", error);
1270
+ return {
1271
+ success: false,
1272
+ error: error instanceof Error ? error.message : "Failed to run difftastic"
1273
+ };
1274
+ }
1275
+ });
1198
1276
  }
1199
1277
 
1200
1278
  class ApiSessionClient extends node_events.EventEmitter {
@@ -1358,6 +1436,24 @@ class ApiSessionClient extends node_events.EventEmitter {
1358
1436
  }));
1359
1437
  }
1360
1438
  }
1439
+ sendCodexMessage(body) {
1440
+ let content = {
1441
+ role: "agent",
1442
+ content: {
1443
+ type: "codex",
1444
+ data: body
1445
+ // This wraps the entire Claude message
1446
+ },
1447
+ meta: {
1448
+ sentFrom: "cli"
1449
+ }
1450
+ };
1451
+ const encrypted = encodeBase64(encrypt(content, this.secret));
1452
+ this.socket.emit("message", {
1453
+ sid: this.sessionId,
1454
+ message: encrypted
1455
+ });
1456
+ }
1361
1457
  sendSessionEvent(event, id) {
1362
1458
  let content = {
1363
1459
  role: "agent",
@@ -1482,6 +1578,7 @@ class ApiSessionClient extends node_events.EventEmitter {
1482
1578
  });
1483
1579
  }
1484
1580
  async close() {
1581
+ logger.debug("[API] socket.close() called");
1485
1582
  this.socket.close();
1486
1583
  }
1487
1584
  }
@@ -17,11 +17,12 @@ import { readFile as readFile$1, stat as stat$1, writeFile as writeFile$1, readd
17
17
  import { createHash } from 'crypto';
18
18
  import { dirname, resolve, join as join$1 } from 'path';
19
19
  import { fileURLToPath } from 'url';
20
+ import { platform } from 'os';
20
21
  import { Expo } from 'expo-server-sdk';
21
22
 
22
23
  var name = "happy-coder";
23
- var version = "0.10.0-2";
24
- var description = "Claude Code session sharing CLI";
24
+ var version = "0.10.0-3";
25
+ var description = "Mobile and Web client for Claude Code and Codex";
25
26
  var author = "Kirill Dubovitskiy";
26
27
  var license = "MIT";
27
28
  var type = "module";
@@ -29,7 +30,8 @@ var homepage = "https://github.com/slopus/happy-cli";
29
30
  var bugs = "https://github.com/slopus/happy-cli/issues";
30
31
  var repository = "slopus/happy-cli";
31
32
  var bin = {
32
- happy: "./bin/happy.mjs"
33
+ happy: "./bin/happy.mjs",
34
+ "happy-mcp": "./bin/happy-mcp.mjs"
33
35
  };
34
36
  var main = "./dist/index.cjs";
35
37
  var module = "./dist/index.mjs";
@@ -54,13 +56,23 @@ var exports = {
54
56
  types: "./dist/lib.d.mts",
55
57
  "default": "./dist/lib.mjs"
56
58
  }
59
+ },
60
+ "./codex/happyMcpStdioBridge": {
61
+ require: {
62
+ types: "./dist/codex/happyMcpStdioBridge.d.cts",
63
+ "default": "./dist/codex/happyMcpStdioBridge.cjs"
64
+ },
65
+ "import": {
66
+ types: "./dist/codex/happyMcpStdioBridge.d.mts",
67
+ "default": "./dist/codex/happyMcpStdioBridge.mjs"
68
+ }
57
69
  }
58
70
  };
59
71
  var files = [
60
72
  "dist",
61
73
  "bin",
62
74
  "scripts",
63
- "ripgrep",
75
+ "tools",
64
76
  "package.json"
65
77
  ];
66
78
  var scripts = {
@@ -73,7 +85,8 @@ var scripts = {
73
85
  "dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
74
86
  "dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
75
87
  prepublishOnly: "yarn build && yarn test",
76
- release: "release-it"
88
+ release: "release-it",
89
+ postinstall: "node scripts/unpack-tools.cjs"
77
90
  };
78
91
  var dependencies = {
79
92
  "@anthropic-ai/claude-code": "^1.0.102",
@@ -99,6 +112,7 @@ var dependencies = {
99
112
  "qrcode-terminal": "^0.12.0",
100
113
  react: "^19.1.1",
101
114
  "socket.io-client": "^4.8.1",
115
+ tar: "^7.4.3",
102
116
  tweetnacl: "^1.0.3",
103
117
  zod: "^3.23.8"
104
118
  };
@@ -482,6 +496,13 @@ class Logger {
482
496
  this.logToConsole("info", "[DEV]", message, ...args);
483
497
  }
484
498
  }
499
+ warn(message, ...args) {
500
+ this.logToConsole("warn", "", message, ...args);
501
+ this.debug(`[WARN] ${message}`, ...args);
502
+ }
503
+ getLogPath() {
504
+ return this.logFilePath;
505
+ }
485
506
  logToConsole(level, prefix, message, ...args) {
486
507
  switch (level) {
487
508
  case "debug": {
@@ -673,7 +694,8 @@ z$1.object({
673
694
  platform: z$1.string(),
674
695
  happyCliVersion: z$1.string(),
675
696
  homeDir: z$1.string(),
676
- happyHomeDir: z$1.string()
697
+ happyHomeDir: z$1.string(),
698
+ happyLibDir: z$1.string()
677
699
  });
678
700
  z$1.object({
679
701
  status: z$1.union([
@@ -937,7 +959,7 @@ function projectPath() {
937
959
  return path;
938
960
  }
939
961
 
940
- function run(args, options) {
962
+ function run$1(args, options) {
941
963
  const RUNNER_PATH = resolve(join$1(projectPath(), "scripts", "ripgrep_launcher.cjs"));
942
964
  return new Promise((resolve2, reject) => {
943
965
  const child = spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
@@ -965,6 +987,44 @@ function run(args, options) {
965
987
  });
966
988
  }
967
989
 
990
+ function getBinaryPath() {
991
+ const platformName = platform();
992
+ const binaryName = platformName === "win32" ? "difft.exe" : "difft";
993
+ return resolve(join$1(projectPath(), "tools", "unpacked", binaryName));
994
+ }
995
+ function run(args, options) {
996
+ const binaryPath = getBinaryPath();
997
+ return new Promise((resolve2, reject) => {
998
+ const child = spawn(binaryPath, args, {
999
+ stdio: ["pipe", "pipe", "pipe"],
1000
+ cwd: options?.cwd,
1001
+ env: {
1002
+ ...process.env,
1003
+ // Force color output when needed
1004
+ FORCE_COLOR: "1"
1005
+ }
1006
+ });
1007
+ let stdout = "";
1008
+ let stderr = "";
1009
+ child.stdout.on("data", (data) => {
1010
+ stdout += data.toString();
1011
+ });
1012
+ child.stderr.on("data", (data) => {
1013
+ stderr += data.toString();
1014
+ });
1015
+ child.on("close", (code) => {
1016
+ resolve2({
1017
+ exitCode: code || 0,
1018
+ stdout,
1019
+ stderr
1020
+ });
1021
+ });
1022
+ child.on("error", (err) => {
1023
+ reject(err);
1024
+ });
1025
+ });
1026
+ }
1027
+
968
1028
  const execAsync = promisify(exec);
969
1029
  function registerCommonHandlers(rpcHandlerManager) {
970
1030
  rpcHandlerManager.registerHandler("bash", async (data) => {
@@ -1159,7 +1219,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1159
1219
  rpcHandlerManager.registerHandler("ripgrep", async (data) => {
1160
1220
  logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
1161
1221
  try {
1162
- const result = await run(data.args, { cwd: data.cwd });
1222
+ const result = await run$1(data.args, { cwd: data.cwd });
1163
1223
  return {
1164
1224
  success: true,
1165
1225
  exitCode: result.exitCode,
@@ -1174,6 +1234,24 @@ function registerCommonHandlers(rpcHandlerManager) {
1174
1234
  };
1175
1235
  }
1176
1236
  });
1237
+ rpcHandlerManager.registerHandler("difftastic", async (data) => {
1238
+ logger.debug("Difftastic request with args:", data.args, "cwd:", data.cwd);
1239
+ try {
1240
+ const result = await run(data.args, { cwd: data.cwd });
1241
+ return {
1242
+ success: true,
1243
+ exitCode: result.exitCode,
1244
+ stdout: result.stdout.toString(),
1245
+ stderr: result.stderr.toString()
1246
+ };
1247
+ } catch (error) {
1248
+ logger.debug("Failed to run difftastic:", error);
1249
+ return {
1250
+ success: false,
1251
+ error: error instanceof Error ? error.message : "Failed to run difftastic"
1252
+ };
1253
+ }
1254
+ });
1177
1255
  }
1178
1256
 
1179
1257
  class ApiSessionClient extends EventEmitter {
@@ -1337,6 +1415,24 @@ class ApiSessionClient extends EventEmitter {
1337
1415
  }));
1338
1416
  }
1339
1417
  }
1418
+ sendCodexMessage(body) {
1419
+ let content = {
1420
+ role: "agent",
1421
+ content: {
1422
+ type: "codex",
1423
+ data: body
1424
+ // This wraps the entire Claude message
1425
+ },
1426
+ meta: {
1427
+ sentFrom: "cli"
1428
+ }
1429
+ };
1430
+ const encrypted = encodeBase64(encrypt(content, this.secret));
1431
+ this.socket.emit("message", {
1432
+ sid: this.sessionId,
1433
+ message: encrypted
1434
+ });
1435
+ }
1340
1436
  sendSessionEvent(event, id) {
1341
1437
  let content = {
1342
1438
  role: "agent",
@@ -1461,6 +1557,7 @@ class ApiSessionClient extends EventEmitter {
1461
1557
  });
1462
1558
  }
1463
1559
  async close() {
1560
+ logger.debug("[API] socket.close() called");
1464
1561
  this.socket.close();
1465
1562
  }
1466
1563
  }
@@ -1996,4 +2093,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
1996
2093
  }).passthrough()
1997
2094
  ]);
1998
2095
 
1999
- export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e, clearDaemonState as f, packageJson as g, readSettings as h, readCredentials as i, encodeBase64 as j, encodeBase64Url as k, logger as l, decodeBase64 as m, acquireDaemonLock as n, writeDaemonState as o, projectPath as p, releaseDaemonLock as q, readDaemonState as r, clearCredentials as s, clearMachineId as t, updateSettings as u, getLatestDaemonLog as v, writeCredentials as w };
2096
+ export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, AsyncLock as f, readDaemonState as g, clearDaemonState as h, readCredentials as i, encodeBase64 as j, encodeBase64Url as k, logger as l, decodeBase64 as m, acquireDaemonLock as n, writeDaemonState as o, projectPath as p, releaseDaemonLock as q, readSettings as r, clearCredentials as s, clearMachineId as t, updateSettings as u, getLatestDaemonLog as v, writeCredentials as w };