happy-coder 0.10.0-4 → 0.10.0

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.
@@ -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-8Ad05p3x.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';
@@ -4265,7 +4266,22 @@ async function startDaemon() {
4265
4266
  }
4266
4267
  }
4267
4268
  try {
4269
+ let extraEnv = {};
4270
+ if (options.token) {
4271
+ if (options.agent === "codex") {
4272
+ const codexHomeDir = tmp.dirSync();
4273
+ fs.writeFile(join$1(codexHomeDir.name, "auth.json"), options.token);
4274
+ extraEnv = {
4275
+ CODEX_HOME: codexHomeDir.name
4276
+ };
4277
+ } else {
4278
+ extraEnv = {
4279
+ CLAUDE_CODE_OAUTH_TOKEN: options.token
4280
+ };
4281
+ }
4282
+ }
4268
4283
  const args = [
4284
+ options.agent === "claude" ? "claude" : "codex",
4269
4285
  "--happy-starting-mode",
4270
4286
  "remote",
4271
4287
  "--started-by",
@@ -4275,9 +4291,12 @@ async function startDaemon() {
4275
4291
  cwd: directory,
4276
4292
  detached: true,
4277
4293
  // Sessions stay alive when daemon stops
4278
- stdio: ["ignore", "pipe", "pipe"]
4294
+ stdio: ["ignore", "pipe", "pipe"],
4279
4295
  // Capture stdout/stderr for debugging
4280
- // env is inherited automatically from parent process
4296
+ env: {
4297
+ ...process.env,
4298
+ ...extraEnv
4299
+ }
4281
4300
  });
4282
4301
  if (process.env.DEBUG) {
4283
4302
  happyProcess.stdout?.on("data", (data) => {
@@ -4628,7 +4647,8 @@ async function runClaude(credentials, options = {}) {
4628
4647
  startedBy: options.startedBy || "terminal",
4629
4648
  // Initialize lifecycle state
4630
4649
  lifecycleState: "running",
4631
- lifecycleStateSince: Date.now()
4650
+ lifecycleStateSince: Date.now(),
4651
+ flavor: "claude"
4632
4652
  };
4633
4653
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4634
4654
  logger.debug(`Session created: ${response.id}`);
@@ -5723,11 +5743,17 @@ async function handleConnectVendor(vendor, displayName) {
5723
5743
  return;
5724
5744
  } else if (subcommand === "codex") {
5725
5745
  try {
5726
- const { runCodex } = await import('./runCodex-BNH8w4O9.mjs');
5746
+ const { runCodex } = await import('./runCodex-HlLNepHI.mjs');
5747
+ let startedBy = void 0;
5748
+ for (let i = 1; i < args.length; i++) {
5749
+ if (args[i] === "--started-by") {
5750
+ startedBy = args[++i];
5751
+ }
5752
+ }
5727
5753
  const {
5728
5754
  credentials
5729
5755
  } = await authAndSetupMachineIfNeeded();
5730
- await runCodex({ credentials });
5756
+ await runCodex({ credentials, startedBy });
5731
5757
  } catch (error) {
5732
5758
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
5733
5759
  if (process.env.DEBUG) {
@@ -5860,6 +5886,9 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5860
5886
  }
5861
5887
  return;
5862
5888
  } else {
5889
+ if (args.length > 0 && args[0] === "claude") {
5890
+ args.shift();
5891
+ }
5863
5892
  const options = {};
5864
5893
  let showHelp = false;
5865
5894
  let showVersion = false;
@@ -6022,4 +6051,4 @@ ${chalk.bold("Examples:")}
6022
6051
  }
6023
6052
  }
6024
6053
 
6025
- export { MessageQueue2 as M, MessageBuffer as a, hashObject as h, initialMachineMetadata as i, startHappyServer as s, trimIdent as t };
6054
+ 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 };
@@ -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-CQOz_mPp.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;
@@ -923,7 +943,7 @@ class AbortError extends Error {
923
943
  }
924
944
  }
925
945
 
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)));
946
+ 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-ettJex_e.cjs', document.baseURI).href)));
927
947
  const __dirname$1 = node_path.join(__filename$1, "..");
