happy-coder 0.10.0-4 → 0.10.1

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.
@@ -3,7 +3,7 @@
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-BcDnTXMg.cjs');
6
+ var types = require('./types-DOKP_I5s.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');
@@ -25,6 +25,7 @@ var path = require('path');
25
25
  var psList = require('ps-list');
26
26
  var spawn = require('cross-spawn');
27
27
  var os$1 = require('os');
28
+ var tmp = require('tmp');
28
29
  var qrcode = require('qrcode-terminal');
29
30
  var open = require('open');
30
31
  var fastify = require('fastify');
@@ -37,6 +38,25 @@ var http = require('http');
37
38
  var util = require('util');
38
39
 
39
40
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
41
+ function _interopNamespaceDefault(e) {
42
+ var n = Object.create(null);
43
+ if (e) {
44
+ Object.keys(e).forEach(function (k) {
45
+ if (k !== 'default') {
46
+ var d = Object.getOwnPropertyDescriptor(e, k);
47
+ Object.defineProperty(n, k, d.get ? d : {
48
+ enumerable: true,
49
+ get: function () { return e[k]; }
50
+ });
51
+ }
52
+ });
53
+ }
54
+ n.default = e;
55
+ return Object.freeze(n);
56
+ }
57
+
58
+ var tmp__namespace = /*#__PURE__*/_interopNamespaceDefault(tmp);
59
+
40
60
  class Session {
41
61
  path;
42
62
  logPath;
@@ -166,9 +186,39 @@ function trimIdent(text) {
166
186
  return trimmedLines.join("\n");
167
187
  }
168
188
 
169
- const systemPrompt = trimIdent(`
170
- ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
189
+ function getClaudeSettingsPath() {
190
+ const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || node_path.join(os.homedir(), ".claude");
191
+ return node_path.join(claudeConfigDir, "settings.json");
192
+ }
193
+ function readClaudeSettings() {
194
+ try {
195
+ const settingsPath = getClaudeSettingsPath();
196
+ if (!fs.existsSync(settingsPath)) {
197
+ types.logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
198
+ return null;
199
+ }
200
+ const settingsContent = fs.readFileSync(settingsPath, "utf-8");
201
+ const settings = JSON.parse(settingsContent);
202
+ types.logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
203
+ types.logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
204
+ return settings;
205
+ } catch (error) {
206
+ types.logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
207
+ return null;
208
+ }
209
+ }
210
+ function shouldIncludeCoAuthoredBy() {
211
+ const settings = readClaudeSettings();
212
+ if (!settings || settings.includeCoAuthoredBy === void 0) {
213
+ return true;
214
+ }
215
+ return settings.includeCoAuthoredBy;
216
+ }
171
217
 
218
+ const BASE_SYSTEM_PROMPT = (() => trimIdent(`
219
+ ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
220
+ `))();
221
+ const CO_AUTHORED_CREDITS = (() => trimIdent(`
172
222
  When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
173
223
 
174
224
  <main commit message>
@@ -178,7 +228,15 @@ const systemPrompt = trimIdent(`
178
228
 
179
229
  Co-Authored-By: Claude <noreply@anthropic.com>
180
230
  Co-Authored-By: Happy <yesreply@happy.engineering>
181
- `);
231
+ `))();
232
+ const systemPrompt = (() => {
233
+ const includeCoAuthored = shouldIncludeCoAuthoredBy();
234
+ if (includeCoAuthored) {
235
+ return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
236
+ } else {
237
+ return BASE_SYSTEM_PROMPT;
238
+ }
239
+ })();
182
240
 
183
241
  const claudeCliPath = node_path.resolve(node_path.join(types.projectPath(), "scripts", "claude_local_launcher.cjs"));
184
242
  async function claudeLocal(opts) {
@@ -923,7 +981,7 @@ class AbortError extends Error {
923
981
  }
924
982
  }
925
983
 
926
- 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-67rskwL7.cjs', document.baseURI).href)));
984
+ 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-B_nemqpL.cjs', document.baseURI).href)));
927
985
  const __dirname$1 = node_path.join(__filename$1, "..");
928
986
  function getDefaultClaudeCodePath() {
929
987
  return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
@@ -4268,7 +4326,22 @@ async function startDaemon() {
4268
4326
  }
4269
4327
  }
4270
4328
  try {
4329
+ let extraEnv = {};
4330
+ if (options.token) {
4331
+ if (options.agent === "codex") {
4332
+ const codexHomeDir = tmp__namespace.dirSync();
4333
+ fs$1.writeFile(path.join(codexHomeDir.name, "auth.json"), options.token);
4334
+ extraEnv = {
4335
+ CODEX_HOME: codexHomeDir.name
4336
+ };
4337
+ } else {
4338
+ extraEnv = {
4339
+ CLAUDE_CODE_OAUTH_TOKEN: options.token
4340
+ };
4341
+ }
4342
+ }
4271
4343
  const args = [
4344
+ options.agent === "claude" ? "claude" : "codex",
4272
4345
  "--happy-starting-mode",
4273
4346
  "remote",
4274
4347
  "--started-by",
@@ -4278,9 +4351,12 @@ async function startDaemon() {
4278
4351
  cwd: directory,
4279
4352
  detached: true,
4280
4353
  // Sessions stay alive when daemon stops
4281
- stdio: ["ignore", "pipe", "pipe"]
4354
+ stdio: ["ignore", "pipe", "pipe"],
4282
4355
  // Capture stdout/stderr for debugging
4283
- // env is inherited automatically from parent process
4356
+ env: {
4357
+ ...process.env,
4358
+ ...extraEnv
4359
+ }
4284
4360
  });
4285
4361
  if (process.env.DEBUG) {
4286
4362
  happyProcess.stdout?.on("data", (data) => {
@@ -4631,7 +4707,8 @@ async function runClaude(credentials, options = {}) {
4631
4707
  startedBy: options.startedBy || "terminal",
4632
4708
  // Initialize lifecycle state
4633
4709
  lifecycleState: "running",
4634
- lifecycleStateSince: Date.now()
4710
+ lifecycleStateSince: Date.now(),
4711
+ flavor: "claude"
4635
4712
  };
4636
4713
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4637
4714
  types.logger.debug(`Session created: ${response.id}`);
@@ -5726,11 +5803,17 @@ async function handleConnectVendor(vendor, displayName) {
5726
5803
  return;
5727
5804
  } else if (subcommand === "codex") {
5728
5805
  try {
5729
- const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BLNf5zb1.cjs'); });
5806
+ const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BjT2Vmcd.cjs'); });
5807
+ let startedBy = void 0;
5808
+ for (let i = 1; i < args.length; i++) {
5809
+ if (args[i] === "--started-by") {
5810
+ startedBy = args[++i];
5811
+ }
5812
+ }
5730
5813
  const {
5731
5814
  credentials
5732
5815
  } = await authAndSetupMachineIfNeeded();
5733
- await runCodex({ credentials });
5816
+ await runCodex({ credentials, startedBy });
5734
5817
  } catch (error) {
5735
5818
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
5736
5819
  if (process.env.DEBUG) {
@@ -5863,6 +5946,9 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5863
5946
  }
5864
5947
  return;
5865
5948
  } else {
5949
+ if (args.length > 0 && args[0] === "claude") {
5950
+ args.shift();
5951
+ }
5866
5952
  const options = {};
5867
5953
  let showHelp = false;
5868
5954
  let showVersion = false;
@@ -6029,5 +6115,8 @@ exports.MessageBuffer = MessageBuffer;
6029
6115
  exports.MessageQueue2 = MessageQueue2;
6030
6116
  exports.hashObject = hashObject;
6031
6117
  exports.initialMachineMetadata = initialMachineMetadata;
6118
+ exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
6119
+ exports.registerKillSessionHandler = registerKillSessionHandler;
6032
6120
  exports.startHappyServer = startHappyServer;
6121
+ exports.stopCaffeinate = stopCaffeinate;
6033
6122
  exports.trimIdent = trimIdent;
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import os$1, { homedir } from 'node:os';
3
3
  import { randomUUID, randomBytes } from 'node:crypto';
4
- import { l as logger, p as projectPath, d as backoff, e as delay, R as RawJSONLinesSchema, f as AsyncLock, g as readDaemonState, h as clearDaemonState, b as packageJson, c as configuration, r as readSettings, i as readCredentials, j as encodeBase64, u as updateSettings, k as encodeBase64Url, m as decodeBase64, w as writeCredentialsLegacy, n as writeCredentialsDataKey, o as acquireDaemonLock, q as writeDaemonState, A as ApiClient, s as releaseDaemonLock, t as clearCredentials, v as clearMachineId, x as getLatestDaemonLog } from './types-2wHnX7UW.mjs';
4
+ import { l as logger, p as projectPath, d as backoff, e as delay, R as RawJSONLinesSchema, f as AsyncLock, g as readDaemonState, h as clearDaemonState, b as packageJson, c as configuration, r as readSettings, i as readCredentials, j as encodeBase64, u as updateSettings, k as encodeBase64Url, m as decodeBase64, w as writeCredentialsLegacy, n as writeCredentialsDataKey, o as acquireDaemonLock, q as writeDaemonState, A as ApiClient, s as releaseDaemonLock, t as clearCredentials, v as clearMachineId, x as getLatestDaemonLog } from './types-CYZG1S69.mjs';
5
5
  import { spawn, execSync, execFileSync } from 'node:child_process';
6
6
  import { resolve, join } from 'node:path';
7
7
  import { createInterface } from 'node:readline';
@@ -23,6 +23,7 @@ import { join as join$1 } from 'path';
23
23
  import psList from 'ps-list';
24
24
  import spawn$2 from 'cross-spawn';
25
25
  import os from 'os';
26
+ import * as tmp from 'tmp';
26
27
  import qrcode from 'qrcode-terminal';
27
28
  import open from 'open';
28
29
  import fastify from 'fastify';
@@ -163,9 +164,39 @@ function trimIdent(text) {
163
164
  return trimmedLines.join("\n");
164
165
  }
165
166
 
166
- const systemPrompt = trimIdent(`
167
- ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
167
+ function getClaudeSettingsPath() {
168
+ const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
169
+ return join(claudeConfigDir, "settings.json");
170
+ }
171
+ function readClaudeSettings() {
172
+ try {
173
+ const settingsPath = getClaudeSettingsPath();
174
+ if (!existsSync(settingsPath)) {
175
+ logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
176
+ return null;
177
+ }
178
+ const settingsContent = readFileSync(settingsPath, "utf-8");
179
+ const settings = JSON.parse(settingsContent);
180
+ logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
181
+ logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
182
+ return settings;
183
+ } catch (error) {
184
+ logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
185
+ return null;
186
+ }
187
+ }
188
+ function shouldIncludeCoAuthoredBy() {
189
+ const settings = readClaudeSettings();
190
+ if (!settings || settings.includeCoAuthoredBy === void 0) {
191
+ return true;
192
+ }
193
+ return settings.includeCoAuthoredBy;
194
+ }
168
195
 
196
+ const BASE_SYSTEM_PROMPT = (() => trimIdent(`
197
+ ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
198
+ `))();
199
+ const CO_AUTHORED_CREDITS = (() => trimIdent(`
169
200
  When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
170
201
 
171
202
  <main commit message>
@@ -175,7 +206,15 @@ const systemPrompt = trimIdent(`
175
206
 
176
207
  Co-Authored-By: Claude <noreply@anthropic.com>
177
208
  Co-Authored-By: Happy <yesreply@happy.engineering>
178
- `);
209
+ `))();
210
+ const systemPrompt = (() => {
211
+ const includeCoAuthored = shouldIncludeCoAuthoredBy();
212
+ if (includeCoAuthored) {
213
+ return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
214
+ } else {
215
+ return BASE_SYSTEM_PROMPT;
216
+ }
217
+ })();
179
218
 
180
219
  const claudeCliPath = resolve(join(projectPath(), "scripts", "claude_local_launcher.cjs"));
181
220
  async function claudeLocal(opts) {
@@ -4265,7 +4304,22 @@ async function startDaemon() {
4265
4304
  }
4266
4305
  }
4267
4306
  try {
4307
+ let extraEnv = {};
4308
+ if (options.token) {
4309
+ if (options.agent === "codex") {
4310
+ const codexHomeDir = tmp.dirSync();
4311
+ fs.writeFile(join$1(codexHomeDir.name, "auth.json"), options.token);
4312
+ extraEnv = {
4313
+ CODEX_HOME: codexHomeDir.name
4314
+ };
4315
+ } else {
4316
+ extraEnv = {
4317
+ CLAUDE_CODE_OAUTH_TOKEN: options.token
4318
+ };
4319
+ }
4320
+ }
4268
4321
  const args = [
4322
+ options.agent === "claude" ? "claude" : "codex",
4269
4323
  "--happy-starting-mode",
4270
4324
  "remote",
4271
4325
  "--started-by",
@@ -4275,9 +4329,12 @@ async function startDaemon() {
4275
4329
  cwd: directory,
4276
4330
  detached: true,
4277
4331
  // Sessions stay alive when daemon stops
4278
- stdio: ["ignore", "pipe", "pipe"]
4332
+ stdio: ["ignore", "pipe", "pipe"],
4279
4333
  // Capture stdout/stderr for debugging
4280
- // env is inherited automatically from parent process
4334
+ env: {
4335
+ ...process.env,
4336
+ ...extraEnv
4337
+ }
4281
4338
  });
4282
4339
  if (process.env.DEBUG) {
4283
4340
  happyProcess.stdout?.on("data", (data) => {
@@ -4628,7 +4685,8 @@ async function runClaude(credentials, options = {}) {
4628
4685
  startedBy: options.startedBy || "terminal",
4629
4686
  // Initialize lifecycle state
4630
4687
  lifecycleState: "running",
4631
- lifecycleStateSince: Date.now()
4688
+ lifecycleStateSince: Date.now(),
4689
+ flavor: "claude"
4632
4690
  };
4633
4691
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4634
4692
  logger.debug(`Session created: ${response.id}`);
@@ -5723,11 +5781,17 @@ async function handleConnectVendor(vendor, displayName) {
5723
5781
  return;
5724
5782
  } else if (subcommand === "codex") {
5725
5783
  try {
5726
- const { runCodex } = await import('./runCodex-BNH8w4O9.mjs');
5784
+ const { runCodex } = await import('./runCodex-CHAghw9e.mjs');
5785
+ let startedBy = void 0;
5786
+ for (let i = 1; i < args.length; i++) {
5787
+ if (args[i] === "--started-by") {
5788
+ startedBy = args[++i];
5789
+ }
5790
+ }
5727
5791
  const {
5728
5792
  credentials
5729
5793
  } = await authAndSetupMachineIfNeeded();
5730
- await runCodex({ credentials });
5794
+ await runCodex({ credentials, startedBy });
5731
5795
  } catch (error) {
5732
5796
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
5733
5797
  if (process.env.DEBUG) {
@@ -5860,6 +5924,9 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5860
5924
  }
5861
5925
  return;
5862
5926
  } else {
5927
+ if (args.length > 0 && args[0] === "claude") {
5928
+ args.shift();
5929
+ }
5863
5930
  const options = {};
5864
5931
  let showHelp = false;
5865
5932
  let showVersion = false;
@@ -6022,4 +6089,4 @@ ${chalk.bold("Examples:")}
6022
6089
  }
6023
6090
  }
6024
6091
 
6025
- export { MessageQueue2 as M, MessageBuffer as a, hashObject as h, initialMachineMetadata as i, startHappyServer as s, trimIdent as t };
6092
+ export { MessageQueue2 as M, MessageBuffer as a, stopCaffeinate as b, hashObject as h, initialMachineMetadata as i, notifyDaemonSessionStarted as n, registerKillSessionHandler as r, startHappyServer as s, trimIdent as t };
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  require('chalk');
4
- require('./index-67rskwL7.cjs');
5
- require('./types-BcDnTXMg.cjs');
4
+ require('./index-B_nemqpL.cjs');
5
+ require('./types-DOKP_I5s.cjs');
6
6
  require('zod');
7
7
  require('node:child_process');
8
8
  require('node:os');
@@ -27,6 +27,7 @@ require('path');
27
27
  require('ps-list');
28
28
  require('cross-spawn');
29
29
  require('os');
30
+ require('tmp');
30
31
  require('qrcode-terminal');
31
32
  require('open');
32
33
  require('fastify');
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'chalk';
2
- import './index-Dw96QD4T.mjs';
3
- import './types-2wHnX7UW.mjs';
2
+ import './index-Cp1f1oto.mjs';
3
+ import './types-CYZG1S69.mjs';
4
4
  import 'zod';
5
5
  import 'node:child_process';
6
6
  import 'node:os';
@@ -25,6 +25,7 @@ import 'path';
25
25
  import 'ps-list';
26
26
  import 'cross-spawn';
27
27
  import 'os';
28
+ import 'tmp';
28
29
  import 'qrcode-terminal';
29
30
  import 'open';
30
31
  import 'fastify';
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-BcDnTXMg.cjs');
3
+ var types = require('./types-DOKP_I5s.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('fs');
package/dist/lib.d.cts CHANGED
@@ -638,6 +638,8 @@ interface SpawnSessionOptions {
638
638
  directory: string;
639
639
  sessionId?: string;
640
640
  approvedNewDirectoryCreation?: boolean;
641
+ agent?: 'claude' | 'codex';
642
+ token?: string;
641
643
  }
642
644
  type SpawnSessionResult = {
643
645
  type: 'success';
package/dist/lib.d.mts CHANGED
@@ -638,6 +638,8 @@ interface SpawnSessionOptions {
638
638
  directory: string;
639
639
  sessionId?: string;
640
640
  approvedNewDirectoryCreation?: boolean;
641
+ agent?: 'claude' | 'codex';
642
+ token?: string;
641
643
  }
642
644
  type SpawnSessionResult = {
643
645
  type: 'success';
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-2wHnX7UW.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-CYZG1S69.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -2,13 +2,13 @@
2
2
 
3
3
  var ink = require('ink');
4
4
  var React = require('react');
5
- var types = require('./types-BcDnTXMg.cjs');
5
+ var types = require('./types-DOKP_I5s.cjs');
6
6
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
7
7
  var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
8
8
  var z = require('zod');
9
9
  var types_js = require('@modelcontextprotocol/sdk/types.js');
10
10
  var node_crypto = require('node:crypto');
11
- var index = require('./index-67rskwL7.cjs');
11
+ var index = require('./index-B_nemqpL.cjs');
12
12
  var os = require('node:os');
13
13
  var node_path = require('node:path');
14
14
  var fs = require('node:fs');
@@ -32,6 +32,7 @@ require('node:readline');
32
32
  require('node:url');
33
33
  require('ps-list');
34
34
  require('cross-spawn');
35
+ require('tmp');
35
36
  require('qrcode-terminal');
36
37
  require('open');
37
38
  require('fastify');
@@ -182,8 +183,16 @@ class CodexMcpClient {
182
183
  return this.sessionId !== null;
183
184
  }
184
185
  clearSession() {
186
+ const previousSessionId = this.sessionId;
185
187
  this.sessionId = null;
186
- types.logger.debug("[CodexMCP] Session cleared");
188
+ types.logger.debug("[CodexMCP] Session cleared, previous sessionId:", previousSessionId);
189
+ }
190
+ /**
191
+ * Store the current session ID without clearing it, useful for abort handling
192
+ */
193
+ storeSessionForResume() {
194
+ types.logger.debug("[CodexMCP] Storing session for potential resume:", this.sessionId);
195
+ return this.sessionId;
187
196
  }
188
197
  async disconnect() {
189
198
  if (!this.connected) return;
@@ -681,6 +690,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
681
690
  async function runCodex(opts) {
682
691
  const sessionTag = node_crypto.randomUUID();
683
692
  const api = await types.ApiClient.create(opts.credentials);
693
+ types.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
684
694
  const settings = await types.readSettings();
685
695
  let machineId = settings?.machineId;
686
696
  if (!machineId) {
@@ -705,9 +715,9 @@ async function runCodex(opts) {
705
715
  happyHomeDir: types.configuration.happyHomeDir,
706
716
  happyLibDir: types.projectPath(),
707
717
  happyToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
708
- startedFromDaemon: false,
718
+ startedFromDaemon: opts.startedBy === "daemon",
709
719
  hostPid: process.pid,
710
- startedBy: "terminal",
720
+ startedBy: opts.startedBy || "terminal",
711
721
  // Initialize lifecycle state
712
722
  lifecycleState: "running",
713
723
  lifecycleStateSince: Date.now(),
@@ -715,6 +725,17 @@ async function runCodex(opts) {
715
725
  };
716
726
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
717
727
  const session = api.sessionSyncClient(response);
728
+ try {
729
+ types.logger.debug(`[START] Reporting session ${response.id} to daemon`);
730
+ const result = await index.notifyDaemonSessionStarted(response.id, metadata);
731
+ if (result.error) {
732
+ types.logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
733
+ } else {
734
+ types.logger.debug(`[START] Reported session ${response.id} to daemon`);
735
+ }
736
+ } catch (error) {
737
+ types.logger.debug("[START] Failed to report to daemon (may not be running):", error);
738
+ }
718
739
  const messageQueue = new index.MessageQueue2((mode) => index.hashObject({
719
740
  permissionMode: mode.permissionMode,
720
741
  model: mode.model
@@ -768,22 +789,54 @@ async function runCodex(opts) {
768
789
  }
769
790
  let abortController = new AbortController();
770
791
  let shouldExit = false;
792
+ let storedSessionIdForResume = null;
771
793
  async function handleAbort() {
772
- types.logger.debug("[Codex] Abort requested");
794
+ types.logger.debug("[Codex] Abort requested - stopping current task");
773
795
  try {
796
+ if (client.hasActiveSession()) {
797
+ storedSessionIdForResume = client.storeSessionForResume();
798
+ types.logger.debug("[Codex] Stored session for resume:", storedSessionIdForResume);
799
+ }
774
800
  abortController.abort();
775
801
  messageQueue.reset();
776
802
  permissionHandler.reset();
777
803
  reasoningProcessor.abort();
778
804
  diffProcessor.reset();
779
- types.logger.debug("[Codex] Abort completed");
805
+ types.logger.debug("[Codex] Abort completed - session remains active");
780
806
  } catch (error) {
781
807
  types.logger.debug("[Codex] Error during abort:", error);
782
808
  } finally {
783
809
  abortController = new AbortController();
784
810
  }
785
811
  }
812
+ const handleKillSession = async () => {
813
+ types.logger.debug("[Codex] Kill session requested - terminating process");
814
+ await handleAbort();
815
+ types.logger.debug("[Codex] Abort completed, proceeding with termination");
816
+ try {
817
+ if (session) {
818
+ session.updateMetadata((currentMetadata) => ({
819
+ ...currentMetadata,
820
+ lifecycleState: "archived",
821
+ lifecycleStateSince: Date.now(),
822
+ archivedBy: "cli",
823
+ archiveReason: "User terminated"
824
+ }));
825
+ session.sendSessionDeath();
826
+ await session.flush();
827
+ await session.close();
828
+ }
829
+ index.stopCaffeinate();
830
+ happyServer.stop();
831
+ types.logger.debug("[Codex] Session termination complete, exiting");
832
+ process.exit(0);
833
+ } catch (error) {
834
+ types.logger.debug("[Codex] Error during session termination:", error);
835
+ process.exit(1);
836
+ }
837
+ };
786
838
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
839
+ index.registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
787
840
  const messageBuffer = new index.MessageBuffer();
788
841
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
789
842
  let inkInstance = null;
@@ -831,7 +884,8 @@ async function runCodex(opts) {
831
884
  return acc;
832
885
  };
833
886
  var collectFilesRecursive = collectFilesRecursive2;
834
- const rootDir = node_path.join(os.homedir(), ".codex", "sessions");
887
+ const codexHomeDir = process.env.CODEX_HOME || node_path.join(os.homedir(), ".codex");
888
+ const rootDir = node_path.join(codexHomeDir, "sessions");
835
889
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
836
890
  try {
837
891
  return fs.statSync(full).isFile();
@@ -998,8 +1052,13 @@ async function runCodex(opts) {
998
1052
  let message = pending;
999
1053
  pending = null;
1000
1054
  if (!message) {
1001
- const batch = await messageQueue.waitForMessagesAndGetAsString(abortController.signal);
1002
- if (!batch || shouldExit) {
1055
+ const waitSignal = abortController.signal;
1056
+ const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
1057
+ if (!batch) {
1058
+ if (waitSignal.aborted && !shouldExit) {
1059
+ types.logger.debug("[codex]: Wait aborted while idle; ignoring and continuing");
1060
+ continue;
1061
+ }
1003
1062
  types.logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
1004
1063
  break;
1005
1064
  }
@@ -1072,9 +1131,22 @@ async function runCodex(opts) {
1072
1131
  if (message.mode.model) {
1073
1132
  startConfig.model = message.mode.model;
1074
1133
  }
1134
+ let resumeFile = null;
1075
1135
  if (nextExperimentalResume) {
1076
- startConfig.config.experimental_resume = nextExperimentalResume;
1136
+ resumeFile = nextExperimentalResume;
1077
1137
  nextExperimentalResume = null;
1138
+ types.logger.debug("[Codex] Using resume file from mode change:", resumeFile);
1139
+ } else if (storedSessionIdForResume) {
1140
+ const abortResumeFile = findCodexResumeFile(storedSessionIdForResume);
1141
+ if (abortResumeFile) {
1142
+ resumeFile = abortResumeFile;
1143
+ types.logger.debug("[Codex] Using resume file from aborted session:", resumeFile);
1144
+ messageBuffer.addMessage("Resuming from aborted session...", "status");
1145
+ }
1146
+ storedSessionIdForResume = null;
1147
+ }
1148
+ if (resumeFile) {
1149
+ startConfig.config.experimental_resume = resumeFile;
1078
1150
  }
1079
1151
  await client.startSession(
1080
1152
  startConfig,
@@ -1090,12 +1162,20 @@ async function runCodex(opts) {
1090
1162
  }
1091
1163
  } catch (error) {
1092
1164
  types.logger.warn("Error in codex session:", error);
1093
- if (error instanceof Error && error.name === "AbortError") {
1165
+ const isAbortError = error instanceof Error && error.name === "AbortError";
1166
+ if (isAbortError) {
1094
1167
  messageBuffer.addMessage("Aborted by user", "status");
1095
1168
  session.sendSessionEvent({ type: "message", message: "Aborted by user" });
1169
+ wasCreated = false;
1170
+ currentModeHash = null;
1171
+ types.logger.debug("[Codex] Marked session as not created after abort for proper resume");
1096
1172
  } else {
1097
1173
  messageBuffer.addMessage("Process exited unexpectedly", "status");
1098
1174
  session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
1175
+ if (client.hasActiveSession()) {
1176
+ storedSessionIdForResume = client.storeSessionForResume();
1177
+ types.logger.debug("[Codex] Stored session after unexpected error:", storedSessionIdForResume);
1178
+ }
1099
1179
  }
1100
1180
  } finally {
1101
1181
  permissionHandler.reset();
@@ -1107,7 +1187,7 @@ async function runCodex(opts) {
1107
1187
  }
1108
1188
  }
1109
1189
  } finally {
1110
- types.logger.debug("[codex]: Cleanup start");
1190
+ types.logger.debug("[codex]: Final cleanup start");
1111
1191
  logActiveHandles("cleanup-start");
1112
1192
  try {
1113
1193
  types.logger.debug("[codex]: sendSessionDeath");
@@ -1148,7 +1228,7 @@ async function runCodex(opts) {
1148
1228
  }
1149
1229
  messageBuffer.clear();
1150
1230
  logActiveHandles("cleanup-end");
1151
- types.logger.debug("[codex]: Cleanup completed");
1231
+ types.logger.debug("[codex]: Final cleanup completed");
1152
1232
  }
1153
1233
  }
1154
1234
 
@@ -1,12 +1,12 @@
1
1
  import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
- import { l as logger, A as ApiClient, r as readSettings, p as projectPath, c as configuration, b as packageJson } from './types-2wHnX7UW.mjs';
3
+ import { l as logger, A as ApiClient, r as readSettings, p as projectPath, c as configuration, b as packageJson } from './types-CYZG1S69.mjs';
4
4
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
5
5
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
6
6
  import { z } from 'zod';
7
7
  import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8
8
  import { randomUUID } from 'node:crypto';
9
- import { i as initialMachineMetadata, M as MessageQueue2, h as hashObject, a as MessageBuffer, s as startHappyServer, t as trimIdent } from './index-Dw96QD4T.mjs';
9
+ import { i as initialMachineMetadata, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, r as registerKillSessionHandler, a as MessageBuffer, s as startHappyServer, t as trimIdent, b as stopCaffeinate } from './index-Cp1f1oto.mjs';
10
10
  import os from 'node:os';
11
11
  import { resolve, join } from 'node:path';
12
12
  import fs from 'node:fs';
@@ -30,6 +30,7 @@ import 'node:readline';
30
30
  import 'node:url';
31
31
  import 'ps-list';
32
32
  import 'cross-spawn';
33
+ import 'tmp';
33
34
  import 'qrcode-terminal';
34
35
  import 'open';
35
36
  import 'fastify';
@@ -180,8 +181,16 @@ class CodexMcpClient {
180
181
  return this.sessionId !== null;
181
182
  }
182
183
  clearSession() {
184
+ const previousSessionId = this.sessionId;
183
185
  this.sessionId = null;
184
- logger.debug("[CodexMCP] Session cleared");
186
+ logger.debug("[CodexMCP] Session cleared, previous sessionId:", previousSessionId);
187
+ }
188
+ /**
189
+ * Store the current session ID without clearing it, useful for abort handling
190
+ */
191
+ storeSessionForResume() {
192
+ logger.debug("[CodexMCP] Storing session for potential resume:", this.sessionId);
193
+ return this.sessionId;
185
194
  }
186
195
  async disconnect() {
187
196
  if (!this.connected) return;
@@ -679,6 +688,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
679
688
  async function runCodex(opts) {
680
689
  const sessionTag = randomUUID();
681
690
  const api = await ApiClient.create(opts.credentials);
691
+ logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
682
692
  const settings = await readSettings();
683
693
  let machineId = settings?.machineId;
684
694
  if (!machineId) {
@@ -703,9 +713,9 @@ async function runCodex(opts) {
703
713
  happyHomeDir: configuration.happyHomeDir,
704
714
  happyLibDir: projectPath(),
705
715
  happyToolsDir: resolve(projectPath(), "tools", "unpacked"),
706
- startedFromDaemon: false,
716
+ startedFromDaemon: opts.startedBy === "daemon",
707
717
  hostPid: process.pid,
708
- startedBy: "terminal",
718
+ startedBy: opts.startedBy || "terminal",
709
719
  // Initialize lifecycle state
710
720
  lifecycleState: "running",
711
721
  lifecycleStateSince: Date.now(),
@@ -713,6 +723,17 @@ async function runCodex(opts) {
713
723
  };
714
724
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
715
725
  const session = api.sessionSyncClient(response);
726
+ try {
727
+ logger.debug(`[START] Reporting session ${response.id} to daemon`);
728
+ const result = await notifyDaemonSessionStarted(response.id, metadata);
729
+ if (result.error) {
730
+ logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
731
+ } else {
732
+ logger.debug(`[START] Reported session ${response.id} to daemon`);
733
+ }
734
+ } catch (error) {
735
+ logger.debug("[START] Failed to report to daemon (may not be running):", error);
736
+ }
716
737
  const messageQueue = new MessageQueue2((mode) => hashObject({
717
738
  permissionMode: mode.permissionMode,
718
739
  model: mode.model
@@ -766,22 +787,54 @@ async function runCodex(opts) {
766
787
  }
767
788
  let abortController = new AbortController();
768
789
  let shouldExit = false;
790
+ let storedSessionIdForResume = null;
769
791
  async function handleAbort() {
770
- logger.debug("[Codex] Abort requested");
792
+ logger.debug("[Codex] Abort requested - stopping current task");
771
793
  try {
794
+ if (client.hasActiveSession()) {
795
+ storedSessionIdForResume = client.storeSessionForResume();
796
+ logger.debug("[Codex] Stored session for resume:", storedSessionIdForResume);
797
+ }
772
798
  abortController.abort();
773
799
  messageQueue.reset();
774
800
  permissionHandler.reset();
775
801
  reasoningProcessor.abort();
776
802
  diffProcessor.reset();
777
- logger.debug("[Codex] Abort completed");
803
+ logger.debug("[Codex] Abort completed - session remains active");
778
804
  } catch (error) {
779
805
  logger.debug("[Codex] Error during abort:", error);
780
806
  } finally {
781
807
  abortController = new AbortController();
782
808
  }
783
809
  }
810
+ const handleKillSession = async () => {
811
+ logger.debug("[Codex] Kill session requested - terminating process");
812
+ await handleAbort();
813
+ logger.debug("[Codex] Abort completed, proceeding with termination");
814
+ try {
815
+ if (session) {
816
+ session.updateMetadata((currentMetadata) => ({
817
+ ...currentMetadata,
818
+ lifecycleState: "archived",
819
+ lifecycleStateSince: Date.now(),
820
+ archivedBy: "cli",
821
+ archiveReason: "User terminated"
822
+ }));
823
+ session.sendSessionDeath();
824
+ await session.flush();
825
+ await session.close();
826
+ }
827
+ stopCaffeinate();
828
+ happyServer.stop();
829
+ logger.debug("[Codex] Session termination complete, exiting");
830
+ process.exit(0);
831
+ } catch (error) {
832
+ logger.debug("[Codex] Error during session termination:", error);
833
+ process.exit(1);
834
+ }
835
+ };
784
836
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
837
+ registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
785
838
  const messageBuffer = new MessageBuffer();
786
839
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
787
840
  let inkInstance = null;
@@ -829,7 +882,8 @@ async function runCodex(opts) {
829
882
  return acc;
830
883
  };
831
884
  var collectFilesRecursive = collectFilesRecursive2;
832
- const rootDir = join(os.homedir(), ".codex", "sessions");
885
+ const codexHomeDir = process.env.CODEX_HOME || join(os.homedir(), ".codex");
886
+ const rootDir = join(codexHomeDir, "sessions");
833
887
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
834
888
  try {
835
889
  return fs.statSync(full).isFile();
@@ -996,8 +1050,13 @@ async function runCodex(opts) {
996
1050
  let message = pending;
997
1051
  pending = null;
998
1052
  if (!message) {
999
- const batch = await messageQueue.waitForMessagesAndGetAsString(abortController.signal);
1000
- if (!batch || shouldExit) {
1053
+ const waitSignal = abortController.signal;
1054
+ const batch = await messageQueue.waitForMessagesAndGetAsString(waitSignal);
1055
+ if (!batch) {
1056
+ if (waitSignal.aborted && !shouldExit) {
1057
+ logger.debug("[codex]: Wait aborted while idle; ignoring and continuing");
1058
+ continue;
1059
+ }
1001
1060
  logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
1002
1061
  break;
1003
1062
  }
@@ -1070,9 +1129,22 @@ async function runCodex(opts) {
1070
1129
  if (message.mode.model) {
1071
1130
  startConfig.model = message.mode.model;
1072
1131
  }
1132
+ let resumeFile = null;
1073
1133
  if (nextExperimentalResume) {
1074
- startConfig.config.experimental_resume = nextExperimentalResume;
1134
+ resumeFile = nextExperimentalResume;
1075
1135
  nextExperimentalResume = null;
1136
+ logger.debug("[Codex] Using resume file from mode change:", resumeFile);
1137
+ } else if (storedSessionIdForResume) {
1138
+ const abortResumeFile = findCodexResumeFile(storedSessionIdForResume);
1139
+ if (abortResumeFile) {
1140
+ resumeFile = abortResumeFile;
1141
+ logger.debug("[Codex] Using resume file from aborted session:", resumeFile);
1142
+ messageBuffer.addMessage("Resuming from aborted session...", "status");
1143
+ }
1144
+ storedSessionIdForResume = null;
1145
+ }
1146
+ if (resumeFile) {
1147
+ startConfig.config.experimental_resume = resumeFile;
1076
1148
  }
1077
1149
  await client.startSession(
1078
1150
  startConfig,
@@ -1088,12 +1160,20 @@ async function runCodex(opts) {
1088
1160
  }
1089
1161
  } catch (error) {
1090
1162
  logger.warn("Error in codex session:", error);
1091
- if (error instanceof Error && error.name === "AbortError") {
1163
+ const isAbortError = error instanceof Error && error.name === "AbortError";
1164
+ if (isAbortError) {
1092
1165
  messageBuffer.addMessage("Aborted by user", "status");
1093
1166
  session.sendSessionEvent({ type: "message", message: "Aborted by user" });
1167
+ wasCreated = false;
1168
+ currentModeHash = null;
1169
+ logger.debug("[Codex] Marked session as not created after abort for proper resume");
1094
1170
  } else {
1095
1171
  messageBuffer.addMessage("Process exited unexpectedly", "status");
1096
1172
  session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
1173
+ if (client.hasActiveSession()) {
1174
+ storedSessionIdForResume = client.storeSessionForResume();
1175
+ logger.debug("[Codex] Stored session after unexpected error:", storedSessionIdForResume);
1176
+ }
1097
1177
  }
1098
1178
  } finally {
1099
1179
  permissionHandler.reset();
@@ -1105,7 +1185,7 @@ async function runCodex(opts) {
1105
1185
  }
1106
1186
  }
1107
1187
  } finally {
1108
- logger.debug("[codex]: Cleanup start");
1188
+ logger.debug("[codex]: Final cleanup start");
1109
1189
  logActiveHandles("cleanup-start");
1110
1190
  try {
1111
1191
  logger.debug("[codex]: sendSessionDeath");
@@ -1146,7 +1226,7 @@ async function runCodex(opts) {
1146
1226
  }
1147
1227
  messageBuffer.clear();
1148
1228
  logActiveHandles("cleanup-end");
1149
- logger.debug("[codex]: Cleanup completed");
1229
+ logger.debug("[codex]: Final cleanup completed");
1150
1230
  }
1151
1231
  }
1152
1232
 
@@ -21,7 +21,7 @@ import { platform } from 'os';
21
21
  import { Expo } from 'expo-server-sdk';
22
22
 
23
23
  var name = "happy-coder";
24
- var version = "0.10.0-4";
24
+ var version = "0.10.1";
25
25
  var description = "Mobile and Web client for Claude Code and Codex";
26
26
  var author = "Kirill Dubovitskiy";
27
27
  var license = "MIT";
@@ -89,8 +89,8 @@ var scripts = {
89
89
  postinstall: "node scripts/unpack-tools.cjs"
90
90
  };
91
91
  var dependencies = {
92
- "@anthropic-ai/claude-code": "^1.0.102",
93
- "@anthropic-ai/sdk": "^0.56.0",
92
+ "@anthropic-ai/claude-code": "1.0.120",
93
+ "@anthropic-ai/sdk": "0.56.0",
94
94
  "@modelcontextprotocol/sdk": "^1.15.1",
95
95
  "@stablelib/base64": "^2.0.1",
96
96
  "@stablelib/hex": "^2.0.1",
@@ -99,6 +99,7 @@ var dependencies = {
99
99
  "@types/ps-list": "^6.2.1",
100
100
  "@types/qrcode-terminal": "^0.12.2",
101
101
  "@types/react": "^19.1.9",
102
+ "@types/tmp": "^0.2.6",
102
103
  axios: "^1.10.0",
103
104
  chalk: "^5.4.1",
104
105
  "cross-spawn": "^7.0.6",
@@ -114,6 +115,7 @@ var dependencies = {
114
115
  react: "^19.1.1",
115
116
  "socket.io-client": "^4.8.1",
116
117
  tar: "^7.4.3",
118
+ tmp: "^0.2.5",
117
119
  tweetnacl: "^1.0.3",
118
120
  zod: "^3.23.8"
119
121
  };
@@ -1627,11 +1629,12 @@ class ApiMachineClient {
1627
1629
  requestShutdown
1628
1630
  }) {
1629
1631
  this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
1630
- const { directory, sessionId, machineId, approvedNewDirectoryCreation } = params || {};
1632
+ const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token } = params || {};
1633
+ logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
1631
1634
  if (!directory) {
1632
1635
  throw new Error("Directory is required");
1633
1636
  }
1634
- const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation });
1637
+ const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token });
1635
1638
  switch (result.type) {
1636
1639
  case "success":
1637
1640
  logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
@@ -42,7 +42,7 @@ function _interopNamespaceDefault(e) {
42
42
  var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
43
43
 
44
44
  var name = "happy-coder";
45
- var version = "0.10.0-4";
45
+ var version = "0.10.1";
46
46
  var description = "Mobile and Web client for Claude Code and Codex";
47
47
  var author = "Kirill Dubovitskiy";
48
48
  var license = "MIT";
@@ -110,8 +110,8 @@ var scripts = {
110
110
  postinstall: "node scripts/unpack-tools.cjs"
111
111
  };
112
112
  var dependencies = {
113
- "@anthropic-ai/claude-code": "^1.0.102",
114
- "@anthropic-ai/sdk": "^0.56.0",
113
+ "@anthropic-ai/claude-code": "1.0.120",
114
+ "@anthropic-ai/sdk": "0.56.0",
115
115
  "@modelcontextprotocol/sdk": "^1.15.1",
116
116
  "@stablelib/base64": "^2.0.1",
117
117
  "@stablelib/hex": "^2.0.1",
@@ -120,6 +120,7 @@ var dependencies = {
120
120
  "@types/ps-list": "^6.2.1",
121
121
  "@types/qrcode-terminal": "^0.12.2",
122
122
  "@types/react": "^19.1.9",
123
+ "@types/tmp": "^0.2.6",
123
124
  axios: "^1.10.0",
124
125
  chalk: "^5.4.1",
125
126
  "cross-spawn": "^7.0.6",
@@ -135,6 +136,7 @@ var dependencies = {
135
136
  react: "^19.1.1",
136
137
  "socket.io-client": "^4.8.1",
137
138
  tar: "^7.4.3",
139
+ tmp: "^0.2.5",
138
140
  tweetnacl: "^1.0.3",
139
141
  zod: "^3.23.8"
140
142
  };
@@ -1015,7 +1017,7 @@ class RpcHandlerManager {
1015
1017
  }
1016
1018
  }
1017
1019
 
1018
- 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-BcDnTXMg.cjs', document.baseURI).href))));
1020
+ 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-DOKP_I5s.cjs', document.baseURI).href))));
1019
1021
  function projectPath() {
1020
1022
  const path$1 = path.resolve(__dirname$1, "..");
1021
1023
  return path$1;
@@ -1648,11 +1650,12 @@ class ApiMachineClient {
1648
1650
  requestShutdown
1649
1651
  }) {
1650
1652
  this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
1651
- const { directory, sessionId, machineId, approvedNewDirectoryCreation } = params || {};
1653
+ const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token } = params || {};
1654
+ logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
1652
1655
  if (!directory) {
1653
1656
  throw new Error("Directory is required");
1654
1657
  }
1655
- const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation });
1658
+ const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token });
1656
1659
  switch (result.type) {
1657
1660
  case "success":
1658
1661
  logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.10.0-4",
3
+ "version": "0.10.1",
4
4
  "description": "Mobile and Web client for Claude Code and Codex",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -68,8 +68,8 @@
68
68
  "postinstall": "node scripts/unpack-tools.cjs"
69
69
  },
70
70
  "dependencies": {
71
- "@anthropic-ai/claude-code": "^1.0.102",
72
- "@anthropic-ai/sdk": "^0.56.0",
71
+ "@anthropic-ai/claude-code": "1.0.120",
72
+ "@anthropic-ai/sdk": "0.56.0",
73
73
  "@modelcontextprotocol/sdk": "^1.15.1",
74
74
  "@stablelib/base64": "^2.0.1",
75
75
  "@stablelib/hex": "^2.0.1",
@@ -78,6 +78,7 @@
78
78
  "@types/ps-list": "^6.2.1",
79
79
  "@types/qrcode-terminal": "^0.12.2",
80
80
  "@types/react": "^19.1.9",
81
+ "@types/tmp": "^0.2.6",
81
82
  "axios": "^1.10.0",
82
83
  "chalk": "^5.4.1",
83
84
  "cross-spawn": "^7.0.6",
@@ -93,6 +94,7 @@
93
94
  "react": "^19.1.1",
94
95
  "socket.io-client": "^4.8.1",
95
96
  "tar": "^7.4.3",
97
+ "tmp": "^0.2.5",
96
98
  "tweetnacl": "^1.0.3",
97
99
  "zod": "^3.23.8"
98
100
  },