happy-imou-cloud 2.0.0 → 2.0.2
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/{BaseReasoningProcessor-BRCQXCZY.cjs → BaseReasoningProcessor-B6tJ_eL5.cjs} +96 -9
- package/dist/{BaseReasoningProcessor-BKLRCKTU.mjs → BaseReasoningProcessor-D8VhEbs2.mjs} +95 -10
- package/dist/{api-D7OK-mML.cjs → api-D2Njw9Im.cjs} +124 -6
- package/dist/{api-BGXYX0yH.mjs → api-MYhAGPLn.mjs} +122 -7
- package/dist/{command-G85giEAF.cjs → command-CVldr51S.cjs} +3 -3
- package/dist/{command-CnLtKtP-.mjs → command-nmK6O-ab.mjs} +3 -3
- package/dist/{index-C7Y0R-MI.mjs → index-B97L7qLD.mjs} +689 -229
- package/dist/{index-B_wlQBy2.cjs → index-Bg-YziG2.cjs} +691 -229
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +7 -0
- package/dist/lib.d.mts +7 -0
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-DHgf1CTG.cjs → persistence-D_2GkJAO.cjs} +28 -6
- package/dist/{persistence-BA_unuca.mjs → persistence-Dkm7rm8k.mjs} +29 -7
- package/dist/{registerKillSessionHandler-C2-yHm1V.mjs → registerKillSessionHandler-5GbrO0FM.mjs} +6 -4
- package/dist/{registerKillSessionHandler-CLREXN11.cjs → registerKillSessionHandler-BAXmJQRt.cjs} +6 -4
- package/dist/{runClaude-CwAitpX-.cjs → runClaude-B-GNEkKg.cjs} +237 -45
- package/dist/{runClaude-uNC5Eym4.mjs → runClaude-Cii3R2Fv.mjs} +238 -46
- package/dist/{runCodex-B-05E-YZ.mjs → runCodex-C--ZwAhl.mjs} +636 -819
- package/dist/{runCodex-Cm0VTqw_.cjs → runCodex-CPHyGwj9.cjs} +639 -819
- package/dist/{runGemini-_biXvQAH.mjs → runGemini-CQp7Nuzn.mjs} +20 -16
- package/dist/{runGemini-CLWjwDYS.cjs → runGemini-DaDz1bzQ.cjs} +20 -16
- package/package.json +14 -15
- package/scripts/env-wrapper.cjs +11 -11
- package/scripts/setup-dev.cjs +4 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var api = require('./api-
|
|
5
|
-
var persistence = require('./persistence-
|
|
4
|
+
var api = require('./api-D2Njw9Im.cjs');
|
|
5
|
+
var persistence = require('./persistence-D_2GkJAO.cjs');
|
|
6
6
|
var z = require('zod');
|
|
7
7
|
var fs$2 = require('fs/promises');
|
|
8
8
|
var os$1 = require('os');
|
|
@@ -70,7 +70,7 @@ async function openBrowser(url) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
73
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-Bg-YziG2.cjs', document.baseURI).href)));
|
|
74
74
|
const QRCode = require$1("qrcode-terminal/vendor/QRCode");
|
|
75
75
|
const QRErrorCorrectLevel = require$1("qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel");
|
|
76
76
|
const pendingTempFiles = /* @__PURE__ */ new Set();
|
|
@@ -241,6 +241,64 @@ const AuthSelector = ({ onSelect, onCancel }) => {
|
|
|
241
241
|
})), /* @__PURE__ */ React.createElement(ink.Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
|
|
242
242
|
};
|
|
243
243
|
|
|
244
|
+
async function ensureSigningCredentials(credentials, opts = {}) {
|
|
245
|
+
if (credentials.signing) {
|
|
246
|
+
return credentials;
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const response = await axios.post(`${api.configuration.serverUrl}/v1/auth/refresh`, {}, {
|
|
250
|
+
headers: api.buildAuthenticatedHeaders({
|
|
251
|
+
credentials,
|
|
252
|
+
method: "POST",
|
|
253
|
+
url: `${api.configuration.serverUrl}/v1/auth/refresh`,
|
|
254
|
+
body: {},
|
|
255
|
+
headers: {
|
|
256
|
+
"Content-Type": "application/json"
|
|
257
|
+
},
|
|
258
|
+
signRequest: false
|
|
259
|
+
})
|
|
260
|
+
});
|
|
261
|
+
if (!response.data?.success || !response.data?.token || !response.data?.signing) {
|
|
262
|
+
api.logger.debug("[AUTH] Signing bootstrap returned incomplete payload", response.data);
|
|
263
|
+
throw new api.SigningBootstrapRequiredError(api.SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
264
|
+
}
|
|
265
|
+
const upgradedCredentials = {
|
|
266
|
+
...credentials,
|
|
267
|
+
token: response.data.token,
|
|
268
|
+
signing: response.data.signing
|
|
269
|
+
};
|
|
270
|
+
if (upgradedCredentials.encryption.type === "legacy") {
|
|
271
|
+
await persistence.writeCredentialsLegacy({
|
|
272
|
+
secret: upgradedCredentials.encryption.secret,
|
|
273
|
+
token: upgradedCredentials.token,
|
|
274
|
+
signing: upgradedCredentials.signing
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
await persistence.writeCredentialsDataKey({
|
|
278
|
+
publicKey: upgradedCredentials.encryption.publicKey,
|
|
279
|
+
machineKey: upgradedCredentials.encryption.machineKey,
|
|
280
|
+
token: upgradedCredentials.token,
|
|
281
|
+
signing: upgradedCredentials.signing
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
api.logger.debug("[AUTH] Signing credentials bootstrapped successfully");
|
|
285
|
+
return upgradedCredentials;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
|
288
|
+
api.logger.debug("[AUTH] Signing bootstrap rejected with 401");
|
|
289
|
+
throw new api.SigningBootstrapRequiredError(api.SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
290
|
+
}
|
|
291
|
+
if (error instanceof api.SigningBootstrapRequiredError) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
api.logger.debug("[AUTH] Failed to bootstrap signing credentials", error);
|
|
295
|
+
if (opts.failFast) {
|
|
296
|
+
throw new api.SigningBootstrapRequiredError(api.SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
297
|
+
}
|
|
298
|
+
return credentials;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
244
302
|
function isNonInteractive() {
|
|
245
303
|
return !process.stdin.isTTY || !process.stdout.isTTY;
|
|
246
304
|
}
|
|
@@ -542,7 +600,7 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
542
600
|
} else {
|
|
543
601
|
api.logger.debug("[AUTH] Using existing credentials");
|
|
544
602
|
}
|
|
545
|
-
credentials = await ensureSigningCredentials(credentials);
|
|
603
|
+
credentials = await ensureSigningCredentials(credentials, { failFast: newAuth });
|
|
546
604
|
const currentSettings = await persistence.readSettings();
|
|
547
605
|
const settings = newAuth || !currentSettings.machineId ? await persistence.updateSettings(async (s) => ({
|
|
548
606
|
...s,
|
|
@@ -551,53 +609,6 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
551
609
|
api.logger.debug(`[AUTH] Machine ID: ${settings.machineId}`);
|
|
552
610
|
return { credentials, machineId: settings.machineId };
|
|
553
611
|
}
|
|
554
|
-
async function ensureSigningCredentials(credentials) {
|
|
555
|
-
if (credentials.signing) {
|
|
556
|
-
return credentials;
|
|
557
|
-
}
|
|
558
|
-
try {
|
|
559
|
-
const response = await axios.post(`${api.configuration.serverUrl}/v1/auth/refresh`, {}, {
|
|
560
|
-
headers: api.buildAuthenticatedHeaders({
|
|
561
|
-
credentials,
|
|
562
|
-
method: "POST",
|
|
563
|
-
url: `${api.configuration.serverUrl}/v1/auth/refresh`,
|
|
564
|
-
body: {},
|
|
565
|
-
headers: {
|
|
566
|
-
"Content-Type": "application/json"
|
|
567
|
-
},
|
|
568
|
-
signRequest: false
|
|
569
|
-
})
|
|
570
|
-
});
|
|
571
|
-
if (!response.data?.success || !response.data?.token || !response.data?.signing) {
|
|
572
|
-
api.logger.debug("[AUTH] Signing bootstrap returned incomplete payload");
|
|
573
|
-
return credentials;
|
|
574
|
-
}
|
|
575
|
-
const upgradedCredentials = {
|
|
576
|
-
...credentials,
|
|
577
|
-
token: response.data.token,
|
|
578
|
-
signing: response.data.signing
|
|
579
|
-
};
|
|
580
|
-
if (upgradedCredentials.encryption.type === "legacy") {
|
|
581
|
-
await persistence.writeCredentialsLegacy({
|
|
582
|
-
secret: upgradedCredentials.encryption.secret,
|
|
583
|
-
token: upgradedCredentials.token,
|
|
584
|
-
signing: upgradedCredentials.signing
|
|
585
|
-
});
|
|
586
|
-
} else {
|
|
587
|
-
await persistence.writeCredentialsDataKey({
|
|
588
|
-
publicKey: upgradedCredentials.encryption.publicKey,
|
|
589
|
-
machineKey: upgradedCredentials.encryption.machineKey,
|
|
590
|
-
token: upgradedCredentials.token,
|
|
591
|
-
signing: upgradedCredentials.signing
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
api.logger.debug("[AUTH] Signing credentials bootstrapped successfully");
|
|
595
|
-
return upgradedCredentials;
|
|
596
|
-
} catch (error) {
|
|
597
|
-
api.logger.debug("[AUTH] Failed to bootstrap signing credentials", error);
|
|
598
|
-
return credentials;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
612
|
|
|
602
613
|
let caffeinateProcess = null;
|
|
603
614
|
function startCaffeinate() {
|
|
@@ -682,7 +693,7 @@ function setupCleanupHandlers() {
|
|
|
682
693
|
});
|
|
683
694
|
}
|
|
684
695
|
|
|
685
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
696
|
+
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-Bg-YziG2.cjs', document.baseURI).href))));
|
|
686
697
|
function projectPath() {
|
|
687
698
|
const path = path$1.resolve(__dirname$1, "..");
|
|
688
699
|
return path;
|
|
@@ -980,6 +991,32 @@ async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
|
980
991
|
return false;
|
|
981
992
|
}
|
|
982
993
|
}
|
|
994
|
+
async function isDaemonControlServerResponsive(timeoutMs = 1e3) {
|
|
995
|
+
const state = await persistence.readDaemonState();
|
|
996
|
+
if (!state?.httpPort) {
|
|
997
|
+
api.logger.debug("[DAEMON CONTROL] No daemon state or control port found for readiness check");
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
process.kill(state.pid, 0);
|
|
1002
|
+
} catch {
|
|
1003
|
+
api.logger.debug("[DAEMON CONTROL] Daemon PID not running during readiness check, cleaning up state");
|
|
1004
|
+
await cleanupDaemonState();
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
try {
|
|
1008
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
1009
|
+
method: "POST",
|
|
1010
|
+
headers: { "Content-Type": "application/json" },
|
|
1011
|
+
body: JSON.stringify({}),
|
|
1012
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1013
|
+
});
|
|
1014
|
+
return response.ok;
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
api.logger.debug("[DAEMON CONTROL] Daemon control server not ready yet", error);
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
983
1020
|
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
984
1021
|
api.logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
985
1022
|
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
@@ -997,7 +1034,10 @@ async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
|
997
1034
|
const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
|
|
998
1035
|
const currentCliVersion = packageJson.version;
|
|
999
1036
|
api.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
1000
|
-
|
|
1037
|
+
if (currentCliVersion !== state.startedWithCliVersion) {
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
return await isDaemonControlServerResponsive();
|
|
1001
1041
|
} catch (error) {
|
|
1002
1042
|
api.logger.debug("[DAEMON CONTROL] Error checking daemon version", error);
|
|
1003
1043
|
return false;
|
|
@@ -1214,7 +1254,7 @@ ${typeLabels[type] || type}:`));
|
|
|
1214
1254
|
}
|
|
1215
1255
|
if (filter === "all" && allProcesses.length > 1) {
|
|
1216
1256
|
console.log(chalk.bold("\n\u{1F4A1} Process Management"));
|
|
1217
|
-
console.log(chalk.gray("To clean up runaway processes:
|
|
1257
|
+
console.log(chalk.gray("To clean up runaway processes: hicloud doctor clean"));
|
|
1218
1258
|
}
|
|
1219
1259
|
} catch (error) {
|
|
1220
1260
|
console.log(chalk.red("\u274C Error checking daemon status"));
|
|
@@ -1396,7 +1436,8 @@ function startDaemonControlServer({
|
|
|
1396
1436
|
schema: {
|
|
1397
1437
|
body: z.z.object({
|
|
1398
1438
|
directory: z.z.string(),
|
|
1399
|
-
sessionId: z.z.string().optional()
|
|
1439
|
+
sessionId: z.z.string().optional(),
|
|
1440
|
+
agent: z.z.enum(["claude", "codex", "gemini"]).optional()
|
|
1400
1441
|
}),
|
|
1401
1442
|
response: {
|
|
1402
1443
|
200: z.z.object({
|
|
@@ -1417,9 +1458,9 @@ function startDaemonControlServer({
|
|
|
1417
1458
|
}
|
|
1418
1459
|
}
|
|
1419
1460
|
}, async (request, reply) => {
|
|
1420
|
-
const { directory, sessionId } = request.body;
|
|
1421
|
-
api.logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}`);
|
|
1422
|
-
const result = await spawnSession({ directory, sessionId });
|
|
1461
|
+
const { directory, sessionId, agent } = request.body;
|
|
1462
|
+
api.logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}, agent=${agent || "claude"}`);
|
|
1463
|
+
const result = await spawnSession({ directory, sessionId, agent });
|
|
1423
1464
|
switch (result.type) {
|
|
1424
1465
|
case "success":
|
|
1425
1466
|
if (!result.sessionId) {
|
|
@@ -2934,13 +2975,13 @@ async function handleAuthCommand(args) {
|
|
|
2934
2975
|
}
|
|
2935
2976
|
function showAuthHelp() {
|
|
2936
2977
|
console.log(`
|
|
2937
|
-
${chalk.bold("
|
|
2978
|
+
${chalk.bold("hicloud auth")} - Authentication management
|
|
2938
2979
|
|
|
2939
2980
|
${chalk.bold("Usage:")}
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2981
|
+
hicloud auth login [--force] Authenticate with Happy
|
|
2982
|
+
hicloud auth logout [--yes] Remove authentication and machine data
|
|
2983
|
+
hicloud auth status Show authentication status
|
|
2984
|
+
hicloud auth help Show this help message
|
|
2944
2985
|
|
|
2945
2986
|
${chalk.bold("Options:")}
|
|
2946
2987
|
--force Clear credentials, machine ID, and stop daemon before re-auth
|
|
@@ -2980,7 +3021,7 @@ async function handleAuthLogin(args) {
|
|
|
2980
3021
|
console.log(chalk.green("\u2713 Already authenticated"));
|
|
2981
3022
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
2982
3023
|
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
2983
|
-
console.log(chalk.gray(` Use '
|
|
3024
|
+
console.log(chalk.gray(` Use 'hicloud auth login --force' to re-authenticate`));
|
|
2984
3025
|
return;
|
|
2985
3026
|
} else if (existingCreds && !settings?.machineId) {
|
|
2986
3027
|
console.log(chalk.yellow("\u26A0\uFE0F Credentials exist but machine ID is missing"));
|
|
@@ -3011,7 +3052,7 @@ async function handleAuthLogout(args = []) {
|
|
|
3011
3052
|
let confirmed = skipConfirm;
|
|
3012
3053
|
if (!confirmed) {
|
|
3013
3054
|
if (isNonInteractive) {
|
|
3014
|
-
throw new Error('Non-interactive terminal detected. Use "
|
|
3055
|
+
throw new Error('Non-interactive terminal detected. Use "hicloud auth logout --yes" to confirm.');
|
|
3015
3056
|
}
|
|
3016
3057
|
const rl = node_readline.createInterface({
|
|
3017
3058
|
input: process.stdin,
|
|
@@ -3034,7 +3075,7 @@ async function handleAuthLogout(args = []) {
|
|
|
3034
3075
|
fs.rmSync(happyDir, { recursive: true, force: true });
|
|
3035
3076
|
}
|
|
3036
3077
|
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
3037
|
-
console.log(chalk.gray(' Run "
|
|
3078
|
+
console.log(chalk.gray(' Run "hicloud auth login" to authenticate again'));
|
|
3038
3079
|
} catch (error) {
|
|
3039
3080
|
throw new Error(`Failed to logout: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3040
3081
|
}
|
|
@@ -3048,20 +3089,20 @@ async function handleAuthStatus() {
|
|
|
3048
3089
|
console.log(chalk.bold("\nAuthentication Status\n"));
|
|
3049
3090
|
if (!credentials) {
|
|
3050
3091
|
console.log(chalk.red("\u2717 Not authenticated"));
|
|
3051
|
-
console.log(chalk.gray(' Run "
|
|
3092
|
+
console.log(chalk.gray(' Run "hicloud auth login" to authenticate'));
|
|
3052
3093
|
return;
|
|
3053
3094
|
}
|
|
3054
3095
|
console.log(chalk.green("\u2713 Authenticated"));
|
|
3055
3096
|
const tokenPreview = credentials.token.substring(0, 30) + "...";
|
|
3056
3097
|
console.log(chalk.gray(` Token: ${tokenPreview}`));
|
|
3057
|
-
console.log(chalk.gray(` Request signing: ${credentials
|
|
3098
|
+
console.log(chalk.gray(` Request signing: ${describeSigningStatus(credentials)}`));
|
|
3058
3099
|
if (settings?.machineId) {
|
|
3059
3100
|
console.log(chalk.green("\u2713 Machine registered"));
|
|
3060
3101
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
3061
3102
|
console.log(chalk.gray(` Host: ${os.hostname()}`));
|
|
3062
3103
|
} else {
|
|
3063
3104
|
console.log(chalk.yellow("\u26A0\uFE0F Machine not registered"));
|
|
3064
|
-
console.log(chalk.gray(' Run "
|
|
3105
|
+
console.log(chalk.gray(' Run "hicloud auth login --force" to fix this'));
|
|
3065
3106
|
}
|
|
3066
3107
|
console.log(chalk.gray(`
|
|
3067
3108
|
Data directory: ${api.configuration.happyCloudHomeDir}`));
|
|
@@ -3076,6 +3117,12 @@ async function handleAuthStatus() {
|
|
|
3076
3117
|
console.log(chalk.gray("\u2717 Daemon not running"));
|
|
3077
3118
|
}
|
|
3078
3119
|
}
|
|
3120
|
+
function describeSigningStatus(credentials) {
|
|
3121
|
+
if (credentials.signing) {
|
|
3122
|
+
return "ready";
|
|
3123
|
+
}
|
|
3124
|
+
return "not initialized (server write requests may still fail until signing bootstrap succeeds)";
|
|
3125
|
+
}
|
|
3079
3126
|
|
|
3080
3127
|
const CLIENT_ID$2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
3081
3128
|
const AUTH_BASE_URL = "https://auth.openai.com";
|
|
@@ -3592,14 +3639,14 @@ async function handleConnectCommand(args) {
|
|
|
3592
3639
|
}
|
|
3593
3640
|
function showConnectHelp() {
|
|
3594
3641
|
console.log(`
|
|
3595
|
-
${chalk.bold("
|
|
3642
|
+
${chalk.bold("hicloud connect")} - Connect AI vendor API keys to Happy cloud
|
|
3596
3643
|
|
|
3597
3644
|
${chalk.bold("Usage:")}
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3645
|
+
hicloud connect codex Store your Codex API key in Happy cloud
|
|
3646
|
+
hicloud connect claude Store your Anthropic API key in Happy cloud
|
|
3647
|
+
hicloud connect gemini Store your Gemini API key in Happy cloud
|
|
3648
|
+
hicloud connect status Show connection status for all vendors
|
|
3649
|
+
hicloud connect help Show this help message
|
|
3603
3650
|
|
|
3604
3651
|
${chalk.bold("Description:")}
|
|
3605
3652
|
The connect command allows you to securely store your AI vendor API keys
|
|
@@ -3607,13 +3654,13 @@ ${chalk.bold("Description:")}
|
|
|
3607
3654
|
without exposing your API keys locally.
|
|
3608
3655
|
|
|
3609
3656
|
${chalk.bold("Examples:")}
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3657
|
+
hicloud connect codex
|
|
3658
|
+
hicloud connect claude
|
|
3659
|
+
hicloud connect gemini
|
|
3660
|
+
hicloud connect status
|
|
3614
3661
|
|
|
3615
3662
|
${chalk.bold("Notes:")}
|
|
3616
|
-
\u2022 You must be authenticated with Happy first (run '
|
|
3663
|
+
\u2022 You must be authenticated with Happy first (run 'hicloud auth login')
|
|
3617
3664
|
\u2022 API keys are encrypted and stored securely in Happy cloud
|
|
3618
3665
|
\u2022 You can manage your stored keys at https://happycloudcode.imou.com
|
|
3619
3666
|
`);
|
|
@@ -3625,7 +3672,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
3625
3672
|
const credentials = await persistence.readCredentials();
|
|
3626
3673
|
if (!credentials) {
|
|
3627
3674
|
console.log(chalk.yellow("\u26A0\uFE0F Not authenticated with Happy"));
|
|
3628
|
-
console.log(chalk.gray(' Please run "
|
|
3675
|
+
console.log(chalk.gray(' Please run "hicloud auth login" first'));
|
|
3629
3676
|
process.exit(1);
|
|
3630
3677
|
}
|
|
3631
3678
|
const api$1 = await api.ApiClient.create(credentials);
|
|
@@ -3657,7 +3704,7 @@ async function handleConnectStatus() {
|
|
|
3657
3704
|
const credentials = await persistence.readCredentials();
|
|
3658
3705
|
if (!credentials) {
|
|
3659
3706
|
console.log(chalk.yellow("\u26A0\uFE0F Not authenticated with Happy"));
|
|
3660
|
-
console.log(chalk.gray(' Please run "
|
|
3707
|
+
console.log(chalk.gray(' Please run "hicloud auth login" first'));
|
|
3661
3708
|
process.exit(1);
|
|
3662
3709
|
}
|
|
3663
3710
|
const api$1 = await api.ApiClient.create(credentials);
|
|
@@ -3692,8 +3739,8 @@ async function handleConnectStatus() {
|
|
|
3692
3739
|
}
|
|
3693
3740
|
}
|
|
3694
3741
|
console.log("");
|
|
3695
|
-
console.log(chalk.gray("To connect a vendor, run:
|
|
3696
|
-
console.log(chalk.gray("Example:
|
|
3742
|
+
console.log(chalk.gray("To connect a vendor, run: hicloud connect <vendor>"));
|
|
3743
|
+
console.log(chalk.gray("Example: hicloud connect gemini"));
|
|
3697
3744
|
console.log("");
|
|
3698
3745
|
}
|
|
3699
3746
|
function updateLocalGeminiCredentials(tokens) {
|
|
@@ -3718,19 +3765,17 @@ function updateLocalGeminiCredentials(tokens) {
|
|
|
3718
3765
|
}
|
|
3719
3766
|
}
|
|
3720
3767
|
|
|
3721
|
-
function getProjectPath(workingDirectory) {
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
const projectId = resolved.replace(/[\\\/\.: _]/g, "-");
|
|
3727
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
3768
|
+
function getProjectPath(workingDirectory, claudeConfigDirOverride) {
|
|
3769
|
+
const projectId = path.resolve(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
3770
|
+
const claudeConfigDirRaw = process.env.CLAUDE_CONFIG_DIR ?? "";
|
|
3771
|
+
const claudeConfigDirTrimmed = claudeConfigDirRaw.trim();
|
|
3772
|
+
const claudeConfigDir = claudeConfigDirTrimmed ? claudeConfigDirTrimmed : path.join(os.homedir(), ".claude");
|
|
3728
3773
|
return path.join(claudeConfigDir, "projects", projectId);
|
|
3729
3774
|
}
|
|
3730
3775
|
|
|
3731
|
-
function claudeCheckSession(sessionId, path$1) {
|
|
3776
|
+
function claudeCheckSession(sessionId, path$1, transcriptPath) {
|
|
3732
3777
|
const projectDir = getProjectPath(path$1);
|
|
3733
|
-
const sessionFile = path.join(projectDir, `${sessionId}.jsonl`);
|
|
3778
|
+
const sessionFile = transcriptPath ?? path.join(projectDir, `${sessionId}.jsonl`);
|
|
3734
3779
|
const sessionExists = fs.existsSync(sessionFile);
|
|
3735
3780
|
if (!sessionExists) {
|
|
3736
3781
|
api.logger.debug(`[claudeCheckSession] Path ${sessionFile} does not exist`);
|
|
@@ -4078,6 +4123,77 @@ class AgentRegistry {
|
|
|
4078
4123
|
}
|
|
4079
4124
|
const agentRegistry = new AgentRegistry();
|
|
4080
4125
|
|
|
4126
|
+
const PREFERRED_MESSAGE_FIELDS = ["stderr", "error", "message", "detail", "stdout", "text", "reason"];
|
|
4127
|
+
function safeSerializeDisplayValue(value) {
|
|
4128
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
4129
|
+
try {
|
|
4130
|
+
const serialized = JSON.stringify(value, (_key, currentValue) => {
|
|
4131
|
+
if (typeof currentValue === "bigint") {
|
|
4132
|
+
return currentValue.toString();
|
|
4133
|
+
}
|
|
4134
|
+
if (currentValue instanceof Error) {
|
|
4135
|
+
return {
|
|
4136
|
+
name: currentValue.name,
|
|
4137
|
+
message: currentValue.message,
|
|
4138
|
+
stack: currentValue.stack
|
|
4139
|
+
};
|
|
4140
|
+
}
|
|
4141
|
+
if (typeof currentValue === "object" && currentValue !== null) {
|
|
4142
|
+
if (seen.has(currentValue)) {
|
|
4143
|
+
return "[Circular]";
|
|
4144
|
+
}
|
|
4145
|
+
seen.add(currentValue);
|
|
4146
|
+
}
|
|
4147
|
+
return currentValue;
|
|
4148
|
+
});
|
|
4149
|
+
return serialized ?? String(value);
|
|
4150
|
+
} catch {
|
|
4151
|
+
return String(value);
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
function formatDisplayMessage(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
4155
|
+
if (typeof value === "string") {
|
|
4156
|
+
return value;
|
|
4157
|
+
}
|
|
4158
|
+
if (value instanceof Error) {
|
|
4159
|
+
return value.message || String(value);
|
|
4160
|
+
}
|
|
4161
|
+
if (value === null || value === void 0) {
|
|
4162
|
+
return "";
|
|
4163
|
+
}
|
|
4164
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
4165
|
+
return value.toString("utf8");
|
|
4166
|
+
}
|
|
4167
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
4168
|
+
return String(value);
|
|
4169
|
+
}
|
|
4170
|
+
if (typeof value === "object") {
|
|
4171
|
+
if (seen.has(value)) {
|
|
4172
|
+
return "[Circular]";
|
|
4173
|
+
}
|
|
4174
|
+
seen.add(value);
|
|
4175
|
+
const record = value;
|
|
4176
|
+
for (const field of PREFERRED_MESSAGE_FIELDS) {
|
|
4177
|
+
if (!(field in record)) {
|
|
4178
|
+
continue;
|
|
4179
|
+
}
|
|
4180
|
+
const formattedField = formatDisplayMessage(record[field], seen).trim();
|
|
4181
|
+
if (formattedField.length > 0) {
|
|
4182
|
+
return formattedField;
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
return safeSerializeDisplayValue(value);
|
|
4186
|
+
}
|
|
4187
|
+
return String(value);
|
|
4188
|
+
}
|
|
4189
|
+
function truncateDisplayMessage(value, maxLength) {
|
|
4190
|
+
const formatted = formatDisplayMessage(value);
|
|
4191
|
+
if (formatted.length <= maxLength) {
|
|
4192
|
+
return formatted;
|
|
4193
|
+
}
|
|
4194
|
+
return `${formatted.substring(0, maxLength)}...`;
|
|
4195
|
+
}
|
|
4196
|
+
|
|
4081
4197
|
const DEFAULT_TIMEOUTS = {
|
|
4082
4198
|
/** Default initialization timeout: 60 seconds */
|
|
4083
4199
|
init: 6e4,
|
|
@@ -4372,7 +4488,7 @@ class CodexTransport extends DefaultTransport {
|
|
|
4372
4488
|
return 800;
|
|
4373
4489
|
}
|
|
4374
4490
|
}
|
|
4375
|
-
|
|
4491
|
+
new CodexTransport();
|
|
4376
4492
|
|
|
4377
4493
|
class ClaudeTransport extends DefaultTransport {
|
|
4378
4494
|
constructor() {
|
|
@@ -4706,6 +4822,70 @@ const RETRY_CONFIG = {
|
|
|
4706
4822
|
/** Maximum delay between retries in ms */
|
|
4707
4823
|
maxDelayMs: 5e3
|
|
4708
4824
|
};
|
|
4825
|
+
function getSessionUpdates(params) {
|
|
4826
|
+
const notification = params;
|
|
4827
|
+
const updates = Array.isArray(notification.updates) ? notification.updates.filter((update) => Boolean(update && typeof update === "object")) : [];
|
|
4828
|
+
if (updates.length > 0) {
|
|
4829
|
+
return updates;
|
|
4830
|
+
}
|
|
4831
|
+
if (notification.update && typeof notification.update === "object") {
|
|
4832
|
+
return [notification.update];
|
|
4833
|
+
}
|
|
4834
|
+
return [];
|
|
4835
|
+
}
|
|
4836
|
+
function asNonNegativeFiniteNumber(value) {
|
|
4837
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
4838
|
+
}
|
|
4839
|
+
function extractUsageTokens(record) {
|
|
4840
|
+
const used = asNonNegativeFiniteNumber(record.used);
|
|
4841
|
+
const size = asNonNegativeFiniteNumber(record.size);
|
|
4842
|
+
if (used != null || size != null) {
|
|
4843
|
+
const tokens2 = {
|
|
4844
|
+
total: used ?? 0
|
|
4845
|
+
};
|
|
4846
|
+
if (used != null) tokens2.used = used;
|
|
4847
|
+
if (size != null) tokens2.size = size;
|
|
4848
|
+
return tokens2;
|
|
4849
|
+
}
|
|
4850
|
+
const input = asNonNegativeFiniteNumber(record.input_tokens) ?? asNonNegativeFiniteNumber(record.inputTokens) ?? asNonNegativeFiniteNumber(record.prompt_tokens) ?? asNonNegativeFiniteNumber(record.promptTokens);
|
|
4851
|
+
const output = asNonNegativeFiniteNumber(record.output_tokens) ?? asNonNegativeFiniteNumber(record.outputTokens) ?? asNonNegativeFiniteNumber(record.completion_tokens) ?? asNonNegativeFiniteNumber(record.completionTokens);
|
|
4852
|
+
const cacheRead = asNonNegativeFiniteNumber(record.cache_read_input_tokens) ?? asNonNegativeFiniteNumber(record.cacheReadInputTokens) ?? asNonNegativeFiniteNumber(record.cache_read_tokens) ?? asNonNegativeFiniteNumber(record.cacheReadTokens) ?? asNonNegativeFiniteNumber(record.cached_read_tokens) ?? asNonNegativeFiniteNumber(record.cachedReadTokens);
|
|
4853
|
+
const cacheCreation = asNonNegativeFiniteNumber(record.cache_creation_input_tokens) ?? asNonNegativeFiniteNumber(record.cacheCreationInputTokens) ?? asNonNegativeFiniteNumber(record.cache_creation_tokens) ?? asNonNegativeFiniteNumber(record.cacheCreationTokens) ?? asNonNegativeFiniteNumber(record.cached_write_tokens) ?? asNonNegativeFiniteNumber(record.cachedWriteTokens);
|
|
4854
|
+
const thought = asNonNegativeFiniteNumber(record.thought_tokens) ?? asNonNegativeFiniteNumber(record.thoughtTokens);
|
|
4855
|
+
const totalFromPayload = asNonNegativeFiniteNumber(record.total_tokens) ?? asNonNegativeFiniteNumber(record.totalTokens);
|
|
4856
|
+
const anyPresent = totalFromPayload != null || input != null || output != null || cacheRead != null || cacheCreation != null || thought != null;
|
|
4857
|
+
if (!anyPresent) {
|
|
4858
|
+
return null;
|
|
4859
|
+
}
|
|
4860
|
+
const total = totalFromPayload ?? (input ?? 0) + (output ?? 0) + (cacheRead ?? 0) + (cacheCreation ?? 0) + (thought ?? 0);
|
|
4861
|
+
const tokens = { total };
|
|
4862
|
+
if (input != null) tokens.input = input;
|
|
4863
|
+
if (output != null) tokens.output = output;
|
|
4864
|
+
if (cacheRead != null) tokens.cache_read = cacheRead;
|
|
4865
|
+
if (cacheCreation != null) tokens.cache_creation = cacheCreation;
|
|
4866
|
+
if (thought != null) tokens.thought = thought;
|
|
4867
|
+
return tokens;
|
|
4868
|
+
}
|
|
4869
|
+
function isApprovalOption(option) {
|
|
4870
|
+
if (option.optionId === "proceed_once" || option.optionId === "proceed_always" || option.optionId === "cancel") {
|
|
4871
|
+
return true;
|
|
4872
|
+
}
|
|
4873
|
+
const searchable = `${option.name ?? ""} ${option.label ?? ""}`.toLowerCase();
|
|
4874
|
+
return searchable.includes("once") || searchable.includes("always") || searchable.includes("cancel");
|
|
4875
|
+
}
|
|
4876
|
+
function isSelectionPermissionRequest(params) {
|
|
4877
|
+
if (Array.isArray(params.codex_command) && params.codex_command.length > 0) {
|
|
4878
|
+
return false;
|
|
4879
|
+
}
|
|
4880
|
+
if (params.requestedSchema?.properties?.optionId) {
|
|
4881
|
+
return true;
|
|
4882
|
+
}
|
|
4883
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
4884
|
+
if (options.length === 0) {
|
|
4885
|
+
return false;
|
|
4886
|
+
}
|
|
4887
|
+
return !options.every((option) => isApprovalOption(option));
|
|
4888
|
+
}
|
|
4709
4889
|
function nodeToWebStreams(stdin, stdout) {
|
|
4710
4890
|
const writable = new WritableStream({
|
|
4711
4891
|
write(chunk) {
|
|
@@ -4769,7 +4949,7 @@ async function withRetry(operation, options) {
|
|
|
4769
4949
|
try {
|
|
4770
4950
|
return await operation();
|
|
4771
4951
|
} catch (error) {
|
|
4772
|
-
lastError =
|
|
4952
|
+
lastError = normalizeAcpError(error);
|
|
4773
4953
|
if (attempt < options.maxAttempts) {
|
|
4774
4954
|
const delayMs = Math.min(
|
|
4775
4955
|
options.baseDelayMs * Math.pow(2, attempt - 1),
|
|
@@ -4783,6 +4963,49 @@ async function withRetry(operation, options) {
|
|
|
4783
4963
|
}
|
|
4784
4964
|
throw lastError;
|
|
4785
4965
|
}
|
|
4966
|
+
function formatAcpErrorMessage(error) {
|
|
4967
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4968
|
+
return error.message;
|
|
4969
|
+
}
|
|
4970
|
+
if (typeof error === "object" && error !== null) {
|
|
4971
|
+
const record = error;
|
|
4972
|
+
const message = formatDisplayMessage(error).trim();
|
|
4973
|
+
const code = record.code;
|
|
4974
|
+
const status = record.status;
|
|
4975
|
+
const prefix = [
|
|
4976
|
+
code !== void 0 && code !== null ? `[code=${String(code)}]` : "",
|
|
4977
|
+
status !== void 0 && status !== null ? `[status=${String(status)}]` : ""
|
|
4978
|
+
].filter(Boolean).join(" ");
|
|
4979
|
+
if (message.length > 0) {
|
|
4980
|
+
return prefix ? `${prefix} ${message}` : message;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
const fallback = formatDisplayMessage(error).trim();
|
|
4984
|
+
return fallback || "Unknown ACP error";
|
|
4985
|
+
}
|
|
4986
|
+
function normalizeAcpError(error) {
|
|
4987
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4988
|
+
return error;
|
|
4989
|
+
}
|
|
4990
|
+
const normalized = new Error(formatAcpErrorMessage(error));
|
|
4991
|
+
normalized.name = error instanceof Error ? error.name : "Error";
|
|
4992
|
+
if (error && typeof error === "object") {
|
|
4993
|
+
const { message: _message, ...rest } = error;
|
|
4994
|
+
Object.assign(normalized, rest);
|
|
4995
|
+
}
|
|
4996
|
+
return normalized;
|
|
4997
|
+
}
|
|
4998
|
+
function enrichAcpError(error, stderrExcerpt) {
|
|
4999
|
+
const normalized = normalizeAcpError(error);
|
|
5000
|
+
if (!stderrExcerpt.trim()) {
|
|
5001
|
+
return normalized;
|
|
5002
|
+
}
|
|
5003
|
+
const existingStderr = normalized.stderr;
|
|
5004
|
+
if (typeof existingStderr !== "string" || existingStderr.trim().length === 0) {
|
|
5005
|
+
Object.assign(normalized, { stderr: stderrExcerpt });
|
|
5006
|
+
}
|
|
5007
|
+
return normalized;
|
|
5008
|
+
}
|
|
4786
5009
|
class AcpBackend {
|
|
4787
5010
|
constructor(options) {
|
|
4788
5011
|
this.options = options;
|
|
@@ -4810,6 +5033,21 @@ class AcpBackend {
|
|
|
4810
5033
|
idleTimeout = null;
|
|
4811
5034
|
/** Transport handler for agent-specific behavior */
|
|
4812
5035
|
transport;
|
|
5036
|
+
/** Keep a short rolling stderr buffer so startup failures can surface the real cause. */
|
|
5037
|
+
recentStderrLines = [];
|
|
5038
|
+
recordRecentStderr(text) {
|
|
5039
|
+
const normalized = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
5040
|
+
if (normalized.length === 0) {
|
|
5041
|
+
return;
|
|
5042
|
+
}
|
|
5043
|
+
this.recentStderrLines.push(...normalized);
|
|
5044
|
+
if (this.recentStderrLines.length > 12) {
|
|
5045
|
+
this.recentStderrLines = this.recentStderrLines.slice(-12);
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
getRecentStderrExcerpt() {
|
|
5049
|
+
return this.recentStderrLines.slice(-6).join("\n");
|
|
5050
|
+
}
|
|
4813
5051
|
onMessage(handler) {
|
|
4814
5052
|
this.listeners.push(handler);
|
|
4815
5053
|
}
|
|
@@ -4837,6 +5075,7 @@ class AcpBackend {
|
|
|
4837
5075
|
this.emit({ type: "status", status: "starting" });
|
|
4838
5076
|
try {
|
|
4839
5077
|
api.logger.debug(`[AcpBackend] Starting session: ${sessionId}`);
|
|
5078
|
+
this.recentStderrLines = [];
|
|
4840
5079
|
const args = this.options.args || [];
|
|
4841
5080
|
if (process.platform === "win32") {
|
|
4842
5081
|
const fullCommand = [this.options.command, ...args].join(" ");
|
|
@@ -4863,6 +5102,7 @@ class AcpBackend {
|
|
|
4863
5102
|
this.process.stderr.on("data", (data) => {
|
|
4864
5103
|
const text = data.toString();
|
|
4865
5104
|
if (!text.trim()) return;
|
|
5105
|
+
this.recordRecentStderr(text);
|
|
4866
5106
|
const hasActiveInvestigation = this.transport.isInvestigationTool ? Array.from(this.activeToolCalls).some((id) => this.transport.isInvestigationTool(id)) : false;
|
|
4867
5107
|
const context = {
|
|
4868
5108
|
activeToolCalls: this.activeToolCalls,
|
|
@@ -4973,6 +5213,7 @@ class AcpBackend {
|
|
|
4973
5213
|
}
|
|
4974
5214
|
this.toolCallCountSincePrompt++;
|
|
4975
5215
|
const options = extendedParams.options || [];
|
|
5216
|
+
const isSelectionRequest = isSelectionPermissionRequest(extendedParams);
|
|
4976
5217
|
api.logger.debug(`[AcpBackend] Permission request: tool=${toolName}, toolCallId=${toolCallId}, input=`, JSON.stringify(input));
|
|
4977
5218
|
api.logger.debug(`[AcpBackend] Permission request params structure:`, JSON.stringify({
|
|
4978
5219
|
hasToolCall: !!toolCall,
|
|
@@ -4981,6 +5222,42 @@ class AcpBackend {
|
|
|
4981
5222
|
paramsKind: extendedParams.kind,
|
|
4982
5223
|
paramsKeys: Object.keys(params)
|
|
4983
5224
|
}, null, 2));
|
|
5225
|
+
if (isSelectionRequest) {
|
|
5226
|
+
const selectionOptions = options.reduce((acc, option) => {
|
|
5227
|
+
if (!option.optionId) {
|
|
5228
|
+
return acc;
|
|
5229
|
+
}
|
|
5230
|
+
const displayOption = option;
|
|
5231
|
+
acc.push({
|
|
5232
|
+
optionId: option.optionId,
|
|
5233
|
+
label: displayOption.label || option.name || option.optionId,
|
|
5234
|
+
description: displayOption.description || option.name || option.optionId
|
|
5235
|
+
});
|
|
5236
|
+
return acc;
|
|
5237
|
+
}, []);
|
|
5238
|
+
if (selectionOptions.length === 0) {
|
|
5239
|
+
api.logger.debug("[AcpBackend] Selection request has no valid options, cancelling");
|
|
5240
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5241
|
+
}
|
|
5242
|
+
if (!this.options.selectionHandler) {
|
|
5243
|
+
api.logger.debug("[AcpBackend] No selection handler configured, cancelling selection request");
|
|
5244
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5245
|
+
}
|
|
5246
|
+
try {
|
|
5247
|
+
const selectionMessage = typeof extendedParams.message === "string" && extendedParams.message.trim().length > 0 ? extendedParams.message : formatDisplayMessage(input).trim() || toolName;
|
|
5248
|
+
const requestId = extendedParams.codex_event_id || extendedParams.codex_elicitation || toolCallId;
|
|
5249
|
+
const response = await this.options.selectionHandler.handleSelection({
|
|
5250
|
+
id: requestId,
|
|
5251
|
+
message: selectionMessage,
|
|
5252
|
+
options: selectionOptions,
|
|
5253
|
+
defaultOptionId: extendedParams.requestedSchema?.properties?.optionId?.default
|
|
5254
|
+
});
|
|
5255
|
+
return { outcome: { outcome: "selected", optionId: response.optionId } };
|
|
5256
|
+
} catch (error) {
|
|
5257
|
+
api.logger.debug("[AcpBackend] Error in selection handler:", error);
|
|
5258
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5259
|
+
}
|
|
5260
|
+
}
|
|
4984
5261
|
this.emit({
|
|
4985
5262
|
type: "permission-request",
|
|
4986
5263
|
id: permissionId,
|
|
@@ -5154,18 +5431,19 @@ class AcpBackend {
|
|
|
5154
5431
|
if (initialPrompt) {
|
|
5155
5432
|
this.sendPrompt(sessionId, initialPrompt).catch((error) => {
|
|
5156
5433
|
api.logger.debug("[AcpBackend] Error sending initial prompt:", error);
|
|
5157
|
-
this.emit({ type: "status", status: "error", detail:
|
|
5434
|
+
this.emit({ type: "status", status: "error", detail: formatAcpErrorMessage(error) });
|
|
5158
5435
|
});
|
|
5159
5436
|
}
|
|
5160
5437
|
return { sessionId };
|
|
5161
5438
|
} catch (error) {
|
|
5162
|
-
|
|
5439
|
+
const enrichedError = enrichAcpError(error, this.getRecentStderrExcerpt());
|
|
5440
|
+
api.logger.debug("[AcpBackend] Error starting session:", enrichedError);
|
|
5163
5441
|
this.emit({
|
|
5164
5442
|
type: "status",
|
|
5165
5443
|
status: "error",
|
|
5166
|
-
detail:
|
|
5444
|
+
detail: formatAcpErrorMessage(enrichedError)
|
|
5167
5445
|
});
|
|
5168
|
-
throw
|
|
5446
|
+
throw enrichedError;
|
|
5169
5447
|
}
|
|
5170
5448
|
}
|
|
5171
5449
|
/**
|
|
@@ -5196,51 +5474,73 @@ class AcpBackend {
|
|
|
5196
5474
|
}
|
|
5197
5475
|
};
|
|
5198
5476
|
}
|
|
5477
|
+
emitUsageTelemetry(payload, source) {
|
|
5478
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
5479
|
+
return false;
|
|
5480
|
+
}
|
|
5481
|
+
const tokens = extractUsageTokens(payload);
|
|
5482
|
+
if (!tokens) {
|
|
5483
|
+
return false;
|
|
5484
|
+
}
|
|
5485
|
+
this.emit({
|
|
5486
|
+
type: "token-count",
|
|
5487
|
+
key: source,
|
|
5488
|
+
tokens,
|
|
5489
|
+
source
|
|
5490
|
+
});
|
|
5491
|
+
return true;
|
|
5492
|
+
}
|
|
5199
5493
|
handleSessionUpdate(params) {
|
|
5200
|
-
const
|
|
5201
|
-
|
|
5202
|
-
if (!update) {
|
|
5494
|
+
const updates = getSessionUpdates(params);
|
|
5495
|
+
if (updates.length === 0) {
|
|
5203
5496
|
api.logger.debug("[AcpBackend] Received session update without update field:", params);
|
|
5204
5497
|
return;
|
|
5205
5498
|
}
|
|
5206
|
-
const
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5499
|
+
for (const update of updates) {
|
|
5500
|
+
const sessionUpdateType = update.sessionUpdate;
|
|
5501
|
+
if (sessionUpdateType !== "agent_message_chunk") {
|
|
5502
|
+
api.logger.debug(`[AcpBackend] Received session update: ${sessionUpdateType}`, JSON.stringify({
|
|
5503
|
+
sessionUpdate: sessionUpdateType,
|
|
5504
|
+
toolCallId: update.toolCallId,
|
|
5505
|
+
status: update.status,
|
|
5506
|
+
kind: update.kind,
|
|
5507
|
+
hasContent: !!update.content,
|
|
5508
|
+
hasLocations: !!update.locations
|
|
5509
|
+
}, null, 2));
|
|
5510
|
+
}
|
|
5511
|
+
const ctx = this.createHandlerContext();
|
|
5512
|
+
if (sessionUpdateType === "agent_message_chunk") {
|
|
5513
|
+
handleAgentMessageChunk(update, ctx);
|
|
5514
|
+
continue;
|
|
5515
|
+
}
|
|
5516
|
+
if (sessionUpdateType === "tool_call_update") {
|
|
5517
|
+
const result = handleToolCallUpdate(update, ctx);
|
|
5518
|
+
if (result.toolCallCountSincePrompt !== void 0) {
|
|
5519
|
+
this.toolCallCountSincePrompt = result.toolCallCountSincePrompt;
|
|
5520
|
+
}
|
|
5521
|
+
continue;
|
|
5522
|
+
}
|
|
5523
|
+
if (sessionUpdateType === "agent_thought_chunk") {
|
|
5524
|
+
handleAgentThoughtChunk(update, ctx);
|
|
5525
|
+
continue;
|
|
5526
|
+
}
|
|
5527
|
+
if (sessionUpdateType === "tool_call") {
|
|
5528
|
+
handleToolCall(update, ctx);
|
|
5529
|
+
continue;
|
|
5530
|
+
}
|
|
5531
|
+
if (sessionUpdateType === "usage_update") {
|
|
5532
|
+
this.emitUsageTelemetry(update, "acp-usage-update");
|
|
5533
|
+
continue;
|
|
5534
|
+
}
|
|
5535
|
+
const handledLegacy = handleLegacyMessageChunk(update, ctx).handled;
|
|
5536
|
+
const handledPlan = handlePlanUpdate(update, ctx).handled;
|
|
5537
|
+
const handledThinking = handleThinkingUpdate(update, ctx).handled;
|
|
5538
|
+
const handledUsage = this.emitUsageTelemetry(update.usage, "acp-session-usage");
|
|
5539
|
+
const updateTypeStr = sessionUpdateType;
|
|
5540
|
+
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call", "usage_update"];
|
|
5541
|
+
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !handledLegacy && !handledPlan && !handledThinking && !handledUsage) {
|
|
5542
|
+
api.logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5226
5543
|
}
|
|
5227
|
-
return;
|
|
5228
|
-
}
|
|
5229
|
-
if (sessionUpdateType === "agent_thought_chunk") {
|
|
5230
|
-
handleAgentThoughtChunk(update, ctx);
|
|
5231
|
-
return;
|
|
5232
|
-
}
|
|
5233
|
-
if (sessionUpdateType === "tool_call") {
|
|
5234
|
-
handleToolCall(update, ctx);
|
|
5235
|
-
return;
|
|
5236
|
-
}
|
|
5237
|
-
handleLegacyMessageChunk(update, ctx);
|
|
5238
|
-
handlePlanUpdate(update, ctx);
|
|
5239
|
-
handleThinkingUpdate(update, ctx);
|
|
5240
|
-
const updateTypeStr = sessionUpdateType;
|
|
5241
|
-
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call"];
|
|
5242
|
-
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !update.messageChunk && !update.plan && !update.thinking) {
|
|
5243
|
-
api.logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5244
5544
|
}
|
|
5245
5545
|
}
|
|
5246
5546
|
// Promise resolver for waitForIdle - set when waiting for response to complete
|
|
@@ -5277,17 +5577,9 @@ class AcpBackend {
|
|
|
5277
5577
|
if (error instanceof Error) {
|
|
5278
5578
|
errorDetail = error.message;
|
|
5279
5579
|
} else if (typeof error === "object" && error !== null) {
|
|
5280
|
-
|
|
5281
|
-
const fallbackMessage = (typeof errObj.message === "string" ? errObj.message : void 0) || String(error);
|
|
5282
|
-
if (errObj.code !== void 0) {
|
|
5283
|
-
errorDetail = JSON.stringify({ code: errObj.code, message: fallbackMessage });
|
|
5284
|
-
} else if (typeof errObj.message === "string") {
|
|
5285
|
-
errorDetail = errObj.message;
|
|
5286
|
-
} else {
|
|
5287
|
-
errorDetail = String(error);
|
|
5288
|
-
}
|
|
5580
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5289
5581
|
} else {
|
|
5290
|
-
errorDetail =
|
|
5582
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5291
5583
|
}
|
|
5292
5584
|
this.emit({
|
|
5293
5585
|
type: "status",
|
|
@@ -5638,59 +5930,213 @@ function registerGeminiAgent() {
|
|
|
5638
5930
|
api.logger.debug("[Gemini] Registered with agent registry");
|
|
5639
5931
|
}
|
|
5640
5932
|
|
|
5641
|
-
function
|
|
5642
|
-
for (const
|
|
5643
|
-
|
|
5933
|
+
function readFirstEnv(...names) {
|
|
5934
|
+
for (const name of names) {
|
|
5935
|
+
const raw = process.env[name];
|
|
5936
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
5937
|
+
return raw.trim();
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
return "";
|
|
5941
|
+
}
|
|
5942
|
+
function normalizeCommandPath(command) {
|
|
5943
|
+
if (path.isAbsolute(command)) {
|
|
5944
|
+
return command;
|
|
5945
|
+
}
|
|
5946
|
+
const resolved = path.resolve(process.cwd(), command);
|
|
5947
|
+
return fs.existsSync(resolved) ? resolved : command;
|
|
5948
|
+
}
|
|
5949
|
+
function resolveCommandOnPath(command) {
|
|
5950
|
+
const pathValue = typeof process.env.PATH === "string" ? process.env.PATH : "";
|
|
5951
|
+
if (!pathValue) {
|
|
5952
|
+
return null;
|
|
5953
|
+
}
|
|
5954
|
+
const extensions = process.platform === "win32" ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter(Boolean) : [""];
|
|
5955
|
+
for (const dir of pathValue.split(path.delimiter)) {
|
|
5956
|
+
const trimmedDir = dir.trim();
|
|
5957
|
+
if (!trimmedDir) {
|
|
5958
|
+
continue;
|
|
5959
|
+
}
|
|
5960
|
+
const directCandidate = path.join(trimmedDir, command);
|
|
5961
|
+
if (fs.existsSync(directCandidate)) {
|
|
5962
|
+
return directCandidate;
|
|
5963
|
+
}
|
|
5964
|
+
if (process.platform !== "win32") {
|
|
5965
|
+
continue;
|
|
5966
|
+
}
|
|
5967
|
+
const hasExtension = /\.[^\\/]+$/.test(command);
|
|
5968
|
+
if (hasExtension) {
|
|
5969
|
+
continue;
|
|
5970
|
+
}
|
|
5971
|
+
for (const extension of extensions) {
|
|
5972
|
+
const candidate = path.join(trimmedDir, `${command}${extension.toLowerCase()}`);
|
|
5644
5973
|
if (fs.existsSync(candidate)) {
|
|
5645
5974
|
return candidate;
|
|
5646
5975
|
}
|
|
5647
|
-
} catch {
|
|
5648
5976
|
}
|
|
5649
5977
|
}
|
|
5650
5978
|
return null;
|
|
5651
5979
|
}
|
|
5652
|
-
function
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
const resolved = firstExistingPath([
|
|
5657
|
-
path.join(npmGlobalBin, "codex.cmd"),
|
|
5658
|
-
path.join(npmGlobalBin, "codex.ps1"),
|
|
5659
|
-
path.join(npmGlobalBin, "codex")
|
|
5660
|
-
]);
|
|
5661
|
-
if (resolved) {
|
|
5662
|
-
return resolved;
|
|
5663
|
-
}
|
|
5980
|
+
function readCodexAcpNpxMode() {
|
|
5981
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_NPX_MODE", "HAPPIER_CODEX_ACP_NPX_MODE").toLowerCase();
|
|
5982
|
+
if (raw === "auto" || raw === "never" || raw === "force") {
|
|
5983
|
+
return raw;
|
|
5664
5984
|
}
|
|
5665
|
-
return "
|
|
5985
|
+
return "auto";
|
|
5986
|
+
}
|
|
5987
|
+
function isBinOnPath(baseName) {
|
|
5988
|
+
return resolveCommandOnPath(baseName) !== null;
|
|
5666
5989
|
}
|
|
5667
|
-
function
|
|
5668
|
-
|
|
5990
|
+
function readCodexAcpConfigOverrides() {
|
|
5991
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_CONFIG_OVERRIDES", "HAPPIER_CODEX_ACP_CONFIG_OVERRIDES");
|
|
5992
|
+
return raw.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
5993
|
+
}
|
|
5994
|
+
function buildCodexAcpConfigArgs(options) {
|
|
5995
|
+
const args = [...options.baseArgs ?? []];
|
|
5996
|
+
const overrides = [...readCodexAcpConfigOverrides()];
|
|
5997
|
+
if (options.model) {
|
|
5998
|
+
overrides.push(`model=${JSON.stringify(options.model)}`);
|
|
5999
|
+
}
|
|
6000
|
+
if (options.approvalPolicy) {
|
|
6001
|
+
overrides.push(`approval_policy=${JSON.stringify(options.approvalPolicy)}`);
|
|
6002
|
+
}
|
|
6003
|
+
if (options.sandbox) {
|
|
6004
|
+
overrides.push(`sandbox_mode=${JSON.stringify(options.sandbox)}`);
|
|
6005
|
+
}
|
|
6006
|
+
for (const override of overrides) {
|
|
6007
|
+
args.push("-c", override);
|
|
6008
|
+
}
|
|
6009
|
+
return args;
|
|
6010
|
+
}
|
|
6011
|
+
function resolveNpxCommand() {
|
|
6012
|
+
return resolveCommandOnPath(process.platform === "win32" ? "npx.cmd" : "npx") ?? resolveCommandOnPath("npx") ?? (process.platform === "win32" ? "npx.cmd" : "npx");
|
|
6013
|
+
}
|
|
6014
|
+
function resolveCodexAcpSpawn(options = {}) {
|
|
6015
|
+
if (options.args) {
|
|
6016
|
+
const command = normalizeCommandPath(options.command || readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN") || "codex-acp");
|
|
6017
|
+
return {
|
|
6018
|
+
command,
|
|
6019
|
+
args: [...options.args]
|
|
6020
|
+
};
|
|
6021
|
+
}
|
|
6022
|
+
const directArgs = buildCodexAcpConfigArgs(options);
|
|
6023
|
+
if (options.command) {
|
|
6024
|
+
return {
|
|
6025
|
+
command: normalizeCommandPath(options.command),
|
|
6026
|
+
args: directArgs
|
|
6027
|
+
};
|
|
6028
|
+
}
|
|
6029
|
+
const envOverride = readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN");
|
|
6030
|
+
if (envOverride) {
|
|
6031
|
+
return {
|
|
6032
|
+
command: normalizeCommandPath(envOverride),
|
|
6033
|
+
args: directArgs
|
|
6034
|
+
};
|
|
6035
|
+
}
|
|
6036
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6037
|
+
const codexAcpOnPath = resolveCommandOnPath(process.platform === "win32" ? "codex-acp.cmd" : "codex-acp") ?? resolveCommandOnPath("codex-acp");
|
|
6038
|
+
if (npxMode !== "force" && codexAcpOnPath) {
|
|
6039
|
+
return {
|
|
6040
|
+
command: codexAcpOnPath,
|
|
6041
|
+
args: directArgs
|
|
6042
|
+
};
|
|
6043
|
+
}
|
|
6044
|
+
if (npxMode === "never") {
|
|
6045
|
+
return {
|
|
6046
|
+
command: process.platform === "win32" ? "codex-acp.cmd" : "codex-acp",
|
|
6047
|
+
args: directArgs
|
|
6048
|
+
};
|
|
6049
|
+
}
|
|
6050
|
+
return {
|
|
6051
|
+
command: resolveNpxCommand(),
|
|
6052
|
+
args: ["--prefer-offline", "-y", "@zed-industries/codex-acp", ...directArgs]
|
|
6053
|
+
};
|
|
6054
|
+
}
|
|
6055
|
+
function validateCodexAcpSpawn(options = {}) {
|
|
6056
|
+
let spawn;
|
|
6057
|
+
try {
|
|
6058
|
+
spawn = resolveCodexAcpSpawn(options);
|
|
6059
|
+
} catch (error) {
|
|
6060
|
+
return {
|
|
6061
|
+
ok: false,
|
|
6062
|
+
errorMessage: error instanceof Error ? error.message : "Codex ACP is enabled, but the command could not be resolved."
|
|
6063
|
+
};
|
|
6064
|
+
}
|
|
6065
|
+
const normalizedCommand = spawn.command.trim();
|
|
6066
|
+
const commandLower = normalizedCommand.toLowerCase();
|
|
6067
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6068
|
+
if (path.isAbsolute(normalizedCommand)) {
|
|
6069
|
+
if (!fs.existsSync(normalizedCommand)) {
|
|
6070
|
+
return {
|
|
6071
|
+
ok: false,
|
|
6072
|
+
errorMessage: `Codex ACP is enabled, but the resolved command does not exist: ${normalizedCommand}`
|
|
6073
|
+
};
|
|
6074
|
+
}
|
|
6075
|
+
return { ok: true, spawn };
|
|
6076
|
+
}
|
|
6077
|
+
if (commandLower.endsWith("npx") || commandLower.endsWith("npx.cmd")) {
|
|
6078
|
+
if (isBinOnPath("npx") || isBinOnPath("npx.cmd")) {
|
|
6079
|
+
return { ok: true, spawn };
|
|
6080
|
+
}
|
|
6081
|
+
return {
|
|
6082
|
+
ok: false,
|
|
6083
|
+
errorMessage: "Codex ACP is enabled, but codex-acp is not installed and npx is not available. Install codex-acp, install Node.js/npm so npx is available, or set HAPPY_CODEX_ACP_BIN to a working codex-acp executable."
|
|
6084
|
+
};
|
|
6085
|
+
}
|
|
6086
|
+
if (commandLower === "codex-acp" || commandLower === "codex-acp.cmd") {
|
|
6087
|
+
if (isBinOnPath("codex-acp") || isBinOnPath("codex-acp.cmd")) {
|
|
6088
|
+
return { ok: true, spawn };
|
|
6089
|
+
}
|
|
6090
|
+
return {
|
|
6091
|
+
ok: false,
|
|
6092
|
+
errorMessage: npxMode === "never" ? "Codex ACP is enabled, but codex-acp is not installed and npx fallback is disabled. Install codex-acp, add it to PATH, or set HAPPY_CODEX_ACP_BIN to the executable." : "Codex ACP is enabled, but codex-acp could not be resolved on PATH. Install codex-acp, add it to PATH, or set HAPPY_CODEX_ACP_BIN to the executable."
|
|
6093
|
+
};
|
|
6094
|
+
}
|
|
6095
|
+
return { ok: true, spawn };
|
|
5669
6096
|
}
|
|
5670
6097
|
|
|
5671
|
-
|
|
5672
|
-
|
|
6098
|
+
class CodexAcpTransport extends CodexTransport {
|
|
6099
|
+
constructor(initTimeoutMs) {
|
|
6100
|
+
super();
|
|
6101
|
+
this.initTimeoutMs = initTimeoutMs;
|
|
6102
|
+
}
|
|
6103
|
+
getInitTimeout() {
|
|
6104
|
+
return this.initTimeoutMs;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
function resolveCodexTransport(command) {
|
|
6108
|
+
if (/npx(?:\.cmd)?$/i.test(command)) {
|
|
6109
|
+
return new CodexAcpTransport(18e4);
|
|
6110
|
+
}
|
|
6111
|
+
return new CodexAcpTransport(6e4);
|
|
5673
6112
|
}
|
|
5674
6113
|
function createCodexBackend(options) {
|
|
5675
|
-
const
|
|
5676
|
-
|
|
6114
|
+
const spawn = resolveCodexAcpSpawn({
|
|
6115
|
+
command: options.command,
|
|
6116
|
+
args: options.args,
|
|
6117
|
+
baseArgs: options.baseArgs,
|
|
6118
|
+
model: options.model,
|
|
6119
|
+
sandbox: options.sandbox,
|
|
6120
|
+
approvalPolicy: options.approvalPolicy
|
|
6121
|
+
});
|
|
5677
6122
|
const backendOptions = {
|
|
5678
6123
|
agentName: "codex",
|
|
5679
6124
|
cwd: options.cwd,
|
|
5680
|
-
command,
|
|
5681
|
-
args,
|
|
6125
|
+
command: spawn.command,
|
|
6126
|
+
args: spawn.args,
|
|
5682
6127
|
env: {
|
|
5683
6128
|
...options.env,
|
|
5684
6129
|
NODE_ENV: "production"
|
|
5685
6130
|
},
|
|
5686
6131
|
mcpServers: options.mcpServers,
|
|
5687
6132
|
permissionHandler: options.permissionHandler,
|
|
5688
|
-
|
|
6133
|
+
selectionHandler: options.selectionHandler,
|
|
6134
|
+
transportHandler: resolveCodexTransport(spawn.command)
|
|
5689
6135
|
};
|
|
5690
6136
|
return {
|
|
5691
6137
|
backend: new AcpBackend(backendOptions),
|
|
5692
|
-
command,
|
|
5693
|
-
args
|
|
6138
|
+
command: spawn.command,
|
|
6139
|
+
args: spawn.args
|
|
5694
6140
|
};
|
|
5695
6141
|
}
|
|
5696
6142
|
function registerCodexAgent() {
|
|
@@ -5876,17 +6322,26 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
5876
6322
|
env: process.env
|
|
5877
6323
|
});
|
|
5878
6324
|
daemonProcess.unref();
|
|
5879
|
-
|
|
6325
|
+
for (let i = 0; i < 100; i++) {
|
|
6326
|
+
if (await isDaemonRunningCurrentlyInstalledHappyVersion()) {
|
|
6327
|
+
return;
|
|
6328
|
+
}
|
|
6329
|
+
if (await isDaemonControlServerResponsive(500)) {
|
|
6330
|
+
return;
|
|
6331
|
+
}
|
|
6332
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
6333
|
+
}
|
|
6334
|
+
throw new Error("Failed to start Happy background service.");
|
|
5880
6335
|
}
|
|
5881
6336
|
async function executeUnifiedProvider(opts) {
|
|
5882
6337
|
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials);
|
|
5883
6338
|
if (opts.provider === "claude") {
|
|
5884
|
-
const { runClaude } = await Promise.resolve().then(function () { return require('./runClaude-
|
|
6339
|
+
const { runClaude } = await Promise.resolve().then(function () { return require('./runClaude-B-GNEkKg.cjs'); });
|
|
5885
6340
|
await runClaude(credentials, opts.claudeOptions ?? {});
|
|
5886
6341
|
return;
|
|
5887
6342
|
}
|
|
5888
6343
|
if (opts.provider === "codex") {
|
|
5889
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
6344
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-CPHyGwj9.cjs'); });
|
|
5890
6345
|
await runCodex({
|
|
5891
6346
|
credentials,
|
|
5892
6347
|
startedBy: opts.startedBy,
|
|
@@ -5896,7 +6351,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5896
6351
|
return;
|
|
5897
6352
|
}
|
|
5898
6353
|
if (opts.provider === "gemini") {
|
|
5899
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
6354
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-DaDz1bzQ.cjs'); });
|
|
5900
6355
|
await runGemini({
|
|
5901
6356
|
credentials,
|
|
5902
6357
|
startedBy: opts.startedBy
|
|
@@ -5909,6 +6364,10 @@ async function executeUnifiedProvider(opts) {
|
|
|
5909
6364
|
});
|
|
5910
6365
|
}
|
|
5911
6366
|
|
|
6367
|
+
function shouldRunMainClaudeFlow(opts) {
|
|
6368
|
+
return !opts.showHelp && !opts.showVersion;
|
|
6369
|
+
}
|
|
6370
|
+
|
|
5912
6371
|
(async () => {
|
|
5913
6372
|
const args = process.argv.slice(2);
|
|
5914
6373
|
const isRemoteMode = args.includes("--happy-starting-mode") && args.includes("remote");
|
|
@@ -5917,7 +6376,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5917
6376
|
Object.defineProperty(process.stderr, "isTTY", { value: false });
|
|
5918
6377
|
}
|
|
5919
6378
|
if (!args.includes("--version")) {
|
|
5920
|
-
api.logger.debug("Starting
|
|
6379
|
+
api.logger.debug("Starting hicloud CLI with args: ", process.argv);
|
|
5921
6380
|
}
|
|
5922
6381
|
const subcommand = args[0];
|
|
5923
6382
|
if (!args.includes("--version")) ;
|
|
@@ -5934,7 +6393,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5934
6393
|
return;
|
|
5935
6394
|
} else if (subcommand === "runtime") {
|
|
5936
6395
|
if (args[1] === "providers") {
|
|
5937
|
-
const { renderRuntimeProviders } = await Promise.resolve().then(function () { return require('./command-
|
|
6396
|
+
const { renderRuntimeProviders } = await Promise.resolve().then(function () { return require('./command-CVldr51S.cjs'); });
|
|
5938
6397
|
console.log(renderRuntimeProviders());
|
|
5939
6398
|
return;
|
|
5940
6399
|
}
|
|
@@ -5960,8 +6419,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
5960
6419
|
}
|
|
5961
6420
|
return;
|
|
5962
6421
|
}
|
|
5963
|
-
console.log("Usage:
|
|
5964
|
-
console.log("
|
|
6422
|
+
console.log("Usage: hicloud runtime providers");
|
|
6423
|
+
console.log(" hicloud runtime launch <claude|codex|gemini|cursor> [prompt]");
|
|
5965
6424
|
return;
|
|
5966
6425
|
} else if (subcommand === "auth") {
|
|
5967
6426
|
try {
|
|
@@ -6112,8 +6571,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
6112
6571
|
const projectId = args[3];
|
|
6113
6572
|
try {
|
|
6114
6573
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
6115
|
-
const { readCredentials: readCredentials2 } = await Promise.resolve().then(function () { return require('./persistence-
|
|
6116
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./api-
|
|
6574
|
+
const { readCredentials: readCredentials2 } = await Promise.resolve().then(function () { return require('./persistence-D_2GkJAO.cjs'); });
|
|
6575
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./api-D2Njw9Im.cjs'); }).then(function (n) { return n.api; });
|
|
6117
6576
|
let userEmail = void 0;
|
|
6118
6577
|
try {
|
|
6119
6578
|
const credentials = await readCredentials2();
|
|
@@ -6159,7 +6618,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6159
6618
|
console.log("No Google Cloud Project configured.");
|
|
6160
6619
|
console.log("");
|
|
6161
6620
|
console.log('If you see "Authentication required" error, you may need to set a project:');
|
|
6162
|
-
console.log("
|
|
6621
|
+
console.log(" hicloud gemini project set <your-project-id>");
|
|
6163
6622
|
console.log("");
|
|
6164
6623
|
console.log("This is required for Google Workspace accounts.");
|
|
6165
6624
|
console.log("Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca");
|
|
@@ -6171,7 +6630,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6171
6630
|
}
|
|
6172
6631
|
}
|
|
6173
6632
|
if (geminiSubcommand === "project" && !args[2]) {
|
|
6174
|
-
console.log("Usage:
|
|
6633
|
+
console.log("Usage: hicloud gemini project <command>");
|
|
6175
6634
|
console.log("");
|
|
6176
6635
|
console.log("Commands:");
|
|
6177
6636
|
console.log(" set <project-id> Set Google Cloud Project ID");
|
|
@@ -6203,7 +6662,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6203
6662
|
}
|
|
6204
6663
|
return;
|
|
6205
6664
|
} else if (subcommand === "logout") {
|
|
6206
|
-
console.log(chalk.yellow('Note: "
|
|
6665
|
+
console.log(chalk.yellow('Note: "hicloud logout" is deprecated. Use "hicloud auth logout" instead.\n'));
|
|
6207
6666
|
try {
|
|
6208
6667
|
await handleAuthCommand(["logout"]);
|
|
6209
6668
|
} catch (error) {
|
|
@@ -6262,7 +6721,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6262
6721
|
child.unref();
|
|
6263
6722
|
let started = false;
|
|
6264
6723
|
for (let i = 0; i < 100; i++) {
|
|
6265
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
6724
|
+
if (await checkIfDaemonRunningAndCleanupStaleState() && await isDaemonControlServerResponsive(500)) {
|
|
6266
6725
|
started = true;
|
|
6267
6726
|
break;
|
|
6268
6727
|
}
|
|
@@ -6319,20 +6778,20 @@ async function executeUnifiedProvider(opts) {
|
|
|
6319
6778
|
}
|
|
6320
6779
|
} else {
|
|
6321
6780
|
console.log(`
|
|
6322
|
-
${chalk.bold("
|
|
6781
|
+
${chalk.bold("hicloud daemon")} - Daemon management
|
|
6323
6782
|
|
|
6324
6783
|
${chalk.bold("Usage:")}
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6784
|
+
hicloud daemon start Start the daemon (detached)
|
|
6785
|
+
hicloud daemon stop Stop the daemon (sessions stay alive)
|
|
6786
|
+
hicloud daemon status Show daemon status
|
|
6787
|
+
hicloud daemon list List active sessions
|
|
6329
6788
|
|
|
6330
6789
|
If you want to kill all happy related processes run
|
|
6331
|
-
${chalk.cyan("
|
|
6790
|
+
${chalk.cyan("hicloud doctor clean")}
|
|
6332
6791
|
|
|
6333
6792
|
${chalk.bold("Note:")} The daemon runs in the background and manages Claude sessions.
|
|
6334
6793
|
|
|
6335
|
-
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("
|
|
6794
|
+
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("hicloud doctor clean")}
|
|
6336
6795
|
`);
|
|
6337
6796
|
}
|
|
6338
6797
|
return;
|
|
@@ -6407,33 +6866,33 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy-cloud do
|
|
|
6407
6866
|
${chalk.bold(BRAND_CONFIG.name)} - AI \u7F16\u7A0B\u52A9\u624B
|
|
6408
6867
|
|
|
6409
6868
|
${chalk.bold("Usage:")}
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6869
|
+
hicloud [options] Start Claude with mobile control
|
|
6870
|
+
hicloud auth Manage authentication
|
|
6871
|
+
hicloud codex Start Codex mode
|
|
6872
|
+
hicloud gemini Start Gemini mode (ACP)
|
|
6873
|
+
hicloud cursor Start Cursor mode (experimental ACP)
|
|
6874
|
+
hicloud connect Connect AI vendor API keys
|
|
6875
|
+
hicloud notify Send push notification
|
|
6876
|
+
hicloud daemon Manage background service that allows
|
|
6418
6877
|
to spawn new sessions away from your computer
|
|
6419
|
-
|
|
6878
|
+
hicloud doctor System diagnostics & troubleshooting
|
|
6420
6879
|
|
|
6421
6880
|
${chalk.bold("Examples:")}
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6881
|
+
hicloud Start session
|
|
6882
|
+
hicloud --yolo Start with bypassing permissions
|
|
6883
|
+
hicloud sugar for --dangerously-skip-permissions
|
|
6884
|
+
hicloud --chrome Enable Chrome browser access for this session
|
|
6885
|
+
hicloud --no-chrome Disable Chrome even if default is on
|
|
6886
|
+
hicloud --js-runtime bun Use bun instead of node to spawn Claude Code
|
|
6887
|
+
hicloud --claude-env ANTHROPIC_BASE_URL=http://127.0.0.1:3456
|
|
6429
6888
|
Use a custom API endpoint (e.g., claude-code-router)
|
|
6430
|
-
|
|
6431
|
-
|
|
6889
|
+
hicloud auth login --force Authenticate
|
|
6890
|
+
hicloud doctor Run diagnostics
|
|
6432
6891
|
|
|
6433
6892
|
${chalk.bold(`${BRAND_CONFIG.name} supports ALL Claude options!`)}
|
|
6434
|
-
Use any claude flag with
|
|
6893
|
+
Use any claude flag with hicloud as you would with claude. Our favorite:
|
|
6435
6894
|
|
|
6436
|
-
|
|
6895
|
+
hicloud --resume
|
|
6437
6896
|
|
|
6438
6897
|
${chalk.gray("\u2500".repeat(60))}
|
|
6439
6898
|
${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
@@ -6448,8 +6907,9 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
6448
6907
|
}
|
|
6449
6908
|
if (showVersion) {
|
|
6450
6909
|
console.log(getVersionString());
|
|
6910
|
+
return;
|
|
6451
6911
|
}
|
|
6452
|
-
if (
|
|
6912
|
+
if (shouldRunMainClaudeFlow({ showHelp, showVersion })) {
|
|
6453
6913
|
printBanner();
|
|
6454
6914
|
}
|
|
6455
6915
|
try {
|
|
@@ -6485,31 +6945,31 @@ async function handleNotifyCommand(args) {
|
|
|
6485
6945
|
}
|
|
6486
6946
|
if (showHelp) {
|
|
6487
6947
|
console.log(`
|
|
6488
|
-
${chalk.bold("
|
|
6948
|
+
${chalk.bold("hicloud notify")} - Send notification
|
|
6489
6949
|
|
|
6490
6950
|
${chalk.bold("Usage:")}
|
|
6491
|
-
|
|
6492
|
-
|
|
6951
|
+
hicloud notify -p <message> [-t <title>] Send notification with custom message and optional title
|
|
6952
|
+
hicloud notify -h, --help Show this help
|
|
6493
6953
|
|
|
6494
6954
|
${chalk.bold("Options:")}
|
|
6495
6955
|
-p <message> Notification message (required)
|
|
6496
6956
|
-t <title> Notification title (optional, defaults to "Happy")
|
|
6497
6957
|
|
|
6498
6958
|
${chalk.bold("Examples:")}
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6959
|
+
hicloud notify -p "Deployment complete!"
|
|
6960
|
+
hicloud notify -p "System update complete" -t "Server Status"
|
|
6961
|
+
hicloud notify -t "Alert" -p "Database connection restored"
|
|
6502
6962
|
`);
|
|
6503
6963
|
return;
|
|
6504
6964
|
}
|
|
6505
6965
|
if (!message) {
|
|
6506
6966
|
console.error(chalk.red('Error: Message is required. Use -p "your message" to specify the notification text.'));
|
|
6507
|
-
console.log(chalk.gray('Run "
|
|
6967
|
+
console.log(chalk.gray('Run "hicloud notify --help" for usage information.'));
|
|
6508
6968
|
process.exit(1);
|
|
6509
6969
|
}
|
|
6510
6970
|
let credentials = await persistence.readCredentials();
|
|
6511
6971
|
if (!credentials) {
|
|
6512
|
-
console.error(chalk.red('Error: Not authenticated. Please run "
|
|
6972
|
+
console.error(chalk.red('Error: Not authenticated. Please run "hicloud auth login" first.'));
|
|
6513
6973
|
process.exit(1);
|
|
6514
6974
|
}
|
|
6515
6975
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
@@ -6539,8 +6999,10 @@ exports.ExitCodeError = ExitCodeError;
|
|
|
6539
6999
|
exports.GEMINI_MODEL_ENV = GEMINI_MODEL_ENV;
|
|
6540
7000
|
exports.claudeCheckSession = claudeCheckSession;
|
|
6541
7001
|
exports.claudeLocal = claudeLocal;
|
|
7002
|
+
exports.createCodexBackend = createCodexBackend;
|
|
6542
7003
|
exports.createDefaultRuntimeShell = createDefaultRuntimeShell;
|
|
6543
7004
|
exports.createGeminiBackend = createGeminiBackend;
|
|
7005
|
+
exports.formatDisplayMessage = formatDisplayMessage;
|
|
6544
7006
|
exports.getEnvironmentInfo = getEnvironmentInfo;
|
|
6545
7007
|
exports.getInitialGeminiModel = getInitialGeminiModel;
|
|
6546
7008
|
exports.getProjectPath = getProjectPath;
|
|
@@ -6549,9 +7011,9 @@ exports.isBun = isBun;
|
|
|
6549
7011
|
exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
|
|
6550
7012
|
exports.projectPath = projectPath;
|
|
6551
7013
|
exports.readGeminiLocalConfig = readGeminiLocalConfig;
|
|
6552
|
-
exports.resolveCodexExecutable = resolveCodexExecutable;
|
|
6553
7014
|
exports.saveGeminiModelToConfig = saveGeminiModelToConfig;
|
|
6554
|
-
exports.shouldUseShellForCodex = shouldUseShellForCodex;
|
|
6555
7015
|
exports.startCaffeinate = startCaffeinate;
|
|
6556
7016
|
exports.stopCaffeinate = stopCaffeinate;
|
|
6557
7017
|
exports.trimIdent = trimIdent;
|
|
7018
|
+
exports.truncateDisplayMessage = truncateDisplayMessage;
|
|
7019
|
+
exports.validateCodexAcpSpawn = validateCodexAcpSpawn;
|