928
948
  function getDefaultClaudeCodePath() {
929
949
  return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
@@ -4268,7 +4288,22 @@ async function startDaemon() {
4268
4288
  }
4269
4289
  }
4270
4290
  try {
4291
+ let extraEnv = {};
4292
+ if (options.token) {
4293
+ if (options.agent === "codex") {
4294
+ const codexHomeDir = tmp__namespace.dirSync();
4295
+ fs$1.writeFile(path.join(codexHomeDir.name, "auth.json"), options.token);
4296
+ extraEnv = {
4297
+ CODEX_HOME: codexHomeDir.name
4298
+ };
4299
+ } else {
4300
+ extraEnv = {
4301
+ CLAUDE_CODE_OAUTH_TOKEN: options.token
4302
+ };
4303
+ }
4304
+ }
4271
4305
  const args = [
4306
+ options.agent === "claude" ? "claude" : "codex",
4272
4307
  "--happy-starting-mode",
4273
4308
  "remote",
4274
4309
  "--started-by",
@@ -4278,9 +4313,12 @@ async function startDaemon() {
4278
4313
  cwd: directory,
4279
4314
  detached: true,
4280
4315
  // Sessions stay alive when daemon stops
4281
- stdio: ["ignore", "pipe", "pipe"]
4316
+ stdio: ["ignore", "pipe", "pipe"],
4282
4317
  // Capture stdout/stderr for debugging
4283
- // env is inherited automatically from parent process
4318
+ env: {
4319
+ ...process.env,
4320
+ ...extraEnv
4321
+ }
4284
4322
  });
4285
4323
  if (process.env.DEBUG) {
4286
4324
  happyProcess.stdout?.on("data", (data) => {
@@ -4631,7 +4669,8 @@ async function runClaude(credentials, options = {}) {
4631
4669
  startedBy: options.startedBy || "terminal",
4632
4670
  // Initialize lifecycle state
4633
4671
  lifecycleState: "running",
4634
- lifecycleStateSince: Date.now()
4672
+ lifecycleStateSince: Date.now(),
4673
+ flavor: "claude"
4635
4674
  };
4636
4675
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4637
4676
  types.logger.debug(`Session created: ${response.id}`);
@@ -5726,11 +5765,17 @@ async function handleConnectVendor(vendor, displayName) {
5726
5765
  return;
5727
5766
  } else if (subcommand === "codex") {
5728
5767
  try {
5729
- const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BLNf5zb1.cjs'); });
5768
+ const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-QdQBx9HK.cjs'); });
5769
+ let startedBy = void 0;
5770
+ for (let i = 1; i < args.length; i++) {
5771
+ if (args[i] === "--started-by") {
5772
+ startedBy = args[++i];
5773
+ }
5774
+ }
5730
5775
  const {
5731
5776
  credentials
5732
5777
  } = await authAndSetupMachineIfNeeded();
5733
- await runCodex({ credentials });
5778
+ await runCodex({ credentials, startedBy });
5734
5779
  } catch (error) {
5735
5780
  console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
5736
5781
  if (process.env.DEBUG) {
@@ -5863,6 +5908,9 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5863
5908
  }
5864
5909
  return;
5865
5910
  } else {
5911
+ if (args.length > 0 && args[0] === "claude") {
5912
+ args.shift();
5913
+ }
5866
5914
  const options = {};
5867
5915
  let showHelp = false;
5868
5916
  let showVersion = false;
@@ -6029,5 +6077,8 @@ exports.MessageBuffer = MessageBuffer;
6029
6077
  exports.MessageQueue2 = MessageQueue2;
6030
6078
  exports.hashObject = hashObject;
6031
6079
  exports.initialMachineMetadata = initialMachineMetadata;
6080
+ exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
6081
+ exports.registerKillSessionHandler = registerKillSessionHandler;
6032
6082
  exports.startHappyServer = startHappyServer;
6083
+ exports.stopCaffeinate = stopCaffeinate;
6033
6084
  exports.trimIdent = trimIdent;
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-ettJex_e.cjs');
5
+ require('./types-CQOz_mPp.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-BI37NnoW.mjs';
3
+ import './types-8Ad05p3x.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-CQOz_mPp.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-8Ad05p3x.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -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-8Ad05p3x.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-BI37NnoW.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';
@@ -679,6 +680,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
679
680
  async function runCodex(opts) {
680
681
  const sessionTag = randomUUID();
681
682
  const api = await ApiClient.create(opts.credentials);
683
+ logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
682
684
  const settings = await readSettings();
683
685
  let machineId = settings?.machineId;
684
686
  if (!machineId) {
@@ -703,9 +705,9 @@ async function runCodex(opts) {
703
705
  happyHomeDir: configuration.happyHomeDir,
704
706
  happyLibDir: projectPath(),
705
707
  happyToolsDir: resolve(projectPath(), "tools", "unpacked"),
706
- startedFromDaemon: false,
708
+ startedFromDaemon: opts.startedBy === "daemon",
707
709
  hostPid: process.pid,
708
- startedBy: "terminal",
710
+ startedBy: opts.startedBy || "terminal",
709
711
  // Initialize lifecycle state
710
712
  lifecycleState: "running",
711
713
  lifecycleStateSince: Date.now(),
@@ -713,6 +715,17 @@ async function runCodex(opts) {
713
715
  };
714
716
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
715
717
  const session = api.sessionSyncClient(response);
718
+ try {
719
+ logger.debug(`[START] Reporting session ${response.id} to daemon`);
720
+ const result = await notifyDaemonSessionStarted(response.id, metadata);
721
+ if (result.error) {
722
+ logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
723
+ } else {
724
+ logger.debug(`[START] Reported session ${response.id} to daemon`);
725
+ }
726
+ } catch (error) {
727
+ logger.debug("[START] Failed to report to daemon (may not be running):", error);
728
+ }
716
729
  const messageQueue = new MessageQueue2((mode) => hashObject({
717
730
  permissionMode: mode.permissionMode,
718
731
  model: mode.model
@@ -781,7 +794,34 @@ async function runCodex(opts) {
781
794
  abortController = new AbortController();
782
795
  }
783
796
  }
797
+ const cleanup = async () => {
798
+ logger.debug("[Codex] Cleanup start");
799
+ await handleAbort();
800
+ logger.debug("[Codex] Cleanup completed");
801
+ try {
802
+ if (session) {
803
+ session.updateMetadata((currentMetadata) => ({
804
+ ...currentMetadata,
805
+ lifecycleState: "archived",
806
+ lifecycleStateSince: Date.now(),
807
+ archivedBy: "cli",
808
+ archiveReason: "User terminated"
809
+ }));
810
+ session.sendSessionDeath();
811
+ await session.flush();
812
+ await session.close();
813
+ }
814
+ stopCaffeinate();
815
+ happyServer.stop();
816
+ logger.debug("[Codex] Cleanup complete, exiting");
817
+ process.exit(0);
818
+ } catch (error) {
819
+ logger.debug("[Codex] Error during cleanup:", error);
820
+ process.exit(1);
821
+ }
822
+ };
784
823
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
824
+ registerKillSessionHandler(session.rpcHandlerManager, cleanup);
785
825
  const messageBuffer = new MessageBuffer();
786
826
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
787
827
  let inkInstance = null;
@@ -829,7 +869,8 @@ async function runCodex(opts) {
829
869
  return acc;
830
870
  };
831
871
  var collectFilesRecursive = collectFilesRecursive2;
832
- const rootDir = join(os.homedir(), ".codex", "sessions");
872
+ const codexHomeDir = process.env.CODEX_HOME || join(os.homedir(), ".codex");
873
+ const rootDir = join(codexHomeDir, "sessions");
833
874
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
834
875
  try {
835
876
  return fs.statSync(full).isFile();
@@ -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-CQOz_mPp.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-ettJex_e.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');
@@ -681,6 +682,7 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
681
682
  async function runCodex(opts) {
682
683
  const sessionTag = node_crypto.randomUUID();
683
684
  const api = await types.ApiClient.create(opts.credentials);
685
+ types.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
684
686
  const settings = await types.readSettings();
685
687
  let machineId = settings?.machineId;
686
688
  if (!machineId) {
@@ -705,9 +707,9 @@ async function runCodex(opts) {
705
707
  happyHomeDir: types.configuration.happyHomeDir,
706
708
  happyLibDir: types.projectPath(),
707
709
  happyToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
708
- startedFromDaemon: false,
710
+ startedFromDaemon: opts.startedBy === "daemon",
709
711
  hostPid: process.pid,
710
- startedBy: "terminal",
712
+ startedBy: opts.startedBy || "terminal",
711
713
  // Initialize lifecycle state
712
714
  lifecycleState: "running",
713
715
  lifecycleStateSince: Date.now(),
@@ -715,6 +717,17 @@ async function runCodex(opts) {
715
717
  };
716
718
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
717
719
  const session = api.sessionSyncClient(response);
720
+ try {
721
+ types.logger.debug(`[START] Reporting session ${response.id} to daemon`);
722
+ const result = await index.notifyDaemonSessionStarted(response.id, metadata);
723
+ if (result.error) {
724
+ types.logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
725
+ } else {
726
+ types.logger.debug(`[START] Reported session ${response.id} to daemon`);
727
+ }
728
+ } catch (error) {
729
+ types.logger.debug("[START] Failed to report to daemon (may not be running):", error);
730
+ }
718
731
  const messageQueue = new index.MessageQueue2((mode) => index.hashObject({
719
732
  permissionMode: mode.permissionMode,
720
733
  model: mode.model
@@ -783,7 +796,34 @@ async function runCodex(opts) {
783
796
  abortController = new AbortController();
784
797
  }
785
798
  }
799
+ const cleanup = async () => {
800
+ types.logger.debug("[Codex] Cleanup start");
801
+ await handleAbort();
802
+ types.logger.debug("[Codex] Cleanup completed");
803
+ try {
804
+ if (session) {
805
+ session.updateMetadata((currentMetadata) => ({
806
+ ...currentMetadata,
807
+ lifecycleState: "archived",
808
+ lifecycleStateSince: Date.now(),
809
+ archivedBy: "cli",
810
+ archiveReason: "User terminated"
811
+ }));
812
+ session.sendSessionDeath();
813
+ await session.flush();
814
+ await session.close();
815
+ }
816
+ index.stopCaffeinate();
817
+ happyServer.stop();
818
+ types.logger.debug("[Codex] Cleanup complete, exiting");
819
+ process.exit(0);
820
+ } catch (error) {
821
+ types.logger.debug("[Codex] Error during cleanup:", error);
822
+ process.exit(1);
823
+ }
824
+ };
786
825
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
826
+ index.registerKillSessionHandler(session.rpcHandlerManager, cleanup);
787
827
  const messageBuffer = new index.MessageBuffer();
788
828
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
789
829
  let inkInstance = null;
@@ -831,7 +871,8 @@ async function runCodex(opts) {
831
871
  return acc;
832
872
  };
833
873
  var collectFilesRecursive = collectFilesRecursive2;
834
- const rootDir = node_path.join(os.homedir(), ".codex", "sessions");
874
+ const codexHomeDir = process.env.CODEX_HOME || node_path.join(os.homedir(), ".codex");
875
+ const rootDir = node_path.join(codexHomeDir, "sessions");
835
876
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
836
877
  try {
837
878
  return fs.statSync(full).isFile();
@@ -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.0";
25
25
  var description = "Mobile and Web client for Claude Code and Codex";
26
26
  var author = "Kirill Dubovitskiy";
27
27
  var license = "MIT";
@@ -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.0";
46
46
  var description = "Mobile and Web client for Claude Code and Codex";
47
47
  var author = "Kirill Dubovitskiy";
48
48
  var license = "MIT";
@@ -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-CQOz_mPp.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.0",
4
4
  "description": "Mobile and Web client for Claude Code and Codex",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -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
  },