happy-coder 0.10.0-0 → 0.10.0-2

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.
package/dist/index.cjs CHANGED
@@ -3,13 +3,11 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var types = require('./types-fU2E-jQl.cjs');
6
+ var types = require('./types-WP9wteZE.cjs');
7
7
  var node_child_process = require('node:child_process');
8
8
  var node_path = require('node:path');
9
9
  var node_readline = require('node:readline');
10
10
  var node_fs = require('node:fs');
11
- var path = require('path');
12
- var url = require('url');
13
11
  var promises = require('node:fs/promises');
14
12
  var fs = require('fs/promises');
15
13
  var ink = require('ink');
@@ -20,10 +18,10 @@ require('node:events');
20
18
  require('socket.io-client');
21
19
  var tweetnacl = require('tweetnacl');
22
20
  require('expo-server-sdk');
23
- var child_process = require('child_process');
24
- var util = require('util');
25
21
  var crypto = require('crypto');
22
+ var child_process = require('child_process');
26
23
  var fs$1 = require('fs');
24
+ var path = require('path');
27
25
  var psList = require('ps-list');
28
26
  var spawn = require('cross-spawn');
29
27
  var os$1 = require('os');
@@ -36,6 +34,8 @@ var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
36
34
  var node_http = require('node:http');
37
35
  var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
38
36
  var http = require('http');
37
+ var util = require('util');
38
+ require('url');
39
39
 
