happy-coder 0.11.2 → 0.12.0-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.
- package/dist/codex/happyMcpStdioBridge.cjs +2 -3
- package/dist/codex/happyMcpStdioBridge.mjs +2 -3
- package/dist/{index-DmJ8WyYo.cjs → index-BCQGaH21.cjs} +141 -45
- package/dist/{index-sSOy3f2x.mjs → index-BWDbDDaX.mjs} +143 -47
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +6 -15
- package/dist/lib.d.mts +6 -15
- package/dist/lib.mjs +1 -1
- package/dist/{runCodex-DQPZNHzH.cjs → runCodex-BwpHgdkt.cjs} +3 -3
- package/dist/{runCodex-BnjA1TX6.mjs → runCodex-IZ3YFeAC.mjs} +3 -3
- package/dist/{types-Bg43e3vc.cjs → types-C7skJO9Y.cjs} +74 -29
- package/dist/{types-CjceR-4_.mjs → types-DdwJ6K-A.mjs} +73 -28
- package/package.json +15 -15
- package/scripts/claude_local_launcher.cjs +5 -30
- package/scripts/claude_remote_launcher.cjs +4 -1
- package/scripts/claude_version_utils.cjs +378 -0
|
@@ -31,7 +31,7 @@ async function main() {
|
|
|
31
31
|
if (httpClient) return httpClient;
|
|
32
32
|
const client = new index_js.Client(
|
|
33
33
|
{ name: "happy-stdio-bridge", version: "1.0.0" },
|
|
34
|
-
{ capabilities: {
|
|
34
|
+
{ capabilities: {} }
|
|
35
35
|
);
|
|
36
36
|
const transport = new streamableHttp_js.StreamableHTTPClientTransport(new URL(baseUrl));
|
|
37
37
|
await client.connect(transport);
|
|
@@ -40,8 +40,7 @@ async function main() {
|
|
|
40
40
|
}
|
|
41
41
|
const server = new mcp_js.McpServer({
|
|
42
42
|
name: "Happy MCP Bridge",
|
|
43
|
-
version: "1.0.0"
|
|
44
|
-
description: "STDIO bridge forwarding to Happy HTTP MCP"
|
|
43
|
+
version: "1.0.0"
|
|
45
44
|
});
|
|
46
45
|
server.registerTool(
|
|
47
46
|
"change_title",
|
|
@@ -29,7 +29,7 @@ async function main() {
|
|
|
29
29
|
if (httpClient) return httpClient;
|
|
30
30
|
const client = new Client(
|
|
31
31
|
{ name: "happy-stdio-bridge", version: "1.0.0" },
|
|
32
|
-
{ capabilities: {
|
|
32
|
+
{ capabilities: {} }
|
|
33
33
|
);
|
|
34
34
|
const transport = new StreamableHTTPClientTransport(new URL(baseUrl));
|
|
35
35
|
await client.connect(transport);
|
|
@@ -38,8 +38,7 @@ async function main() {
|
|
|
38
38
|
}
|
|
39
39
|
const server = new McpServer({
|
|
40
40
|
name: "Happy MCP Bridge",
|
|
41
|
-
version: "1.0.0"
|
|
42
|
-
description: "STDIO bridge forwarding to Happy HTTP MCP"
|
|
41
|
+
version: "1.0.0"
|
|
43
42
|
});
|
|
44
43
|
server.registerTool(
|
|
45
44
|
"change_title",
|
|
@@ -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-C7skJO9Y.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');
|
|
@@ -242,31 +242,19 @@ const claudeCliPath = node_path.resolve(node_path.join(types.projectPath(), "scr
|
|
|
242
242
|
async function claudeLocal(opts) {
|
|
243
243
|
const projectDir = getProjectPath(opts.path);
|
|
244
244
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
245
|
-
const watcher = fs.watch(projectDir);
|
|
246
|
-
let resolvedSessionId = null;
|
|
247
|
-
const detectedIdsRandomUUID = /* @__PURE__ */ new Set();
|
|
248
|
-
const detectedIdsFileSystem = /* @__PURE__ */ new Set();
|
|
249
|
-
watcher.on("change", (event, filename) => {
|
|
250
|
-
if (typeof filename === "string" && filename.toLowerCase().endsWith(".jsonl")) {
|
|
251
|
-
types.logger.debug("change", event, filename);
|
|
252
|
-
const sessionId = filename.replace(".jsonl", "");
|
|
253
|
-
if (detectedIdsFileSystem.has(sessionId)) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
detectedIdsFileSystem.add(sessionId);
|
|
257
|
-
if (resolvedSessionId) {
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (detectedIdsRandomUUID.has(sessionId)) {
|
|
261
|
-
resolvedSessionId = sessionId;
|
|
262
|
-
opts.onSessionFound(sessionId);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
245
|
let startFrom = opts.sessionId;
|
|
267
246
|
if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) {
|
|
268
247
|
startFrom = null;
|
|
269
248
|
}
|
|
249
|
+
const newSessionId = startFrom ? null : node_crypto.randomUUID();
|
|
250
|
+
const effectiveSessionId = startFrom || newSessionId;
|
|
251
|
+
if (newSessionId) {
|
|
252
|
+
types.logger.debug(`[ClaudeLocal] Generated new session ID: ${newSessionId}`);
|
|
253
|
+
opts.onSessionFound(newSessionId);
|
|
254
|
+
} else {
|
|
255
|
+
types.logger.debug(`[ClaudeLocal] Resuming session: ${startFrom}`);
|
|
256
|
+
opts.onSessionFound(startFrom);
|
|
257
|
+
}
|
|
270
258
|
let thinking = false;
|
|
271
259
|
let stopThinkingTimeout = null;
|
|
272
260
|
const updateThinking = (newThinking) => {
|
|
@@ -284,6 +272,8 @@ async function claudeLocal(opts) {
|
|
|
284
272
|
const args = [];
|
|
285
273
|
if (startFrom) {
|
|
286
274
|
args.push("--resume", startFrom);
|
|
275
|
+
} else {
|
|
276
|
+
args.push("--session-id", newSessionId);
|
|
287
277
|
}
|
|
288
278
|
args.push("--append-system-prompt", systemPrompt);
|
|
289
279
|
if (opts.mcpServers && Object.keys(opts.mcpServers).length > 0) {
|
|
@@ -302,6 +292,8 @@ async function claudeLocal(opts) {
|
|
|
302
292
|
...process.env,
|
|
303
293
|
...opts.claudeEnvVars
|
|
304
294
|
};
|
|
295
|
+
types.logger.debug(`[ClaudeLocal] Spawning launcher: ${claudeCliPath}`);
|
|
296
|
+
types.logger.debug(`[ClaudeLocal] Args: ${JSON.stringify(args)}`);
|
|
305
297
|
const child = node_child_process.spawn("node", [claudeCliPath, ...args], {
|
|
306
298
|
stdio: ["inherit", "inherit", "inherit", "pipe"],
|
|
307
299
|
signal: opts.abort,
|
|
@@ -318,13 +310,6 @@ async function claudeLocal(opts) {
|
|
|
318
310
|
try {
|
|
319
311
|
const message = JSON.parse(line);
|
|
320
312
|
switch (message.type) {
|
|
321
|
-
case "uuid":
|
|
322
|
-
detectedIdsRandomUUID.add(message.value);
|
|
323
|
-
if (!resolvedSessionId && detectedIdsFileSystem.has(message.value)) {
|
|
324
|
-
resolvedSessionId = message.value;
|
|
325
|
-
opts.onSessionFound(message.value);
|
|
326
|
-
}
|
|
327
|
-
break;
|
|
328
313
|
case "fetch-start":
|
|
329
314
|
activeFetches.set(message.id, {
|
|
330
315
|
hostname: message.hostname,
|
|
@@ -378,7 +363,6 @@ async function claudeLocal(opts) {
|
|
|
378
363
|
});
|
|
379
364
|
});
|
|
380
365
|
} finally {
|
|
381
|
-
watcher.close();
|
|
382
366
|
process.stdin.resume();
|
|
383
367
|
if (stopThinkingTimeout) {
|
|
384
368
|
clearTimeout(stopThinkingTimeout);
|
|
@@ -386,7 +370,7 @@ async function claudeLocal(opts) {
|
|
|
386
370
|
}
|
|
387
371
|
updateThinking(false);
|
|
388
372
|
}
|
|
389
|
-
return
|
|
373
|
+
return effectiveSessionId;
|
|
390
374
|
}
|
|
391
375
|
|
|
392
376
|
class Future {
|
|
@@ -504,6 +488,11 @@ function startFileWatcher(file, onFileChange) {
|
|
|
504
488
|
};
|
|
505
489
|
}
|
|
506
490
|
|
|
491
|
+
const INTERNAL_CLAUDE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
492
|
+
"file-history-snapshot",
|
|
493
|
+
"change",
|
|
494
|
+
"queue-operation"
|
|
495
|
+
]);
|
|
507
496
|
async function createSessionScanner(opts) {
|
|
508
497
|
const projectDir = getProjectPath(opts.workingDirectory);
|
|
509
498
|
let finishedSessions = /* @__PURE__ */ new Set();
|
|
@@ -513,26 +502,42 @@ async function createSessionScanner(opts) {
|
|
|
513
502
|
let processedMessageKeys = /* @__PURE__ */ new Set();
|
|
514
503
|
if (opts.sessionId) {
|
|
515
504
|
let messages = await readSessionLog(projectDir, opts.sessionId);
|
|
505
|
+
types.logger.debug(`[SESSION_SCANNER] Marking ${messages.length} existing messages as processed from session ${opts.sessionId}`);
|
|
516
506
|
for (let m of messages) {
|
|
517
507
|
processedMessageKeys.add(messageKey(m));
|
|
518
508
|
}
|
|
509
|
+
currentSessionId = opts.sessionId;
|
|
519
510
|
}
|
|
520
511
|
const sync = new InvalidateSync(async () => {
|
|
521
512
|
let sessions = [];
|
|
522
513
|
for (let p of pendingSessions) {
|
|
523
514
|
sessions.push(p);
|
|
524
515
|
}
|
|
525
|
-
if (currentSessionId) {
|
|
516
|
+
if (currentSessionId && !pendingSessions.has(currentSessionId)) {
|
|
526
517
|
sessions.push(currentSessionId);
|
|
527
518
|
}
|
|
519
|
+
for (let [sessionId] of watchers) {
|
|
520
|
+
if (!sessions.includes(sessionId)) {
|
|
521
|
+
sessions.push(sessionId);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
528
524
|
for (let session of sessions) {
|
|
529
|
-
|
|
525
|
+
const sessionMessages = await readSessionLog(projectDir, session);
|
|
526
|
+
let skipped = 0;
|
|
527
|
+
let sent = 0;
|
|
528
|
+
for (let file of sessionMessages) {
|
|
530
529
|
let key = messageKey(file);
|
|
531
530
|
if (processedMessageKeys.has(key)) {
|
|
531
|
+
skipped++;
|
|
532
532
|
continue;
|
|
533
533
|
}
|
|
534
534
|
processedMessageKeys.add(key);
|
|
535
|
+
types.logger.debug(`[SESSION_SCANNER] Sending new message: type=${file.type}, uuid=${file.type === "summary" ? file.leafUuid : file.uuid}`);
|
|
535
536
|
opts.onMessage(file);
|
|
537
|
+
sent++;
|
|
538
|
+
}
|
|
539
|
+
if (sessionMessages.length > 0) {
|
|
540
|
+
types.logger.debug(`[SESSION_SCANNER] Session ${session}: found=${sessionMessages.length}, skipped=${skipped}, sent=${sent}`);
|
|
536
541
|
}
|
|
537
542
|
}
|
|
538
543
|
for (let p of sessions) {
|
|
@@ -543,6 +548,7 @@ async function createSessionScanner(opts) {
|
|
|
543
548
|
}
|
|
544
549
|
for (let p of sessions) {
|
|
545
550
|
if (!watchers.has(p)) {
|
|
551
|
+
types.logger.debug(`[SESSION_SCANNER] Starting watcher for session: ${p}`);
|
|
546
552
|
watchers.set(p, startFileWatcher(node_path.join(projectDir, `${p}.jsonl`), () => {
|
|
547
553
|
sync.invalidate();
|
|
548
554
|
}));
|
|
@@ -616,9 +622,11 @@ async function readSessionLog(projectDir, sessionId) {
|
|
|
616
622
|
continue;
|
|
617
623
|
}
|
|
618
624
|
let message = JSON.parse(l);
|
|
625
|
+
if (message.type && INTERNAL_CLAUDE_EVENT_TYPES.has(message.type)) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
619
628
|
let parsed = types.RawJSONLinesSchema.safeParse(message);
|
|
620
629
|
if (!parsed.success) {
|
|
621
|
-
types.logger.debugLargeJson(`[SESSION_SCANNER] Failed to parse message`, message);
|
|
622
630
|
continue;
|
|
623
631
|
}
|
|
624
632
|
messages.push(parsed.data);
|
|
@@ -981,10 +989,94 @@ class AbortError extends Error {
|
|
|
981
989
|
}
|
|
982
990
|
}
|
|
983
991
|
|
|
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-
|
|
992
|
+
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-BCQGaH21.cjs', document.baseURI).href)));
|
|
985
993
|
const __dirname$1 = node_path.join(__filename$1, "..");
|
|
994
|
+
function getGlobalClaudeVersion() {
|
|
995
|
+
try {
|
|
996
|
+
const cleanEnv = getCleanEnv();
|
|
997
|
+
const output = node_child_process.execSync("claude --version", {
|
|
998
|
+
encoding: "utf8",
|
|
999
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1000
|
+
cwd: os.homedir(),
|
|
1001
|
+
env: cleanEnv
|
|
1002
|
+
}).trim();
|
|
1003
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
1004
|
+
types.logger.debug(`[Claude SDK] Global claude --version output: ${output}`);
|
|
1005
|
+
return match ? match[1] : null;
|
|
1006
|
+
} catch {
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
function getCleanEnv() {
|
|
1011
|
+
const env = { ...process.env };
|
|
1012
|
+
const cwd = process.cwd();
|
|
1013
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
1014
|
+
const pathKey = process.platform === "win32" ? "Path" : "PATH";
|
|
1015
|
+
const actualPathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") || pathKey;
|
|
1016
|
+
if (env[actualPathKey]) {
|
|
1017
|
+
const cleanPath = env[actualPathKey].split(pathSep).filter((p) => {
|
|
1018
|
+
const normalizedP = p.replace(/\\/g, "/").toLowerCase();
|
|
1019
|
+
const normalizedCwd = cwd.replace(/\\/g, "/").toLowerCase();
|
|
1020
|
+
return !normalizedP.startsWith(normalizedCwd);
|
|
1021
|
+
}).join(pathSep);
|
|
1022
|
+
env[actualPathKey] = cleanPath;
|
|
1023
|
+
types.logger.debug(`[Claude SDK] Cleaned PATH, removed local paths from: ${cwd}`);
|
|
1024
|
+
}
|
|
1025
|
+
return env;
|
|
1026
|
+
}
|
|
1027
|
+
function findGlobalClaudePath() {
|
|
1028
|
+
const homeDir = os.homedir();
|
|
1029
|
+
const cleanEnv = getCleanEnv();
|
|
1030
|
+
try {
|
|
1031
|
+
node_child_process.execSync("claude --version", {
|
|
1032
|
+
encoding: "utf8",
|
|
1033
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1034
|
+
cwd: homeDir,
|
|
1035
|
+
env: cleanEnv
|
|
1036
|
+
});
|
|
1037
|
+
types.logger.debug("[Claude SDK] Global claude command available (checked with clean PATH)");
|
|
1038
|
+
return "claude";
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
if (process.platform !== "win32") {
|
|
1042
|
+
try {
|
|
1043
|
+
const result = node_child_process.execSync("which claude", {
|
|
1044
|
+
encoding: "utf8",
|
|
1045
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1046
|
+
cwd: homeDir,
|
|
1047
|
+
env: cleanEnv
|
|
1048
|
+
}).trim();
|
|
1049
|
+
if (result && fs.existsSync(result)) {
|
|
1050
|
+
types.logger.debug(`[Claude SDK] Found global claude path via which: ${result}`);
|
|
1051
|
+
return result;
|
|
1052
|
+
}
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
986
1058
|
function getDefaultClaudeCodePath() {
|
|
987
|
-
|
|
1059
|
+
const nodeModulesPath = node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
|
|
1060
|
+
if (process.env.HAPPY_CLAUDE_PATH) {
|
|
1061
|
+
types.logger.debug(`[Claude SDK] Using HAPPY_CLAUDE_PATH: ${process.env.HAPPY_CLAUDE_PATH}`);
|
|
1062
|
+
return process.env.HAPPY_CLAUDE_PATH;
|
|
1063
|
+
}
|
|
1064
|
+
if (process.env.HAPPY_USE_BUNDLED_CLAUDE === "1") {
|
|
1065
|
+
types.logger.debug(`[Claude SDK] Forced bundled version: ${nodeModulesPath}`);
|
|
1066
|
+
return nodeModulesPath;
|
|
1067
|
+
}
|
|
1068
|
+
const globalPath = findGlobalClaudePath();
|
|
1069
|
+
if (!globalPath) {
|
|
1070
|
+
types.logger.debug(`[Claude SDK] No global claude found, using bundled: ${nodeModulesPath}`);
|
|
1071
|
+
return nodeModulesPath;
|
|
1072
|
+
}
|
|
1073
|
+
const globalVersion = getGlobalClaudeVersion();
|
|
1074
|
+
types.logger.debug(`[Claude SDK] Global version: ${globalVersion || "unknown"}`);
|
|
1075
|
+
if (!globalVersion) {
|
|
1076
|
+
types.logger.debug(`[Claude SDK] Cannot compare versions, using global: ${globalPath}`);
|
|
1077
|
+
return globalPath;
|
|
1078
|
+
}
|
|
1079
|
+
return globalPath;
|
|
988
1080
|
}
|
|
989
1081
|
function logDebug(message) {
|
|
990
1082
|
if (process.env.DEBUG) {
|
|
@@ -1249,17 +1341,22 @@ function query(config) {
|
|
|
1249
1341
|
} else {
|
|
1250
1342
|
args.push("--input-format", "stream-json");
|
|
1251
1343
|
}
|
|
1252
|
-
|
|
1344
|
+
const isJsFile = pathToClaudeCodeExecutable.endsWith(".js") || pathToClaudeCodeExecutable.endsWith(".cjs");
|
|
1345
|
+
const isCommandOnly = pathToClaudeCodeExecutable === "claude";
|
|
1346
|
+
if (!isCommandOnly && !fs.existsSync(pathToClaudeCodeExecutable)) {
|
|
1253
1347
|
throw new ReferenceError(`Claude Code executable not found at ${pathToClaudeCodeExecutable}. Is options.pathToClaudeCodeExecutable set?`);
|
|
1254
1348
|
}
|
|
1255
|
-
|
|
1256
|
-
const
|
|
1349
|
+
const spawnCommand = isJsFile ? executable : pathToClaudeCodeExecutable;
|
|
1350
|
+
const spawnArgs = isJsFile ? [...executableArgs, pathToClaudeCodeExecutable, ...args] : args;
|
|
1351
|
+
const spawnEnv = isCommandOnly ? getCleanEnv() : process.env;
|
|
1352
|
+
logDebug(`Spawning Claude Code process: ${spawnCommand} ${spawnArgs.join(" ")} (using ${isCommandOnly ? "clean" : "normal"} env)`);
|
|
1353
|
+
const child = node_child_process.spawn(spawnCommand, spawnArgs, {
|
|
1257
1354
|
cwd,
|
|
1258
1355
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1259
1356
|
signal: config.options?.abort,
|
|
1260
|
-
env:
|
|
1261
|
-
|
|
1262
|
-
|
|
1357
|
+
env: spawnEnv,
|
|
1358
|
+
// Use shell on Windows for global binaries and command-only mode
|
|
1359
|
+
shell: !isJsFile && process.platform === "win32"
|
|
1263
1360
|
});
|
|
1264
1361
|
let childStdin = null;
|
|
1265
1362
|
if (typeof prompt === "string") {
|
|
@@ -4605,8 +4702,7 @@ async function startHappyServer(client) {
|
|
|
4605
4702
|
};
|
|
4606
4703
|
const mcp = new mcp_js.McpServer({
|
|
4607
4704
|
name: "Happy MCP",
|
|
4608
|
-
version: "1.0.0"
|
|
4609
|
-
description: "Happy CLI MCP server with chat session management tools"
|
|
4705
|
+
version: "1.0.0"
|
|
4610
4706
|
});
|
|
4611
4707
|
mcp.registerTool("change_title", {
|
|
4612
4708
|
description: "Change the title of the current chat session",
|
|
@@ -5816,7 +5912,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
5816
5912
|
return;
|
|
5817
5913
|
} else if (subcommand === "codex") {
|
|
5818
5914
|
try {
|
|
5819
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
5915
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BwpHgdkt.cjs'); });
|
|
5820
5916
|
let startedBy = void 0;
|
|
5821
5917
|
for (let i = 1; i < args.length; i++) {
|
|
5822
5918
|
if (args[i] === "--started-by") {
|