happy-imou-cloud 2.1.4 → 2.1.6
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/bin/happy-cloud.mjs +38 -38
- package/dist/{BaseReasoningProcessor-DgdsExMH.cjs → BaseReasoningProcessor-C3oDrA4i.cjs} +17 -4
- package/dist/{BaseReasoningProcessor-lTsZVuAU.mjs → BaseReasoningProcessor-CRXr7Axk.mjs} +16 -3
- package/dist/{ProviderSelectionHandler-CGTnB7ba.mjs → ProviderSelectionHandler-C3kHFqeq.mjs} +2 -2
- package/dist/{ProviderSelectionHandler-Bavm9TDG.cjs → ProviderSelectionHandler-Drg2Pp1-.cjs} +2 -2
- package/dist/{api-B6ESNpGB.cjs → api-CvtU4DI-.cjs} +2 -2
- package/dist/{api-l8X03rs-.mjs → api-DF9A136-.mjs} +3 -3
- package/dist/{command-DPLKOzMr.cjs → command-UZr1nodh.cjs} +3 -3
- package/dist/{command-BVCkEMtp.mjs → command-hO52qTzQ.mjs} +3 -3
- package/dist/{index-D72RMo5Z.mjs → index-B5e-MA1d.mjs} +268 -32
- package/dist/{index-D1BP-fEm.cjs → index-DA-K28E3.cjs} +264 -24
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +36 -36
- package/dist/lib.d.mts +36 -36
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-CyFjFOlN.mjs → persistence-BsWBBi7E.mjs} +1 -1
- package/dist/{persistence-EDmI-c8T.cjs → persistence-CKgPuZRR.cjs} +1 -1
- package/dist/{registerKillSessionHandler-71xCO8e_.cjs → registerKillSessionHandler-3ytO-yBI.cjs} +72 -79
- package/dist/{registerKillSessionHandler-DAVhkb-l.mjs → registerKillSessionHandler-DmG1p8l7.mjs} +73 -78
- package/dist/{runClaude-BRhQLKjh.mjs → runClaude-MF34EsCp.mjs} +103 -40
- package/dist/{runClaude-DjnTGJGC.cjs → runClaude-bAlUdUGw.cjs} +106 -43
- package/dist/{runCodex-DUs_jBE-.mjs → runCodex-B31D_imZ.mjs} +10 -8
- package/dist/{runCodex-BHq7Rnq7.cjs → runCodex-CiIbJ1wa.cjs} +12 -10
- package/dist/{runGemini-pmvBZ6qU.mjs → runGemini-BJ7PxLyl.mjs} +5 -5
- package/dist/{runGemini-hkZeOnA_.cjs → runGemini-BZJR84o-.cjs} +7 -7
- package/package.json +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
|
|
2
|
-
import { l as logger, e as encodeBase64, c as configuration, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders,
|
|
3
|
-
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-
|
|
2
|
+
import { l as logger, e as encodeBase64, c as configuration, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders, p as decodeBase64, q as HAPPY_CLOUD_DAEMON_PORT, r as packageJson, A as ApiClient, t as HeadTailPreviewBuffer, u as getLatestDaemonLog } from './api-DF9A136-.mjs';
|
|
3
|
+
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-BsWBBi7E.mjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import fs, { writeFile as writeFile$1, rename, unlink as unlink$1 } from 'fs/promises';
|
|
6
|
-
import os, { homedir } from 'os';
|
|
6
|
+
import os$1, { homedir } from 'os';
|
|
7
7
|
import * as tmp from 'tmp';
|
|
8
8
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
9
9
|
import tweetnacl from 'tweetnacl';
|
|
@@ -11,7 +11,7 @@ import axios from 'axios';
|
|
|
11
11
|
import qrcode from 'qrcode-terminal';
|
|
12
12
|
import { writeFile, unlink, readdir, readFile, mkdir } from 'node:fs/promises';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
|
-
import os
|
|
14
|
+
import os, { tmpdir, homedir as homedir$1 } from 'node:os';
|
|
15
15
|
import path, { join, resolve as resolve$1, isAbsolute, delimiter, normalize, dirname as dirname$1 } from 'node:path';
|
|
16
16
|
import open from 'open';
|
|
17
17
|
import React, { useState } from 'react';
|
|
@@ -2499,6 +2499,128 @@ async function runDaemonHealthCheck({
|
|
|
2499
2499
|
await writeHeartbeat();
|
|
2500
2500
|
}
|
|
2501
2501
|
|
|
2502
|
+
async function closeProviderSession(session, opts = {}) {
|
|
2503
|
+
let firstError;
|
|
2504
|
+
const captureError = (error) => {
|
|
2505
|
+
if (firstError === void 0) {
|
|
2506
|
+
firstError = error;
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
if (opts.archiveOnClose || opts.archiveReason) {
|
|
2510
|
+
try {
|
|
2511
|
+
session.updateMetadata((currentMetadata) => {
|
|
2512
|
+
const { archiveReason: _existingArchiveReason, ...metadataWithoutArchiveReason } = currentMetadata;
|
|
2513
|
+
return {
|
|
2514
|
+
...metadataWithoutArchiveReason,
|
|
2515
|
+
lifecycleState: "archived",
|
|
2516
|
+
lifecycleStateSince: Date.now(),
|
|
2517
|
+
archivedBy: opts.archivedBy ?? "cli",
|
|
2518
|
+
...opts.archiveReason ? { archiveReason: opts.archiveReason } : {}
|
|
2519
|
+
};
|
|
2520
|
+
});
|
|
2521
|
+
} catch (error) {
|
|
2522
|
+
captureError(error);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
try {
|
|
2526
|
+
session.sendSessionDeath();
|
|
2527
|
+
} catch (error) {
|
|
2528
|
+
captureError(error);
|
|
2529
|
+
}
|
|
2530
|
+
try {
|
|
2531
|
+
await session.flush();
|
|
2532
|
+
} catch (error) {
|
|
2533
|
+
captureError(error);
|
|
2534
|
+
}
|
|
2535
|
+
try {
|
|
2536
|
+
await session.close();
|
|
2537
|
+
} catch (error) {
|
|
2538
|
+
captureError(error);
|
|
2539
|
+
}
|
|
2540
|
+
if (firstError !== void 0) {
|
|
2541
|
+
throw firstError;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
function createSessionMetadata(opts) {
|
|
2546
|
+
const state = {
|
|
2547
|
+
controlledByUser: false
|
|
2548
|
+
};
|
|
2549
|
+
const metadataPath = opts.path ?? process.cwd();
|
|
2550
|
+
const metadataHostPid = opts.hostPid === void 0 ? process.pid : opts.hostPid;
|
|
2551
|
+
const metadata = {
|
|
2552
|
+
path: metadataPath,
|
|
2553
|
+
host: os.hostname(),
|
|
2554
|
+
version: packageJson.version,
|
|
2555
|
+
os: os.platform(),
|
|
2556
|
+
machineId: opts.machineId,
|
|
2557
|
+
homeDir: os.homedir(),
|
|
2558
|
+
happyHomeDir: configuration.happyCloudHomeDir,
|
|
2559
|
+
happyLibDir: projectPath(),
|
|
2560
|
+
happyToolsDir: resolve$1(projectPath(), "tools", "unpacked"),
|
|
2561
|
+
startedFromDaemon: opts.startedBy === "daemon",
|
|
2562
|
+
startedBy: opts.startedBy || "terminal",
|
|
2563
|
+
lifecycleState: "running",
|
|
2564
|
+
lifecycleStateSince: Date.now(),
|
|
2565
|
+
flavor: opts.flavor,
|
|
2566
|
+
...metadataHostPid == null ? {} : { hostPid: metadataHostPid }
|
|
2567
|
+
};
|
|
2568
|
+
return { state, metadata };
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
async function archiveManagedSessionById(opts) {
|
|
2572
|
+
try {
|
|
2573
|
+
const sessionClient = opts.api.sessionSyncClient({ id: opts.sessionId });
|
|
2574
|
+
await closeProviderSession(sessionClient, {
|
|
2575
|
+
archiveReason: opts.archiveReason,
|
|
2576
|
+
archivedBy: "daemon"
|
|
2577
|
+
});
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
logger.debug(
|
|
2580
|
+
`[DAEMON RUN] Failed to archive managed session ${opts.sessionId}: ${opts.archiveReason}`,
|
|
2581
|
+
error
|
|
2582
|
+
);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
async function precreateDaemonManagedSession(opts) {
|
|
2586
|
+
const { state, metadata } = createSessionMetadata({
|
|
2587
|
+
flavor: opts.flavor,
|
|
2588
|
+
machineId: opts.machineId,
|
|
2589
|
+
startedBy: "daemon",
|
|
2590
|
+
path: opts.directory,
|
|
2591
|
+
hostPid: null
|
|
2592
|
+
});
|
|
2593
|
+
try {
|
|
2594
|
+
return await opts.api.getOrCreateSession({
|
|
2595
|
+
tag: opts.sessionTag,
|
|
2596
|
+
metadata,
|
|
2597
|
+
state
|
|
2598
|
+
});
|
|
2599
|
+
} catch (error) {
|
|
2600
|
+
logger.debug(`[DAEMON RUN] Failed to precreate ${opts.flavor} session for tag ${opts.sessionTag}`, error);
|
|
2601
|
+
return null;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
async function archivePrecreatedManagedSession(opts) {
|
|
2605
|
+
await archiveManagedSessionById({
|
|
2606
|
+
api: opts.api,
|
|
2607
|
+
sessionId: opts.session.id,
|
|
2608
|
+
archiveReason: opts.archiveReason
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
async function archiveDetachedManagedSessionIfNeeded(opts) {
|
|
2612
|
+
const { trackedSession } = opts;
|
|
2613
|
+
if (trackedSession.startedBy !== "daemon" || !trackedSession.happySessionId || trackedSession.happySessionMetadataFromLocalWebhook) {
|
|
2614
|
+
return false;
|
|
2615
|
+
}
|
|
2616
|
+
await archiveManagedSessionById({
|
|
2617
|
+
api: opts.api,
|
|
2618
|
+
sessionId: trackedSession.happySessionId,
|
|
2619
|
+
archiveReason: opts.archiveReason
|
|
2620
|
+
});
|
|
2621
|
+
return true;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2502
2624
|
const NON_SESSION_PROCESS_TYPES$1 = /* @__PURE__ */ new Set([
|
|
2503
2625
|
"current",
|
|
2504
2626
|
"daemon",
|
|
@@ -2821,6 +2943,19 @@ async function recoverTrackedSessionsFromLocalRegistry({
|
|
|
2821
2943
|
return { recoveredCount, removedStaleCount };
|
|
2822
2944
|
}
|
|
2823
2945
|
|
|
2946
|
+
const HAPPY_MANAGED_SESSION_TAG_ENV = "HAPPY_MANAGED_SESSION_TAG";
|
|
2947
|
+
function readManagedSessionTag(env = process.env) {
|
|
2948
|
+
const raw = env[HAPPY_MANAGED_SESSION_TAG_ENV];
|
|
2949
|
+
if (typeof raw !== "string") {
|
|
2950
|
+
return null;
|
|
2951
|
+
}
|
|
2952
|
+
const trimmed = raw.trim();
|
|
2953
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2954
|
+
}
|
|
2955
|
+
function resolveManagedSessionTag(env = process.env) {
|
|
2956
|
+
return readManagedSessionTag(env) ?? randomUUID();
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2824
2959
|
const DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS = 45e3;
|
|
2825
2960
|
function resolveSessionWebhookTimeoutMs() {
|
|
2826
2961
|
const parsed = Number.parseInt(process.env.HAPPY_DAEMON_SESSION_WEBHOOK_TIMEOUT || "", 10);
|
|
@@ -2830,10 +2965,10 @@ function resolveSessionWebhookTimeoutMs() {
|
|
|
2830
2965
|
return DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS;
|
|
2831
2966
|
}
|
|
2832
2967
|
const initialMachineMetadata = {
|
|
2833
|
-
host: os.hostname(),
|
|
2834
|
-
platform: os.platform(),
|
|
2968
|
+
host: os$1.hostname(),
|
|
2969
|
+
platform: os$1.platform(),
|
|
2835
2970
|
happyCliVersion: packageJson.version,
|
|
2836
|
-
homeDir: os.homedir(),
|
|
2971
|
+
homeDir: os$1.homedir(),
|
|
2837
2972
|
happyHomeDir: configuration.happyCloudHomeDir,
|
|
2838
2973
|
happyLibDir: projectPath()
|
|
2839
2974
|
};
|
|
@@ -2917,9 +3052,21 @@ async function startDaemon() {
|
|
|
2917
3052
|
}
|
|
2918
3053
|
const { credentials, machineId } = await authAndSetupMachineIfNeeded();
|
|
2919
3054
|
logger.debug("[DAEMON RUN] Auth and machine setup complete");
|
|
3055
|
+
let api = null;
|
|
2920
3056
|
const pidToTrackedSession = /* @__PURE__ */ new Map();
|
|
2921
3057
|
const pidToAwaiter = /* @__PURE__ */ new Map();
|
|
2922
3058
|
const getCurrentChildren = () => Array.from(pidToTrackedSession.values());
|
|
3059
|
+
const removeTrackedSession = (pid, archiveReason) => {
|
|
3060
|
+
const trackedSession = pidToTrackedSession.get(pid);
|
|
3061
|
+
if (trackedSession && api) {
|
|
3062
|
+
void archiveDetachedManagedSessionIfNeeded({
|
|
3063
|
+
api,
|
|
3064
|
+
trackedSession,
|
|
3065
|
+
archiveReason
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
pidToTrackedSession.delete(pid);
|
|
3069
|
+
};
|
|
2923
3070
|
const onHappySessionWebhook = (sessionId, sessionMetadata) => {
|
|
2924
3071
|
logger.debugLargeJson(`[DAEMON RUN] Session reported`, sessionMetadata);
|
|
2925
3072
|
const pid = sessionMetadata.hostPid;
|
|
@@ -2956,8 +3103,9 @@ async function startDaemon() {
|
|
|
2956
3103
|
};
|
|
2957
3104
|
const spawnSession = async (options) => {
|
|
2958
3105
|
logger.debugLargeJson("[DAEMON RUN] Spawning session", options);
|
|
2959
|
-
const { directory, sessionId, machineId:
|
|
3106
|
+
const { directory, sessionId, machineId: _requestedMachineId, approvedNewDirectoryCreation = true } = options;
|
|
2960
3107
|
let directoryCreated = false;
|
|
3108
|
+
let precreatedCodexSession = null;
|
|
2961
3109
|
try {
|
|
2962
3110
|
await fs.access(directory);
|
|
2963
3111
|
logger.debug(`[DAEMON RUN] Directory exists: ${directory}`);
|
|
@@ -3031,6 +3179,11 @@ async function startDaemon() {
|
|
|
3031
3179
|
}
|
|
3032
3180
|
let extraEnv = { ...profileEnv, ...authEnv };
|
|
3033
3181
|
logger.debug(`[DAEMON RUN] Final environment variable keys (before expansion) (${Object.keys(extraEnv).length}): ${Object.keys(extraEnv).join(", ")}`);
|
|
3182
|
+
const managedSessionTag = options.agent === "codex" ? randomUUID() : null;
|
|
3183
|
+
if (managedSessionTag) {
|
|
3184
|
+
extraEnv[HAPPY_MANAGED_SESSION_TAG_ENV] = managedSessionTag;
|
|
3185
|
+
logger.debug(`[DAEMON RUN] Reserved managed Codex session tag ${managedSessionTag}`);
|
|
3186
|
+
}
|
|
3034
3187
|
extraEnv = expandEnvironmentVariables(extraEnv, process.env);
|
|
3035
3188
|
logger.debug(`[DAEMON RUN] After variable expansion: ${Object.keys(extraEnv).join(", ")}`);
|
|
3036
3189
|
const potentialAuthVars = ["ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "OPENAI_API_KEY", "CODEX_HOME", "AZURE_OPENAI_API_KEY", "TOGETHER_API_KEY"];
|
|
@@ -3069,6 +3222,18 @@ async function startDaemon() {
|
|
|
3069
3222
|
const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${buildDaemonSpawnArgs(agent).join(" ")}`;
|
|
3070
3223
|
const windowName = `happy-${Date.now()}-${agent}`;
|
|
3071
3224
|
const tmuxEnv = buildDaemonChildEnv(process.env, extraEnv);
|
|
3225
|
+
if (managedSessionTag && api) {
|
|
3226
|
+
precreatedCodexSession = await precreateDaemonManagedSession({
|
|
3227
|
+
api,
|
|
3228
|
+
sessionTag: managedSessionTag,
|
|
3229
|
+
machineId,
|
|
3230
|
+
directory,
|
|
3231
|
+
flavor: "codex"
|
|
3232
|
+
});
|
|
3233
|
+
if (precreatedCodexSession) {
|
|
3234
|
+
logger.debug(`[DAEMON RUN] Precreated Codex session ${precreatedCodexSession.id} before tmux spawn`);
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3072
3237
|
const tmuxResult = await tmux.spawnInTmux([fullCommand], {
|
|
3073
3238
|
sessionName: tmuxSessionName,
|
|
3074
3239
|
windowName,
|
|
@@ -3081,6 +3246,7 @@ async function startDaemon() {
|
|
|
3081
3246
|
}
|
|
3082
3247
|
const trackedSession = {
|
|
3083
3248
|
startedBy: "daemon",
|
|
3249
|
+
happySessionId: precreatedCodexSession?.id,
|
|
3084
3250
|
pid: tmuxResult.pid,
|
|
3085
3251
|
// Real PID from tmux -P flag
|
|
3086
3252
|
tmuxSessionId: tmuxResult.sessionId,
|
|
@@ -3088,6 +3254,13 @@ async function startDaemon() {
|
|
|
3088
3254
|
message: directoryCreated ? `The path '${directory}' did not exist. We created a new folder and spawned a new session in tmux session '${tmuxSessionName}'. Use 'tmux attach -t ${tmuxSessionName}' to view the session.` : `Spawned new session in tmux session '${tmuxSessionName}'. Use 'tmux attach -t ${tmuxSessionName}' to view the session.`
|
|
3089
3255
|
};
|
|
3090
3256
|
pidToTrackedSession.set(tmuxResult.pid, trackedSession);
|
|
3257
|
+
if (precreatedCodexSession) {
|
|
3258
|
+
logger.debug(`[DAEMON RUN] Returning precreated Codex session ${precreatedCodexSession.id} without waiting for webhook`);
|
|
3259
|
+
return {
|
|
3260
|
+
type: "success",
|
|
3261
|
+
sessionId: precreatedCodexSession.id
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3091
3264
|
logger.debug(`[DAEMON RUN] Waiting for session webhook for PID ${tmuxResult.pid} (tmux)`);
|
|
3092
3265
|
return new Promise((resolve) => {
|
|
3093
3266
|
const timeout = setTimeout(() => {
|
|
@@ -3108,6 +3281,14 @@ async function startDaemon() {
|
|
|
3108
3281
|
});
|
|
3109
3282
|
});
|
|
3110
3283
|
} else {
|
|
3284
|
+
if (precreatedCodexSession && api) {
|
|
3285
|
+
await archivePrecreatedManagedSession({
|
|
3286
|
+
api,
|
|
3287
|
+
session: precreatedCodexSession,
|
|
3288
|
+
archiveReason: `tmux spawn failed before Codex session ${precreatedCodexSession.id} attached`
|
|
3289
|
+
});
|
|
3290
|
+
precreatedCodexSession = null;
|
|
3291
|
+
}
|
|
3111
3292
|
logger.debug(`[DAEMON RUN] Failed to spawn in tmux: ${tmuxResult.error}, falling back to regular spawning`);
|
|
3112
3293
|
useTmux = false;
|
|
3113
3294
|
}
|
|
@@ -3127,6 +3308,18 @@ async function startDaemon() {
|
|
|
3127
3308
|
errorMessage: spawnError.errorMessage
|
|
3128
3309
|
};
|
|
3129
3310
|
}
|
|
3311
|
+
if (managedSessionTag && api) {
|
|
3312
|
+
precreatedCodexSession = await precreateDaemonManagedSession({
|
|
3313
|
+
api,
|
|
3314
|
+
sessionTag: managedSessionTag,
|
|
3315
|
+
machineId,
|
|
3316
|
+
directory,
|
|
3317
|
+
flavor: "codex"
|
|
3318
|
+
});
|
|
3319
|
+
if (precreatedCodexSession) {
|
|
3320
|
+
logger.debug(`[DAEMON RUN] Precreated Codex session ${precreatedCodexSession.id} before process spawn`);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3130
3323
|
const happyProcess = spawnHappyCLI(args, {
|
|
3131
3324
|
cwd: directory,
|
|
3132
3325
|
detached: true,
|
|
@@ -3145,6 +3338,14 @@ async function startDaemon() {
|
|
|
3145
3338
|
}
|
|
3146
3339
|
if (!happyProcess.pid) {
|
|
3147
3340
|
logger.debug("[DAEMON RUN] Failed to spawn process - no PID returned");
|
|
3341
|
+
if (precreatedCodexSession && api) {
|
|
3342
|
+
await archivePrecreatedManagedSession({
|
|
3343
|
+
api,
|
|
3344
|
+
session: precreatedCodexSession,
|
|
3345
|
+
archiveReason: `spawn produced no PID for precreated Codex session ${precreatedCodexSession.id}`
|
|
3346
|
+
});
|
|
3347
|
+
precreatedCodexSession = null;
|
|
3348
|
+
}
|
|
3148
3349
|
const spawnError = createSpawnSessionError(
|
|
3149
3350
|
SPAWN_SESSION_ERROR_CODES.SPAWN_NO_PID,
|
|
3150
3351
|
"Failed to spawn Happy process - no PID returned"
|
|
@@ -3157,6 +3358,7 @@ async function startDaemon() {
|
|
|
3157
3358
|
logger.debug(`[DAEMON RUN] Spawned process with PID ${happyProcess.pid}`);
|
|
3158
3359
|
const trackedSession = {
|
|
3159
3360
|
startedBy: "daemon",
|
|
3361
|
+
happySessionId: precreatedCodexSession?.id,
|
|
3160
3362
|
pid: happyProcess.pid,
|
|
3161
3363
|
childProcess: happyProcess,
|
|
3162
3364
|
directoryCreated,
|
|
@@ -3175,6 +3377,13 @@ async function startDaemon() {
|
|
|
3175
3377
|
onChildExited(happyProcess.pid);
|
|
3176
3378
|
}
|
|
3177
3379
|
});
|
|
3380
|
+
if (precreatedCodexSession) {
|
|
3381
|
+
logger.debug(`[DAEMON RUN] Returning precreated Codex session ${precreatedCodexSession.id} without waiting for webhook`);
|
|
3382
|
+
return {
|
|
3383
|
+
type: "success",
|
|
3384
|
+
sessionId: precreatedCodexSession.id
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3178
3387
|
logger.debug(`[DAEMON RUN] Waiting for session webhook for PID ${happyProcess.pid}`);
|
|
3179
3388
|
return new Promise((resolve) => {
|
|
3180
3389
|
const timeout = setTimeout(() => {
|
|
@@ -3202,6 +3411,13 @@ async function startDaemon() {
|
|
|
3202
3411
|
} catch (error) {
|
|
3203
3412
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3204
3413
|
logger.debug("[DAEMON RUN] Failed to spawn session:", error);
|
|
3414
|
+
if (precreatedCodexSession && api) {
|
|
3415
|
+
await archivePrecreatedManagedSession({
|
|
3416
|
+
api,
|
|
3417
|
+
session: precreatedCodexSession,
|
|
3418
|
+
archiveReason: `spawn failed before precreated Codex session ${precreatedCodexSession.id} attached: ${errorMessage}`
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3205
3421
|
const spawnError = createSpawnSessionError(
|
|
3206
3422
|
SPAWN_SESSION_ERROR_CODES.SPAWN_FAILED,
|
|
3207
3423
|
`Failed to spawn session: ${errorMessage}`
|
|
@@ -3231,7 +3447,7 @@ async function startDaemon() {
|
|
|
3231
3447
|
logger.debug(`[DAEMON RUN] Failed to kill external session PID ${pid}:`, error);
|
|
3232
3448
|
}
|
|
3233
3449
|
}
|
|
3234
|
-
|
|
3450
|
+
removeTrackedSession(pid, `session ${sessionId} was stopped from daemon control before local attachment completed`);
|
|
3235
3451
|
logger.debug(`[DAEMON RUN] Removed session ${sessionId} from tracking`);
|
|
3236
3452
|
return true;
|
|
3237
3453
|
}
|
|
@@ -3253,7 +3469,7 @@ async function startDaemon() {
|
|
|
3253
3469
|
errorMessage: spawnError.errorMessage
|
|
3254
3470
|
});
|
|
3255
3471
|
}
|
|
3256
|
-
|
|
3472
|
+
removeTrackedSession(pid, `process ${pid} exited before the managed session fully attached`);
|
|
3257
3473
|
};
|
|
3258
3474
|
const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
|
|
3259
3475
|
getChildren: getCurrentChildren,
|
|
@@ -3291,22 +3507,23 @@ async function startDaemon() {
|
|
|
3291
3507
|
httpPort: controlPort,
|
|
3292
3508
|
startedAt: Date.now()
|
|
3293
3509
|
};
|
|
3294
|
-
|
|
3510
|
+
api = await ApiClient.create(credentials);
|
|
3511
|
+
const activeApi = api;
|
|
3295
3512
|
let userScopedObserver = null;
|
|
3296
3513
|
if (credentials.signing) {
|
|
3297
3514
|
try {
|
|
3298
|
-
userScopedObserver =
|
|
3515
|
+
userScopedObserver = activeApi.userScopedObserverClient?.() ?? null;
|
|
3299
3516
|
} catch (error) {
|
|
3300
3517
|
logger.debug("[DAEMON RUN] Failed to start user-scoped observer, continuing without it", error);
|
|
3301
3518
|
}
|
|
3302
3519
|
}
|
|
3303
|
-
const machine = await
|
|
3520
|
+
const machine = await activeApi.getOrCreateMachine({
|
|
3304
3521
|
machineId,
|
|
3305
3522
|
metadata: initialMachineMetadata,
|
|
3306
3523
|
daemonState: initialDaemonState
|
|
3307
3524
|
});
|
|
3308
3525
|
logger.debug(`[DAEMON RUN] Machine registered: ${machine.id}`);
|
|
3309
|
-
const apiMachine =
|
|
3526
|
+
const apiMachine = activeApi.machineSyncClient(machine);
|
|
3310
3527
|
apiMachine.setRPCHandlers({
|
|
3311
3528
|
spawnSession,
|
|
3312
3529
|
stopSession,
|
|
@@ -3314,7 +3531,7 @@ async function startDaemon() {
|
|
|
3314
3531
|
});
|
|
3315
3532
|
const runRemoteSessionIndexRecovery = async (label) => {
|
|
3316
3533
|
const recoveryResult = await recoverTrackedSessionsFromRemoteIndex({
|
|
3317
|
-
api,
|
|
3534
|
+
api: activeApi,
|
|
3318
3535
|
machineId,
|
|
3319
3536
|
trackedSessionPids: pidToTrackedSession.keys(),
|
|
3320
3537
|
trackSession: (pid, trackedSession) => {
|
|
@@ -3370,7 +3587,7 @@ async function startDaemon() {
|
|
|
3370
3587
|
await runDaemonHealthCheck({
|
|
3371
3588
|
trackedSessionPids: pidToTrackedSession.keys(),
|
|
3372
3589
|
removeTrackedSession: (pid) => {
|
|
3373
|
-
|
|
3590
|
+
removeTrackedSession(pid, `health check removed stale tracked PID ${pid} before local attachment completed`);
|
|
3374
3591
|
},
|
|
3375
3592
|
currentCliVersion: configuration.currentCliVersion,
|
|
3376
3593
|
onDaemonOutdated: async () => {
|
|
@@ -3513,10 +3730,10 @@ async function install$1() {
|
|
|
3513
3730
|
<true/>
|
|
3514
3731
|
|
|
3515
3732
|
<key>StandardErrorPath</key>
|
|
3516
|
-
<string>${os.homedir()}/.happy/daemon.err</string>
|
|
3733
|
+
<string>${os$1.homedir()}/.happy/daemon.err</string>
|
|
3517
3734
|
|
|
3518
3735
|
<key>StandardOutPath</key>
|
|
3519
|
-
<string>${os.homedir()}/.happy/daemon.log</string>
|
|
3736
|
+
<string>${os$1.homedir()}/.happy/daemon.log</string>
|
|
3520
3737
|
|
|
3521
3738
|
<key>WorkingDirectory</key>
|
|
3522
3739
|
<string>/tmp</string>
|
|
@@ -3649,7 +3866,7 @@ async function handleAuthLogin(args) {
|
|
|
3649
3866
|
if (existingCreds && settings?.machineId) {
|
|
3650
3867
|
console.log(chalk.green("\u2713 Already authenticated"));
|
|
3651
3868
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
3652
|
-
console.log(chalk.gray(` Host: ${os
|
|
3869
|
+
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
3653
3870
|
console.log(chalk.gray(` Use 'hicloud auth login --force' to re-authenticate`));
|
|
3654
3871
|
return;
|
|
3655
3872
|
} else if (existingCreds && !settings?.machineId) {
|
|
@@ -3728,7 +3945,7 @@ async function handleAuthStatus() {
|
|
|
3728
3945
|
if (settings?.machineId) {
|
|
3729
3946
|
console.log(chalk.green("\u2713 Machine registered"));
|
|
3730
3947
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
3731
|
-
console.log(chalk.gray(` Host: ${os
|
|
3948
|
+
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
3732
3949
|
} else {
|
|
3733
3950
|
console.log(chalk.yellow("\u26A0\uFE0F Machine not registered"));
|
|
3734
3951
|
console.log(chalk.gray(' Run "hicloud auth login --force" to fix this'));
|
|
@@ -7925,7 +8142,7 @@ function firstExistingPath(candidates) {
|
|
|
7925
8142
|
}
|
|
7926
8143
|
function resolveCodexExecutable() {
|
|
7927
8144
|
if (process.platform === "win32") {
|
|
7928
|
-
const appData = process.env.APPDATA || path.join(os
|
|
8145
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
7929
8146
|
const npmGlobalBin = path.join(appData, "npm");
|
|
7930
8147
|
const resolved = firstExistingPath([
|
|
7931
8148
|
path.join(npmGlobalBin, "codex.cmd"),
|
|
@@ -8365,6 +8582,7 @@ class Query {
|
|
|
8365
8582
|
}
|
|
8366
8583
|
pendingControlResponses = /* @__PURE__ */ new Map();
|
|
8367
8584
|
cancelControllers = /* @__PURE__ */ new Map();
|
|
8585
|
+
controlTasks = /* @__PURE__ */ new Set();
|
|
8368
8586
|
sdkMessages;
|
|
8369
8587
|
inputStream = new Stream();
|
|
8370
8588
|
canCallTool;
|
|
@@ -8413,7 +8631,12 @@ class Query {
|
|
|
8413
8631
|
}
|
|
8414
8632
|
continue;
|
|
8415
8633
|
} else if (message.type === "control_request") {
|
|
8416
|
-
|
|
8634
|
+
const controlTask = this.handleControlRequest(message).catch((error) => {
|
|
8635
|
+
logger.debug("[ClaudeSDK] Failed to handle control request:", error);
|
|
8636
|
+
}).finally(() => {
|
|
8637
|
+
this.controlTasks.delete(controlTask);
|
|
8638
|
+
});
|
|
8639
|
+
this.controlTasks.add(controlTask);
|
|
8417
8640
|
continue;
|
|
8418
8641
|
} else if (message.type === "control_cancel_request") {
|
|
8419
8642
|
this.handleControlCancelRequest(message);
|
|
@@ -8429,8 +8652,11 @@ class Query {
|
|
|
8429
8652
|
} catch (error) {
|
|
8430
8653
|
this.inputStream.error(error);
|
|
8431
8654
|
} finally {
|
|
8432
|
-
this.inputStream.done();
|
|
8433
8655
|
this.cleanupControllers();
|
|
8656
|
+
if (this.controlTasks.size > 0) {
|
|
8657
|
+
await Promise.allSettled(Array.from(this.controlTasks));
|
|
8658
|
+
}
|
|
8659
|
+
this.inputStream.done();
|
|
8434
8660
|
rl.close();
|
|
8435
8661
|
}
|
|
8436
8662
|
}
|
|
@@ -8495,7 +8721,7 @@ class Query {
|
|
|
8495
8721
|
response
|
|
8496
8722
|
}
|
|
8497
8723
|
};
|
|
8498
|
-
this.
|
|
8724
|
+
this.writeControlResponse(controlResponse);
|
|
8499
8725
|
} catch (error) {
|
|
8500
8726
|
const controlErrorResponse = {
|
|
8501
8727
|
type: "control_response",
|
|
@@ -8505,11 +8731,21 @@ class Query {
|
|
|
8505
8731
|
error: error instanceof Error ? error.message : String(error)
|
|
8506
8732
|
}
|
|
8507
8733
|
};
|
|
8508
|
-
this.
|
|
8734
|
+
this.writeControlResponse(controlErrorResponse);
|
|
8509
8735
|
} finally {
|
|
8510
8736
|
this.cancelControllers.delete(request.request_id);
|
|
8511
8737
|
}
|
|
8512
8738
|
}
|
|
8739
|
+
writeControlResponse(response) {
|
|
8740
|
+
if (!this.childStdin || this.childStdin.destroyed || !this.childStdin.writable) {
|
|
8741
|
+
return;
|
|
8742
|
+
}
|
|
8743
|
+
try {
|
|
8744
|
+
this.childStdin.write(JSON.stringify(response) + "\n");
|
|
8745
|
+
} catch (error) {
|
|
8746
|
+
logger.debug("[ClaudeSDK] Failed to write control response:", error);
|
|
8747
|
+
}
|
|
8748
|
+
}
|
|
8513
8749
|
/**
|
|
8514
8750
|
* Handle control cancel requests
|
|
8515
8751
|
* Replicates the exact logic from the SDK's handleControlCancelRequest method
|
|
@@ -9494,11 +9730,11 @@ var launch = /*#__PURE__*/Object.freeze({
|
|
|
9494
9730
|
|
|
9495
9731
|
const unifiedProviderExecutors = {
|
|
9496
9732
|
claude: async (opts) => {
|
|
9497
|
-
const { runClaude } = await import('./runClaude-
|
|
9733
|
+
const { runClaude } = await import('./runClaude-MF34EsCp.mjs');
|
|
9498
9734
|
await runClaude(opts.credentials, opts.claudeOptions ?? {});
|
|
9499
9735
|
},
|
|
9500
9736
|
codex: async (opts) => {
|
|
9501
|
-
const { runCodex } = await import('./runCodex-
|
|
9737
|
+
const { runCodex } = await import('./runCodex-B31D_imZ.mjs');
|
|
9502
9738
|
await runCodex({
|
|
9503
9739
|
credentials: opts.credentials,
|
|
9504
9740
|
startedBy: opts.startedBy,
|
|
@@ -9507,7 +9743,7 @@ const unifiedProviderExecutors = {
|
|
|
9507
9743
|
});
|
|
9508
9744
|
},
|
|
9509
9745
|
gemini: async (opts) => {
|
|
9510
|
-
const { runGemini } = await import('./runGemini-
|
|
9746
|
+
const { runGemini } = await import('./runGemini-BJ7PxLyl.mjs');
|
|
9511
9747
|
await runGemini({
|
|
9512
9748
|
credentials: opts.credentials,
|
|
9513
9749
|
startedBy: opts.startedBy
|
|
@@ -9583,7 +9819,7 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
9583
9819
|
return;
|
|
9584
9820
|
} else if (subcommand === "runtime") {
|
|
9585
9821
|
if (args[1] === "providers") {
|
|
9586
|
-
const { renderRuntimeProviders } = await import('./command-
|
|
9822
|
+
const { renderRuntimeProviders } = await import('./command-hO52qTzQ.mjs');
|
|
9587
9823
|
console.log(renderRuntimeProviders());
|
|
9588
9824
|
return;
|
|
9589
9825
|
}
|
|
@@ -9761,8 +9997,8 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
9761
9997
|
const projectId = args[3];
|
|
9762
9998
|
try {
|
|
9763
9999
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
9764
|
-
const { readCredentials: readCredentials2 } = await import('./persistence-
|
|
9765
|
-
const { ApiClient: ApiClient2 } = await import('./api-
|
|
10000
|
+
const { readCredentials: readCredentials2 } = await import('./persistence-BsWBBi7E.mjs');
|
|
10001
|
+
const { ApiClient: ApiClient2 } = await import('./api-DF9A136-.mjs').then(function (n) { return n.v; });
|
|
9766
10002
|
let userEmail = void 0;
|
|
9767
10003
|
try {
|
|
9768
10004
|
const credentials = await readCredentials2();
|
|
@@ -10178,4 +10414,4 @@ ${chalk.bold("Examples:")}
|
|
|
10178
10414
|
}
|
|
10179
10415
|
}
|
|
10180
10416
|
|
|
10181
|
-
export { AbortError as A, ExitCodeError as E, Future as F, GEMINI_MODEL_ENV as G, PushableAsyncIterable as P, RuntimeShell as R,
|
|
10417
|
+
export { AbortError as A, startCaffeinate as B, ExitCodeError as E, Future as F, GEMINI_MODEL_ENV as G, PushableAsyncIterable as P, RuntimeShell as R, createSessionMetadata as a, closeProviderSession as b, createDefaultRuntimeShell as c, createGeminiBackend as d, stopCaffeinate as e, formatDisplayMessage as f, getInitialGeminiModel as g, createCodexBackend as h, readManagedSessionTag as i, resolveManagedSessionTag as j, initialMachineMetadata as k, resolveCanonicalToolNameV2 as l, getProjectPath as m, claudeLocal as n, trimIdent as o, publishSessionRegistration as p, createClaudeBackend as q, readGeminiLocalConfig as r, saveGeminiModelToConfig as s, truncateDisplayMessage as t, claudeCheckSession as u, validateCodexAcpSpawn as v, projectPath as w, mapToClaudeMode as x, query as y, getEnvironmentInfo as z };
|