40
40
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
41
41
  class Session {
@@ -148,12 +148,6 @@ function claudeCheckSession(sessionId, path) {
148
148
  return hasGoodMessage;
149
149
  }
150
150
 
151
- const __dirname$2 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
152
- function projectPath() {
153
- const path$1 = path.resolve(__dirname$2, "..");
154
- return path$1;
155
- }
156
-
157
151
  function trimIdent(text) {
158
152
  const lines = text.split("\n");
159
153
  while (lines.length > 0 && lines[0].trim() === "") {
@@ -187,7 +181,7 @@ const systemPrompt = trimIdent(`
187
181
  Co-Authored-By: Happy <yesreply@happy.engineering>
188
182
  `);
189
183
 
190
- const claudeCliPath = node_path.resolve(node_path.join(projectPath(), "scripts", "claude_local_launcher.cjs"));
184
+ const claudeCliPath = node_path.resolve(node_path.join(types.projectPath(), "scripts", "claude_local_launcher.cjs"));
191
185
  async function claudeLocal(opts) {
192
186
  const projectDir = getProjectPath(opts.path);
193
187
  node_fs.mkdirSync(projectDir, { recursive: true });
@@ -614,8 +608,8 @@ async function claudeLocalLauncher(session) {
614
608
  }
615
609
  await abort();
616
610
  }
617
- session.client.setHandler("abort", doAbort);
618
- session.client.setHandler("switch", doSwitch);
611
+ session.client.rpcHandlerManager.registerHandler("abort", doAbort);
612
+ session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
619
613
  session.queue.setOnMessage((message, mode) => {
620
614
  doSwitch();
621
615
  });
@@ -661,9 +655,9 @@ async function claudeLocalLauncher(session) {
661
655
  }
662
656
  } finally {
663
657
  exutFuture.resolve(void 0);
664
- session.client.setHandler("abort", async () => {
658
+ session.client.rpcHandlerManager.registerHandler("abort", async () => {
665
659
  });
666
- session.client.setHandler("switch", async () => {
660
+ session.client.rpcHandlerManager.registerHandler("switch", async () => {
667
661
  });
668
662
  session.queue.setOnMessage(null);
669
663
  await scanner.cleanup();
@@ -1510,7 +1504,7 @@ async function claudeRemote(opts) {
1510
1504
  executable: "node",
1511
1505
  abort: opts.signal,
1512
1506
  pathToClaudeCodeExecutable: (() => {
1513
- return node_path.resolve(node_path.join(projectPath(), "scripts", "claude_remote_launcher.cjs"));
1507
+ return node_path.resolve(node_path.join(types.projectPath(), "scripts", "claude_remote_launcher.cjs"));
1514
1508
  })()
1515
1509
  };
1516
1510
  let thinking = false;
@@ -1925,7 +1919,7 @@ class PermissionHandler {
1925
1919
  * Sets up the client handler for permission responses
1926
1920
  */
1927
1921
  setupClientHandler() {
1928
- this.session.client.setHandler("permission", async (message) => {
1922
+ this.session.client.rpcHandlerManager.registerHandler("permission", async (message) => {
1929
1923
  types.logger.debug(`Permission response: ${JSON.stringify(message)}`);
1930
1924
  const id = message.id;
1931
1925
  const pending = this.pendingRequests.get(id);
@@ -2495,8 +2489,8 @@ async function claudeRemoteLauncher(session) {
2495
2489
  }
2496
2490
  await abort();
2497
2491
  }
2498
- session.client.setHandler("abort", doAbort);
2499
- session.client.setHandler("switch", doSwitch);
2492
+ session.client.rpcHandlerManager.registerHandler("abort", doAbort);
2493
+ session.client.rpcHandlerManager.registerHandler("switch", doSwitch);
2500
2494
  const permissionHandler = new PermissionHandler(session);
2501
2495
  const messageQueue = new OutgoingMessageQueue(
2502
2496
  (logMessage) => session.client.sendClaudeSessionMessage(logMessage)
@@ -2812,255 +2806,6 @@ async function loop(opts) {
2812
2806
  }
2813
2807
  }
2814
2808
 
2815
- function run(args, options) {
2816
- const RUNNER_PATH = path.resolve(path.join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
2817
- return new Promise((resolve2, reject) => {
2818
- const child = child_process.spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
2819
- stdio: ["pipe", "pipe", "pipe"],
2820
- cwd: options?.cwd
2821
- });
2822
- let stdout = "";
2823
- let stderr = "";
2824
- child.stdout.on("data", (data) => {
2825
- stdout += data.toString();
2826
- });
2827
- child.stderr.on("data", (data) => {
2828
- stderr += data.toString();
2829
- });
2830
- child.on("close", (code) => {
2831
- resolve2({
2832
- exitCode: code || 0,
2833
- stdout,
2834
- stderr
2835
- });
2836
- });
2837
- child.on("error", (err) => {
2838
- reject(err);
2839
- });
2840
- });
2841
- }
2842
-
2843
- const execAsync$1 = util.promisify(child_process.exec);
2844
- function registerHandlers(session) {
2845
- session.setHandler("bash", async (data) => {
2846
- types.logger.debug("Shell command request:", data.command);
2847
- try {
2848
- const options = {
2849
- cwd: data.cwd,
2850
- timeout: data.timeout || 3e4
2851
- // Default 30 seconds timeout
2852
- };
2853
- const { stdout, stderr } = await execAsync$1(data.command, options);
2854
- return {
2855
- success: true,
2856
- stdout: stdout ? stdout.toString() : "",
2857
- stderr: stderr ? stderr.toString() : "",
2858
- exitCode: 0
2859
- };
2860
- } catch (error) {
2861
- const execError = error;
2862
- if (execError.code === "ETIMEDOUT" || execError.killed) {
2863
- return {
2864
- success: false,
2865
- stdout: execError.stdout || "",
2866
- stderr: execError.stderr || "",
2867
- exitCode: typeof execError.code === "number" ? execError.code : -1,
2868
- error: "Command timed out"
2869
- };
2870
- }
2871
- return {
2872
- success: false,
2873
- stdout: execError.stdout ? execError.stdout.toString() : "",
2874
- stderr: execError.stderr ? execError.stderr.toString() : execError.message || "Command failed",
2875
- exitCode: typeof execError.code === "number" ? execError.code : 1,
2876
- error: execError.message || "Command failed"
2877
- };
2878
- }
2879
- });
2880
- session.setHandler("readFile", async (data) => {
2881
- types.logger.debug("Read file request:", data.path);
2882
- try {
2883
- const buffer = await fs.readFile(data.path);
2884
- const content = buffer.toString("base64");
2885
- return { success: true, content };
2886
- } catch (error) {
2887
- types.logger.debug("Failed to read file:", error);
2888
- return { success: false, error: error instanceof Error ? error.message : "Failed to read file" };
2889
- }
2890
- });
2891
- session.setHandler("writeFile", async (data) => {
2892
- types.logger.debug("Write file request:", data.path);
2893
- try {
2894
- if (data.expectedHash !== null && data.expectedHash !== void 0) {
2895
- try {
2896
- const existingBuffer = await fs.readFile(data.path);
2897
- const existingHash = crypto.createHash("sha256").update(existingBuffer).digest("hex");
2898
- if (existingHash !== data.expectedHash) {
2899
- return {
2900
- success: false,
2901
- error: `File hash mismatch. Expected: ${data.expectedHash}, Actual: ${existingHash}`
2902
- };
2903
- }
2904
- } catch (error) {
2905
- const nodeError = error;
2906
- if (nodeError.code !== "ENOENT") {
2907
- throw error;
2908
- }
2909
- return {
2910
- success: false,
2911
- error: "File does not exist but hash was provided"
2912
- };
2913
- }
2914
- } else {
2915
- try {
2916
- await fs.stat(data.path);
2917
- return {
2918
- success: false,
2919
- error: "File already exists but was expected to be new"
2920
- };
2921
- } catch (error) {
2922
- const nodeError = error;
2923
- if (nodeError.code !== "ENOENT") {
2924
- throw error;
2925
- }
2926
- }
2927
- }
2928
- const buffer = Buffer.from(data.content, "base64");
2929
- await fs.writeFile(data.path, buffer);
2930
- const hash = crypto.createHash("sha256").update(buffer).digest("hex");
2931
- return { success: true, hash };
2932
- } catch (error) {
2933
- types.logger.debug("Failed to write file:", error);
2934
- return { success: false, error: error instanceof Error ? error.message : "Failed to write file" };
2935
- }
2936
- });
2937
- session.setHandler("listDirectory", async (data) => {
2938
- types.logger.debug("List directory request:", data.path);
2939
- try {
2940
- const entries = await fs.readdir(data.path, { withFileTypes: true });
2941
- const directoryEntries = await Promise.all(
2942
- entries.map(async (entry) => {
2943
- const fullPath = path.join(data.path, entry.name);
2944
- let type = "other";
2945
- let size;
2946
- let modified;
2947
- if (entry.isDirectory()) {
2948
- type = "directory";
2949
- } else if (entry.isFile()) {
2950
- type = "file";
2951
- }
2952
- try {
2953
- const stats = await fs.stat(fullPath);
2954
- size = stats.size;
2955
- modified = stats.mtime.getTime();
2956
- } catch (error) {
2957
- types.logger.debug(`Failed to stat ${fullPath}:`, error);
2958
- }
2959
- return {
2960
- name: entry.name,
2961
- type,
2962
- size,
2963
- modified
2964
- };
2965
- })
2966
- );
2967
- directoryEntries.sort((a, b) => {
2968
- if (a.type === "directory" && b.type !== "directory") return -1;
2969
- if (a.type !== "directory" && b.type === "directory") return 1;
2970
- return a.name.localeCompare(b.name);
2971
- });
2972
- return { success: true, entries: directoryEntries };
2973
- } catch (error) {
2974
- types.logger.debug("Failed to list directory:", error);
2975
- return { success: false, error: error instanceof Error ? error.message : "Failed to list directory" };
2976
- }
2977
- });
2978
- session.setHandler("getDirectoryTree", async (data) => {
2979
- types.logger.debug("Get directory tree request:", data.path, "maxDepth:", data.maxDepth);
2980
- async function buildTree(path$1, name, currentDepth) {
2981
- try {
2982
- const stats = await fs.stat(path$1);
2983
- const node = {
2984
- name,
2985
- path: path$1,
2986
- type: stats.isDirectory() ? "directory" : "file",
2987
- size: stats.size,
2988
- modified: stats.mtime.getTime()
2989
- };
2990
- if (stats.isDirectory() && currentDepth < data.maxDepth) {
2991
- const entries = await fs.readdir(path$1, { withFileTypes: true });
2992
- const children = [];
2993
- await Promise.all(
2994
- entries.map(async (entry) => {
2995
- if (entry.isSymbolicLink()) {
2996
- types.logger.debug(`Skipping symlink: ${path.join(path$1, entry.name)}`);
2997
- return;
2998
- }
2999
- const childPath = path.join(path$1, entry.name);
3000
- const childNode = await buildTree(childPath, entry.name, currentDepth + 1);
3001
- if (childNode) {
3002
- children.push(childNode);
3003
- }
3004
- })
3005
- );
3006
- children.sort((a, b) => {
3007
- if (a.type === "directory" && b.type !== "directory") return -1;
3008
- if (a.type !== "directory" && b.type === "directory") return 1;
3009
- return a.name.localeCompare(b.name);
3010
- });
3011
- node.children = children;
3012
- }
3013
- return node;
3014
- } catch (error) {
3015
- types.logger.debug(`Failed to process ${path$1}:`, error instanceof Error ? error.message : String(error));
3016
- return null;
3017
- }
3018
- }
3019
- try {
3020
- if (data.maxDepth < 0) {
3021
- return { success: false, error: "maxDepth must be non-negative" };
3022
- }
3023
- const baseName = data.path === "/" ? "/" : data.path.split("/").pop() || data.path;
3024
- const tree = await buildTree(data.path, baseName, 0);
3025
- if (!tree) {
3026
- return { success: false, error: "Failed to access the specified path" };
3027
- }
3028
- return { success: true, tree };
3029
- } catch (error) {
3030
- types.logger.debug("Failed to get directory tree:", error);
3031
- return { success: false, error: error instanceof Error ? error.message : "Failed to get directory tree" };
3032
- }
3033
- });
3034
- session.setHandler("ripgrep", async (data) => {
3035
- types.logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
3036
- try {
3037
- const result = await run(data.args, { cwd: data.cwd });
3038
- return {
3039
- success: true,
3040
- exitCode: result.exitCode,
3041
- stdout: result.stdout.toString(),
3042
- stderr: result.stderr.toString()
3043
- };
3044
- } catch (error) {
3045
- types.logger.debug("Failed to run ripgrep:", error);
3046
- return {
3047
- success: false,
3048
- error: error instanceof Error ? error.message : "Failed to run ripgrep"
3049
- };
3050
- }
3051
- });
3052
- }
3053
- function registerKillSessionHandler(session, killThisHappy) {
3054
- session.setHandler("killSession", async () => {
3055
- types.logger.debug("Kill session request received");
3056
- void killThisHappy();
3057
- return {
3058
- success: true,
3059
- message: "Killing happy-cli process"
3060
- };
3061
- });
3062
- }
3063
-
3064
2809
  class MessageQueue2 {
3065
2810
  queue = [];
3066
2811
  // Made public for testing
@@ -3607,7 +3352,7 @@ async function isDaemonRunningCurrentlyInstalledHappyVersion() {
3607
3352
  return false;
3608
3353
  }
3609
3354
  try {
3610
- const packageJsonPath = path.join(projectPath(), "package.json");
3355
+ const packageJsonPath = path.join(types.projectPath(), "package.json");
3611
3356
  const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
3612
3357
  const currentCliVersion = packageJson.version;
3613
3358
  types.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
@@ -3784,7 +3529,7 @@ async function runDoctorCommand(filter) {
3784
3529
  console.log(`Node.js Version: ${chalk.green(process.version)}`);
3785
3530
  console.log("");
3786
3531
  console.log(chalk.bold("\u{1F527} Daemon Spawn Diagnostics"));
3787
- const projectRoot = projectPath();
3532
+ const projectRoot = types.projectPath();
3788
3533
  const wrapperPath = node_path.join(projectRoot, "bin", "happy.mjs");
3789
3534
  const cliEntrypoint = node_path.join(projectRoot, "dist", "index.mjs");
3790
3535
  console.log(`Project Root: ${chalk.blue(projectRoot)}`);
@@ -4157,7 +3902,7 @@ async function authAndSetupMachineIfNeeded() {
4157
3902
  }
4158
3903
 
4159
3904
  function spawnHappyCLI(args, options = {}) {
4160
- const projectRoot = projectPath();
3905
+ const projectRoot = types.projectPath();
4161
3906
  const entrypoint = node_path.join(projectRoot, "dist", "index.mjs");
4162
3907
  let directory;
4163
3908
  if ("cwd" in options) {
@@ -4624,7 +4369,7 @@ async function startDaemon() {
4624
4369
  startedAt: Date.now()
4625
4370
  };
4626
4371
  const api = new types.ApiClient(credentials.token, credentials.secret);
4627
- const machine = await api.createMachineOrGetExistingAsIs({
4372
+ const machine = await api.getOrCreateMachine({
4628
4373
  machineId,
4629
4374
  metadata: initialMachineMetadata,
4630
4375
  daemonState: initialDaemonState
@@ -4655,7 +4400,7 @@ async function startDaemon() {
4655
4400
  pidToTrackedSession.delete(pid);
4656
4401
  }
4657
4402
  }
4658
- const projectVersion = JSON.parse(fs$1.readFileSync(path.join(projectPath(), "package.json"), "utf-8")).version;
4403
+ const projectVersion = JSON.parse(fs$1.readFileSync(path.join(types.projectPath(), "package.json"), "utf-8")).version;
4659
4404
  if (projectVersion !== types.configuration.currentCliVersion) {
4660
4405
  types.logger.debug("[DAEMON RUN] Daemon is outdated, triggering self-restart with latest version, clearing heartbeat interval");
4661
4406
  clearInterval(restartOnStaleVersionAndHeartbeat);
@@ -4806,6 +4551,17 @@ async function startHappyServer(client) {
4806
4551
  };
4807
4552
  }
4808
4553
 
4554
+ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
4555
+ rpcHandlerManager.registerHandler("killSession", async () => {
4556
+ types.logger.debug("Kill session request received");
4557
+ void killThisHappy();
4558
+ return {
4559
+ success: true,
4560
+ message: "Killing happy-cli process"
4561
+ };
4562
+ });
4563
+ }
4564
+
4809
4565
  async function start(credentials, options = {}) {
4810
4566
  const workingDirectory = process.cwd();
4811
4567
  const sessionTag = node_crypto.randomUUID();
@@ -4824,7 +4580,7 @@ async function start(credentials, options = {}) {
4824
4580
  process.exit(1);
4825
4581
  }
4826
4582
  types.logger.debug(`Using machineId: ${machineId}`);
4827
- await api.createMachineOrGetExistingAsIs({
4583
+ await api.getOrCreateMachine({
4828
4584
  machineId,
4829
4585
  metadata: initialMachineMetadata
4830
4586
  });
@@ -4892,7 +4648,6 @@ async function start(credentials, options = {}) {
4892
4648
  allowedTools: mode.allowedTools,
4893
4649
  disallowedTools: mode.disallowedTools
4894
4650
  }));
4895
- registerHandlers(session);
4896
4651
  let currentPermissionMode = options.permissionMode;
4897
4652
  let currentModel = options.model;
4898
4653
  let currentFallbackModel = void 0;
@@ -5039,7 +4794,7 @@ async function start(credentials, options = {}) {
5039
4794
  types.logger.debug("[START] Unhandled rejection:", reason);
5040
4795
  cleanup();
5041
4796
  });
5042
- registerKillSessionHandler(session, cleanup);
4797
+ registerKillSessionHandler(session.rpcHandlerManager, cleanup);
5043
4798
  await loop({
5044
4799
  path: workingDirectory,
5045
4800
  model: options.model,