happy-coder 0.10.0 → 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.
- package/dist/{index-ettJex_e.cjs → index-B_nemqpL.cjs} +44 -6
- package/dist/{index-BI37NnoW.mjs → index-Cp1f1oto.mjs} +43 -5
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{runCodex-QdQBx9HK.cjs → runCodex-BjT2Vmcd.cjs} +56 -17
- package/dist/{runCodex-HlLNepHI.mjs → runCodex-CHAghw9e.mjs} +56 -17
- package/dist/{types-8Ad05p3x.mjs → types-CYZG1S69.mjs} +3 -3
- package/dist/{types-CQOz_mPp.cjs → types-DOKP_I5s.cjs} +4 -4
- package/package.json +3 -3
|
@@ -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-
|
|
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');
|
|
@@ -186,9 +186,39 @@ function trimIdent(text) {
|
|
|
186
186
|
return trimmedLines.join("\n");
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
}
|
|
191
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(`
|
|
192
222
|
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
193
223
|
|
|
194
224
|
<main commit message>
|
|
@@ -198,7 +228,15 @@ const systemPrompt = trimIdent(`
|
|
|
198
228
|
|
|
199
229
|
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
200
230
|
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
201
|
-
`);
|
|
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
|
+
})();
|
|
202
240
|
|
|
203
241
|
const claudeCliPath = node_path.resolve(node_path.join(types.projectPath(), "scripts", "claude_local_launcher.cjs"));
|
|
204
242
|
async function claudeLocal(opts) {
|
|
@@ -943,7 +981,7 @@ class AbortError extends Error {
|
|
|
943
981
|
}
|
|
944
982
|
}
|
|
945
983
|
|
|
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-
|
|
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)));
|
|
947
985
|
const __dirname$1 = node_path.join(__filename$1, "..");
|
|
948
986
|
function getDefaultClaudeCodePath() {
|
|
949
987
|
return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
|
|
@@ -5765,7 +5803,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5765
5803
|
return;
|
|
5766
5804
|
} else if (subcommand === "codex") {
|
|
5767
5805
|
try {
|
|
5768
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
5806
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BjT2Vmcd.cjs'); });
|
|
5769
5807
|
let startedBy = void 0;
|
|
5770
5808
|
for (let i = 1; i < args.length; i++) {
|
|
5771
5809
|
if (args[i] === "--started-by") {
|
|
@@ -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-
|
|
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';
|
|
@@ -164,9 +164,39 @@ function trimIdent(text) {
|
|
|
164
164
|
return trimmedLines.join("\n");
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
}
|
|
169
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(`
|
|
170
200
|
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
171
201
|
|
|
172
202
|
<main commit message>
|
|
@@ -176,7 +206,15 @@ const systemPrompt = trimIdent(`
|
|
|
176
206
|
|
|
177
207
|
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
178
208
|
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
179
|
-
`);
|
|
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
|
+
})();
|
|
180
218
|
|
|
181
219
|
const claudeCliPath = resolve(join(projectPath(), "scripts", "claude_local_launcher.cjs"));
|
|
182
220
|
async function claudeLocal(opts) {
|
|
@@ -5743,7 +5781,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5743
5781
|
return;
|
|
5744
5782
|
} else if (subcommand === "codex") {
|
|
5745
5783
|
try {
|
|
5746
|
-
const { runCodex } = await import('./runCodex-
|
|
5784
|
+
const { runCodex } = await import('./runCodex-CHAghw9e.mjs');
|
|
5747
5785
|
let startedBy = void 0;
|
|
5748
5786
|
for (let i = 1; i < args.length; i++) {
|
|
5749
5787
|
if (args[i] === "--started-by") {
|
package/dist/index.cjs
CHANGED
package/dist/index.mjs
CHANGED
package/dist/lib.cjs
CHANGED
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-
|
|
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-
|
|
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-
|
|
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');
|
|
@@ -183,8 +183,16 @@ class CodexMcpClient {
|
|
|
183
183
|
return this.sessionId !== null;
|
|
184
184
|
}
|
|
185
185
|
clearSession() {
|
|
186
|
+
const previousSessionId = this.sessionId;
|
|
186
187
|
this.sessionId = null;
|
|
187
|
-
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;
|
|
188
196
|
}
|
|
189
197
|
async disconnect() {
|
|
190
198
|
if (!this.connected) return;
|
|
@@ -781,25 +789,30 @@ async function runCodex(opts) {
|
|
|
781
789
|
}
|
|
782
790
|
let abortController = new AbortController();
|
|
783
791
|
let shouldExit = false;
|
|
792
|
+
let storedSessionIdForResume = null;
|
|
784
793
|
async function handleAbort() {
|
|
785
|
-
types.logger.debug("[Codex] Abort requested");
|
|
794
|
+
types.logger.debug("[Codex] Abort requested - stopping current task");
|
|
786
795
|
try {
|
|
796
|
+
if (client.hasActiveSession()) {
|
|
797
|
+
storedSessionIdForResume = client.storeSessionForResume();
|
|
798
|
+
types.logger.debug("[Codex] Stored session for resume:", storedSessionIdForResume);
|
|
799
|
+
}
|
|
787
800
|
abortController.abort();
|
|
788
801
|
messageQueue.reset();
|
|
789
802
|
permissionHandler.reset();
|
|
790
803
|
reasoningProcessor.abort();
|
|
791
804
|
diffProcessor.reset();
|
|
792
|
-
types.logger.debug("[Codex] Abort completed");
|
|
805
|
+
types.logger.debug("[Codex] Abort completed - session remains active");
|
|
793
806
|
} catch (error) {
|
|
794
807
|
types.logger.debug("[Codex] Error during abort:", error);
|
|
795
808
|
} finally {
|
|
796
809
|
abortController = new AbortController();
|
|
797
810
|
}
|
|
798
811
|
}
|
|
799
|
-
const
|
|
800
|
-
types.logger.debug("[Codex]
|
|
812
|
+
const handleKillSession = async () => {
|
|
813
|
+
types.logger.debug("[Codex] Kill session requested - terminating process");
|
|
801
814
|
await handleAbort();
|
|
802
|
-
types.logger.debug("[Codex]
|
|
815
|
+
types.logger.debug("[Codex] Abort completed, proceeding with termination");
|
|
803
816
|
try {
|
|
804
817
|
if (session) {
|
|
805
818
|
session.updateMetadata((currentMetadata) => ({
|
|
@@ -815,15 +828,15 @@ async function runCodex(opts) {
|
|
|
815
828
|
}
|
|
816
829
|
index.stopCaffeinate();
|
|
817
830
|
happyServer.stop();
|
|
818
|
-
types.logger.debug("[Codex]
|
|
831
|
+
types.logger.debug("[Codex] Session termination complete, exiting");
|
|
819
832
|
process.exit(0);
|
|
820
833
|
} catch (error) {
|
|
821
|
-
types.logger.debug("[Codex] Error during
|
|
834
|
+
types.logger.debug("[Codex] Error during session termination:", error);
|
|
822
835
|
process.exit(1);
|
|
823
836
|
}
|
|
824
837
|
};
|
|
825
838
|
session.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
826
|
-
index.registerKillSessionHandler(session.rpcHandlerManager,
|
|
839
|
+
index.registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
|
|
827
840
|
const messageBuffer = new index.MessageBuffer();
|
|
828
841
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
829
842
|
let inkInstance = null;
|
|
@@ -1039,8 +1052,13 @@ async function runCodex(opts) {
|
|
|
1039
1052
|
let message = pending;
|
|
1040
1053
|
pending = null;
|
|
1041
1054
|
if (!message) {
|
|
1042
|
-
const
|
|
1043
|
-
|
|
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
|
+
}
|
|
1044
1062
|
types.logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
|
|
1045
1063
|
break;
|
|
1046
1064
|
}
|
|
@@ -1113,9 +1131,22 @@ async function runCodex(opts) {
|
|
|
1113
1131
|
if (message.mode.model) {
|
|
1114
1132
|
startConfig.model = message.mode.model;
|
|
1115
1133
|
}
|
|
1134
|
+
let resumeFile = null;
|
|
1116
1135
|
if (nextExperimentalResume) {
|
|
1117
|
-
|
|
1136
|
+
resumeFile = nextExperimentalResume;
|
|
1118
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;
|
|
1119
1150
|
}
|
|
1120
1151
|
await client.startSession(
|
|
1121
1152
|
startConfig,
|
|
@@ -1131,12 +1162,20 @@ async function runCodex(opts) {
|
|
|
1131
1162
|
}
|
|
1132
1163
|
} catch (error) {
|
|
1133
1164
|
types.logger.warn("Error in codex session:", error);
|
|
1134
|
-
|
|
1165
|
+
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1166
|
+
if (isAbortError) {
|
|
1135
1167
|
messageBuffer.addMessage("Aborted by user", "status");
|
|
1136
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");
|
|
1137
1172
|
} else {
|
|
1138
1173
|
messageBuffer.addMessage("Process exited unexpectedly", "status");
|
|
1139
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
|
+
}
|
|
1140
1179
|
}
|
|
1141
1180
|
} finally {
|
|
1142
1181
|
permissionHandler.reset();
|
|
@@ -1148,7 +1187,7 @@ async function runCodex(opts) {
|
|
|
1148
1187
|
}
|
|
1149
1188
|
}
|
|
1150
1189
|
} finally {
|
|
1151
|
-
types.logger.debug("[codex]:
|
|
1190
|
+
types.logger.debug("[codex]: Final cleanup start");
|
|
1152
1191
|
logActiveHandles("cleanup-start");
|
|
1153
1192
|
try {
|
|
1154
1193
|
types.logger.debug("[codex]: sendSessionDeath");
|
|
@@ -1189,7 +1228,7 @@ async function runCodex(opts) {
|
|
|
1189
1228
|
}
|
|
1190
1229
|
messageBuffer.clear();
|
|
1191
1230
|
logActiveHandles("cleanup-end");
|
|
1192
|
-
types.logger.debug("[codex]:
|
|
1231
|
+
types.logger.debug("[codex]: Final cleanup completed");
|
|
1193
1232
|
}
|
|
1194
1233
|
}
|
|
1195
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-
|
|
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, 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-
|
|
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';
|
|
@@ -181,8 +181,16 @@ class CodexMcpClient {
|
|
|
181
181
|
return this.sessionId !== null;
|
|
182
182
|
}
|
|
183
183
|
clearSession() {
|
|
184
|
+
const previousSessionId = this.sessionId;
|
|
184
185
|
this.sessionId = null;
|
|
185
|
-
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;
|
|
186
194
|
}
|
|
187
195
|
async disconnect() {
|
|
188
196
|
if (!this.connected) return;
|
|
@@ -779,25 +787,30 @@ async function runCodex(opts) {
|
|
|
779
787
|
}
|
|
780
788
|
let abortController = new AbortController();
|
|
781
789
|
let shouldExit = false;
|
|
790
|
+
let storedSessionIdForResume = null;
|
|
782
791
|
async function handleAbort() {
|
|
783
|
-
logger.debug("[Codex] Abort requested");
|
|
792
|
+
logger.debug("[Codex] Abort requested - stopping current task");
|
|
784
793
|
try {
|
|
794
|
+
if (client.hasActiveSession()) {
|
|
795
|
+
storedSessionIdForResume = client.storeSessionForResume();
|
|
796
|
+
logger.debug("[Codex] Stored session for resume:", storedSessionIdForResume);
|
|
797
|
+
}
|
|
785
798
|
abortController.abort();
|
|
786
799
|
messageQueue.reset();
|
|
787
800
|
permissionHandler.reset();
|
|
788
801
|
reasoningProcessor.abort();
|
|
789
802
|
diffProcessor.reset();
|
|
790
|
-
logger.debug("[Codex] Abort completed");
|
|
803
|
+
logger.debug("[Codex] Abort completed - session remains active");
|
|
791
804
|
} catch (error) {
|
|
792
805
|
logger.debug("[Codex] Error during abort:", error);
|
|
793
806
|
} finally {
|
|
794
807
|
abortController = new AbortController();
|
|
795
808
|
}
|
|
796
809
|
}
|
|
797
|
-
const
|
|
798
|
-
logger.debug("[Codex]
|
|
810
|
+
const handleKillSession = async () => {
|
|
811
|
+
logger.debug("[Codex] Kill session requested - terminating process");
|
|
799
812
|
await handleAbort();
|
|
800
|
-
logger.debug("[Codex]
|
|
813
|
+
logger.debug("[Codex] Abort completed, proceeding with termination");
|
|
801
814
|
try {
|
|
802
815
|
if (session) {
|
|
803
816
|
session.updateMetadata((currentMetadata) => ({
|
|
@@ -813,15 +826,15 @@ async function runCodex(opts) {
|
|
|
813
826
|
}
|
|
814
827
|
stopCaffeinate();
|
|
815
828
|
happyServer.stop();
|
|
816
|
-
logger.debug("[Codex]
|
|
829
|
+
logger.debug("[Codex] Session termination complete, exiting");
|
|
817
830
|
process.exit(0);
|
|
818
831
|
} catch (error) {
|
|
819
|
-
logger.debug("[Codex] Error during
|
|
832
|
+
logger.debug("[Codex] Error during session termination:", error);
|
|
820
833
|
process.exit(1);
|
|
821
834
|
}
|
|
822
835
|
};
|
|
823
836
|
session.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
824
|
-
registerKillSessionHandler(session.rpcHandlerManager,
|
|
837
|
+
registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
|
|
825
838
|
const messageBuffer = new MessageBuffer();
|
|
826
839
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
827
840
|
let inkInstance = null;
|
|
@@ -1037,8 +1050,13 @@ async function runCodex(opts) {
|
|
|
1037
1050
|
let message = pending;
|
|
1038
1051
|
pending = null;
|
|
1039
1052
|
if (!message) {
|
|
1040
|
-
const
|
|
1041
|
-
|
|
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
|
+
}
|
|
1042
1060
|
logger.debug(`[codex]: batch=${!!batch}, shouldExit=${shouldExit}`);
|
|
1043
1061
|
break;
|
|
1044
1062
|
}
|
|
@@ -1111,9 +1129,22 @@ async function runCodex(opts) {
|
|
|
1111
1129
|
if (message.mode.model) {
|
|
1112
1130
|
startConfig.model = message.mode.model;
|
|
1113
1131
|
}
|
|
1132
|
+
let resumeFile = null;
|
|
1114
1133
|
if (nextExperimentalResume) {
|
|
1115
|
-
|
|
1134
|
+
resumeFile = nextExperimentalResume;
|
|
1116
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;
|
|
1117
1148
|
}
|
|
1118
1149
|
await client.startSession(
|
|
1119
1150
|
startConfig,
|
|
@@ -1129,12 +1160,20 @@ async function runCodex(opts) {
|
|
|
1129
1160
|
}
|
|
1130
1161
|
} catch (error) {
|
|
1131
1162
|
logger.warn("Error in codex session:", error);
|
|
1132
|
-
|
|
1163
|
+
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1164
|
+
if (isAbortError) {
|
|
1133
1165
|
messageBuffer.addMessage("Aborted by user", "status");
|
|
1134
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");
|
|
1135
1170
|
} else {
|
|
1136
1171
|
messageBuffer.addMessage("Process exited unexpectedly", "status");
|
|
1137
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
|
+
}
|
|
1138
1177
|
}
|
|
1139
1178
|
} finally {
|
|
1140
1179
|
permissionHandler.reset();
|
|
@@ -1146,7 +1185,7 @@ async function runCodex(opts) {
|
|
|
1146
1185
|
}
|
|
1147
1186
|
}
|
|
1148
1187
|
} finally {
|
|
1149
|
-
logger.debug("[codex]:
|
|
1188
|
+
logger.debug("[codex]: Final cleanup start");
|
|
1150
1189
|
logActiveHandles("cleanup-start");
|
|
1151
1190
|
try {
|
|
1152
1191
|
logger.debug("[codex]: sendSessionDeath");
|
|
@@ -1187,7 +1226,7 @@ async function runCodex(opts) {
|
|
|
1187
1226
|
}
|
|
1188
1227
|
messageBuffer.clear();
|
|
1189
1228
|
logActiveHandles("cleanup-end");
|
|
1190
|
-
logger.debug("[codex]:
|
|
1229
|
+
logger.debug("[codex]: Final cleanup completed");
|
|
1191
1230
|
}
|
|
1192
1231
|
}
|
|
1193
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.
|
|
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": "
|
|
93
|
-
"@anthropic-ai/sdk": "
|
|
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",
|
|
@@ -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.
|
|
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": "
|
|
114
|
-
"@anthropic-ai/sdk": "
|
|
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",
|
|
@@ -1017,7 +1017,7 @@ class RpcHandlerManager {
|
|
|
1017
1017
|
}
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
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-
|
|
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))));
|
|
1021
1021
|
function projectPath() {
|
|
1022
1022
|
const path$1 = path.resolve(__dirname$1, "..");
|
|
1023
1023
|
return path$1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-coder",
|
|
3
|
-
"version": "0.10.
|
|
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": "
|
|
72
|
-
"@anthropic-ai/sdk": "
|
|
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",
|