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,6 +1,6 @@
|
|
|
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, h as buildAuthenticatedHeaders,
|
|
3
|
-
import {
|
|
2
|
+
import { l as logger, e as encodeBase64, c as configuration, h as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, j as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, k as encodeBase64Url, f as delay, m as buildClientHeaders, n as decodeBase64, H as HAPPY_CLOUD_DAEMON_PORT, p as packageJson, A as ApiClient, o as getLatestDaemonLog } from './api-MYhAGPLn.mjs';
|
|
3
|
+
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-Dkm7rm8k.mjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import fs from 'fs/promises';
|
|
6
6
|
import os, { homedir } from 'os';
|
|
@@ -12,7 +12,7 @@ import qrcode from 'qrcode-terminal';
|
|
|
12
12
|
import { writeFile, unlink } from 'node:fs/promises';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
14
|
import os$1, { tmpdir, homedir as homedir$1 } from 'node:os';
|
|
15
|
-
import
|
|
15
|
+
import { join, resolve as resolve$1, isAbsolute, delimiter } from 'node:path';
|
|
16
16
|
import open from 'open';
|
|
17
17
|
import React, { useState } from 'react';
|
|
18
18
|
import { useInput, Box, Text, render } from 'ink';
|
|
@@ -23,7 +23,7 @@ import { readFileSync as readFileSync$1, existsSync as existsSync$1, writeFileSy
|
|
|
23
23
|
import { execFileSync, spawn as spawn$2 } from 'node:child_process';
|
|
24
24
|
import psList from 'ps-list';
|
|
25
25
|
import spawn$1 from 'cross-spawn';
|
|
26
|
-
import
|
|
26
|
+
import { existsSync, readFileSync, readdirSync, statSync, rmSync, mkdirSync as mkdirSync$1 } from 'node:fs';
|
|
27
27
|
import fastify from 'fastify';
|
|
28
28
|
import { validatorCompiler, serializerCompiler } from 'fastify-type-provider-zod';
|
|
29
29
|
import { createInterface } from 'node:readline';
|
|
@@ -219,6 +219,64 @@ const AuthSelector = ({ onSelect, onCancel }) => {
|
|
|
219
219
|
})), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
|
|
220
220
|
};
|
|
221
221
|
|
|
222
|
+
async function ensureSigningCredentials(credentials, opts = {}) {
|
|
223
|
+
if (credentials.signing) {
|
|
224
|
+
return credentials;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const response = await axios.post(`${configuration.serverUrl}/v1/auth/refresh`, {}, {
|
|
228
|
+
headers: buildAuthenticatedHeaders({
|
|
229
|
+
credentials,
|
|
230
|
+
method: "POST",
|
|
231
|
+
url: `${configuration.serverUrl}/v1/auth/refresh`,
|
|
232
|
+
body: {},
|
|
233
|
+
headers: {
|
|
234
|
+
"Content-Type": "application/json"
|
|
235
|
+
},
|
|
236
|
+
signRequest: false
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
if (!response.data?.success || !response.data?.token || !response.data?.signing) {
|
|
240
|
+
logger.debug("[AUTH] Signing bootstrap returned incomplete payload", response.data);
|
|
241
|
+
throw new SigningBootstrapRequiredError(SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
242
|
+
}
|
|
243
|
+
const upgradedCredentials = {
|
|
244
|
+
...credentials,
|
|
245
|
+
token: response.data.token,
|
|
246
|
+
signing: response.data.signing
|
|
247
|
+
};
|
|
248
|
+
if (upgradedCredentials.encryption.type === "legacy") {
|
|
249
|
+
await writeCredentialsLegacy({
|
|
250
|
+
secret: upgradedCredentials.encryption.secret,
|
|
251
|
+
token: upgradedCredentials.token,
|
|
252
|
+
signing: upgradedCredentials.signing
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
await writeCredentialsDataKey({
|
|
256
|
+
publicKey: upgradedCredentials.encryption.publicKey,
|
|
257
|
+
machineKey: upgradedCredentials.encryption.machineKey,
|
|
258
|
+
token: upgradedCredentials.token,
|
|
259
|
+
signing: upgradedCredentials.signing
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
logger.debug("[AUTH] Signing credentials bootstrapped successfully");
|
|
263
|
+
return upgradedCredentials;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
|
266
|
+
logger.debug("[AUTH] Signing bootstrap rejected with 401");
|
|
267
|
+
throw new SigningBootstrapRequiredError(SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
268
|
+
}
|
|
269
|
+
if (error instanceof SigningBootstrapRequiredError) {
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
logger.debug("[AUTH] Failed to bootstrap signing credentials", error);
|
|
273
|
+
if (opts.failFast) {
|
|
274
|
+
throw new SigningBootstrapRequiredError(SIGNING_BOOTSTRAP_REQUIRED_MESSAGE);
|
|
275
|
+
}
|
|
276
|
+
return credentials;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
222
280
|
function isNonInteractive() {
|
|
223
281
|
return !process.stdin.isTTY || !process.stdout.isTTY;
|
|
224
282
|
}
|
|
@@ -520,7 +578,7 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
520
578
|
} else {
|
|
521
579
|
logger.debug("[AUTH] Using existing credentials");
|
|
522
580
|
}
|
|
523
|
-
credentials = await ensureSigningCredentials(credentials);
|
|
581
|
+
credentials = await ensureSigningCredentials(credentials, { failFast: newAuth });
|
|
524
582
|
const currentSettings = await readSettings();
|
|
525
583
|
const settings = newAuth || !currentSettings.machineId ? await updateSettings(async (s) => ({
|
|
526
584
|
...s,
|
|
@@ -529,53 +587,6 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
529
587
|
logger.debug(`[AUTH] Machine ID: ${settings.machineId}`);
|
|
530
588
|
return { credentials, machineId: settings.machineId };
|
|
531
589
|
}
|
|
532
|
-
async function ensureSigningCredentials(credentials) {
|
|
533
|
-
if (credentials.signing) {
|
|
534
|
-
return credentials;
|
|
535
|
-
}
|
|
536
|
-
try {
|
|
537
|
-
const response = await axios.post(`${configuration.serverUrl}/v1/auth/refresh`, {}, {
|
|
538
|
-
headers: buildAuthenticatedHeaders({
|
|
539
|
-
credentials,
|
|
540
|
-
method: "POST",
|
|
541
|
-
url: `${configuration.serverUrl}/v1/auth/refresh`,
|
|
542
|
-
body: {},
|
|
543
|
-
headers: {
|
|
544
|
-
"Content-Type": "application/json"
|
|
545
|
-
},
|
|
546
|
-
signRequest: false
|
|
547
|
-
})
|
|
548
|
-
});
|
|
549
|
-
if (!response.data?.success || !response.data?.token || !response.data?.signing) {
|
|
550
|
-
logger.debug("[AUTH] Signing bootstrap returned incomplete payload");
|
|
551
|
-
return credentials;
|
|
552
|
-
}
|
|
553
|
-
const upgradedCredentials = {
|
|
554
|
-
...credentials,
|
|
555
|
-
token: response.data.token,
|
|
556
|
-
signing: response.data.signing
|
|
557
|
-
};
|
|
558
|
-
if (upgradedCredentials.encryption.type === "legacy") {
|
|
559
|
-
await writeCredentialsLegacy({
|
|
560
|
-
secret: upgradedCredentials.encryption.secret,
|
|
561
|
-
token: upgradedCredentials.token,
|
|
562
|
-
signing: upgradedCredentials.signing
|
|
563
|
-
});
|
|
564
|
-
} else {
|
|
565
|
-
await writeCredentialsDataKey({
|
|
566
|
-
publicKey: upgradedCredentials.encryption.publicKey,
|
|
567
|
-
machineKey: upgradedCredentials.encryption.machineKey,
|
|
568
|
-
token: upgradedCredentials.token,
|
|
569
|
-
signing: upgradedCredentials.signing
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
logger.debug("[AUTH] Signing credentials bootstrapped successfully");
|
|
573
|
-
return upgradedCredentials;
|
|
574
|
-
} catch (error) {
|
|
575
|
-
logger.debug("[AUTH] Failed to bootstrap signing credentials", error);
|
|
576
|
-
return credentials;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
590
|
|
|
580
591
|
let caffeinateProcess = null;
|
|
581
592
|
function startCaffeinate() {
|
|
@@ -958,6 +969,32 @@ async function checkIfDaemonRunningAndCleanupStaleState() {
|
|
|
958
969
|
return false;
|
|
959
970
|
}
|
|
960
971
|
}
|
|
972
|
+
async function isDaemonControlServerResponsive(timeoutMs = 1e3) {
|
|
973
|
+
const state = await readDaemonState();
|
|
974
|
+
if (!state?.httpPort) {
|
|
975
|
+
logger.debug("[DAEMON CONTROL] No daemon state or control port found for readiness check");
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
process.kill(state.pid, 0);
|
|
980
|
+
} catch {
|
|
981
|
+
logger.debug("[DAEMON CONTROL] Daemon PID not running during readiness check, cleaning up state");
|
|
982
|
+
await cleanupDaemonState();
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
try {
|
|
986
|
+
const response = await fetch(`http://127.0.0.1:${state.httpPort}/list`, {
|
|
987
|
+
method: "POST",
|
|
988
|
+
headers: { "Content-Type": "application/json" },
|
|
989
|
+
body: JSON.stringify({}),
|
|
990
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
991
|
+
});
|
|
992
|
+
return response.ok;
|
|
993
|
+
} catch (error) {
|
|
994
|
+
logger.debug("[DAEMON CONTROL] Daemon control server not ready yet", error);
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
961
998
|
async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
962
999
|
logger.debug("[DAEMON CONTROL] Checking if daemon is running same version");
|
|
963
1000
|
const runningDaemon = await checkIfDaemonRunningAndCleanupStaleState();
|
|
@@ -975,7 +1012,10 @@ async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
|
975
1012
|
const packageJson = JSON.parse(readFileSync$1(packageJsonPath, "utf-8"));
|
|
976
1013
|
const currentCliVersion = packageJson.version;
|
|
977
1014
|
logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
978
|
-
|
|
1015
|
+
if (currentCliVersion !== state.startedWithCliVersion) {
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
return await isDaemonControlServerResponsive();
|
|
979
1019
|
} catch (error) {
|
|
980
1020
|
logger.debug("[DAEMON CONTROL] Error checking daemon version", error);
|
|
981
1021
|
return false;
|
|
@@ -1192,7 +1232,7 @@ ${typeLabels[type] || type}:`));
|
|
|
1192
1232
|
}
|
|
1193
1233
|
if (filter === "all" && allProcesses.length > 1) {
|
|
1194
1234
|
console.log(chalk.bold("\n\u{1F4A1} Process Management"));
|
|
1195
|
-
console.log(chalk.gray("To clean up runaway processes:
|
|
1235
|
+
console.log(chalk.gray("To clean up runaway processes: hicloud doctor clean"));
|
|
1196
1236
|
}
|
|
1197
1237
|
} catch (error) {
|
|
1198
1238
|
console.log(chalk.red("\u274C Error checking daemon status"));
|
|
@@ -1374,7 +1414,8 @@ function startDaemonControlServer({
|
|
|
1374
1414
|
schema: {
|
|
1375
1415
|
body: z.object({
|
|
1376
1416
|
directory: z.string(),
|
|
1377
|
-
sessionId: z.string().optional()
|
|
1417
|
+
sessionId: z.string().optional(),
|
|
1418
|
+
agent: z.enum(["claude", "codex", "gemini"]).optional()
|
|
1378
1419
|
}),
|
|
1379
1420
|
response: {
|
|
1380
1421
|
200: z.object({
|
|
@@ -1395,9 +1436,9 @@ function startDaemonControlServer({
|
|
|
1395
1436
|
}
|
|
1396
1437
|
}
|
|
1397
1438
|
}, async (request, reply) => {
|
|
1398
|
-
const { directory, sessionId } = request.body;
|
|
1399
|
-
logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}`);
|
|
1400
|
-
const result = await spawnSession({ directory, sessionId });
|
|
1439
|
+
const { directory, sessionId, agent } = request.body;
|
|
1440
|
+
logger.debug(`[CONTROL SERVER] Spawn session request: dir=${directory}, sessionId=${sessionId || "new"}, agent=${agent || "claude"}`);
|
|
1441
|
+
const result = await spawnSession({ directory, sessionId, agent });
|
|
1401
1442
|
switch (result.type) {
|
|
1402
1443
|
case "success":
|
|
1403
1444
|
if (!result.sessionId) {
|
|
@@ -2912,13 +2953,13 @@ async function handleAuthCommand(args) {
|
|
|
2912
2953
|
}
|
|
2913
2954
|
function showAuthHelp() {
|
|
2914
2955
|
console.log(`
|
|
2915
|
-
${chalk.bold("
|
|
2956
|
+
${chalk.bold("hicloud auth")} - Authentication management
|
|
2916
2957
|
|
|
2917
2958
|
${chalk.bold("Usage:")}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2959
|
+
hicloud auth login [--force] Authenticate with Happy
|
|
2960
|
+
hicloud auth logout [--yes] Remove authentication and machine data
|
|
2961
|
+
hicloud auth status Show authentication status
|
|
2962
|
+
hicloud auth help Show this help message
|
|
2922
2963
|
|
|
2923
2964
|
${chalk.bold("Options:")}
|
|
2924
2965
|
--force Clear credentials, machine ID, and stop daemon before re-auth
|
|
@@ -2958,7 +2999,7 @@ async function handleAuthLogin(args) {
|
|
|
2958
2999
|
console.log(chalk.green("\u2713 Already authenticated"));
|
|
2959
3000
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
2960
3001
|
console.log(chalk.gray(` Host: ${os$1.hostname()}`));
|
|
2961
|
-
console.log(chalk.gray(` Use '
|
|
3002
|
+
console.log(chalk.gray(` Use 'hicloud auth login --force' to re-authenticate`));
|
|
2962
3003
|
return;
|
|
2963
3004
|
} else if (existingCreds && !settings?.machineId) {
|
|
2964
3005
|
console.log(chalk.yellow("\u26A0\uFE0F Credentials exist but machine ID is missing"));
|
|
@@ -2989,7 +3030,7 @@ async function handleAuthLogout(args = []) {
|
|
|
2989
3030
|
let confirmed = skipConfirm;
|
|
2990
3031
|
if (!confirmed) {
|
|
2991
3032
|
if (isNonInteractive) {
|
|
2992
|
-
throw new Error('Non-interactive terminal detected. Use "
|
|
3033
|
+
throw new Error('Non-interactive terminal detected. Use "hicloud auth logout --yes" to confirm.');
|
|
2993
3034
|
}
|
|
2994
3035
|
const rl = createInterface({
|
|
2995
3036
|
input: process.stdin,
|
|
@@ -3012,7 +3053,7 @@ async function handleAuthLogout(args = []) {
|
|
|
3012
3053
|
rmSync(happyDir, { recursive: true, force: true });
|
|
3013
3054
|
}
|
|
3014
3055
|
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
3015
|
-
console.log(chalk.gray(' Run "
|
|
3056
|
+
console.log(chalk.gray(' Run "hicloud auth login" to authenticate again'));
|
|
3016
3057
|
} catch (error) {
|
|
3017
3058
|
throw new Error(`Failed to logout: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3018
3059
|
}
|
|
@@ -3026,20 +3067,20 @@ async function handleAuthStatus() {
|
|
|
3026
3067
|
console.log(chalk.bold("\nAuthentication Status\n"));
|
|
3027
3068
|
if (!credentials) {
|
|
3028
3069
|
console.log(chalk.red("\u2717 Not authenticated"));
|
|
3029
|
-
console.log(chalk.gray(' Run "
|
|
3070
|
+
console.log(chalk.gray(' Run "hicloud auth login" to authenticate'));
|
|
3030
3071
|
return;
|
|
3031
3072
|
}
|
|
3032
3073
|
console.log(chalk.green("\u2713 Authenticated"));
|
|
3033
3074
|
const tokenPreview = credentials.token.substring(0, 30) + "...";
|
|
3034
3075
|
console.log(chalk.gray(` Token: ${tokenPreview}`));
|
|
3035
|
-
console.log(chalk.gray(` Request signing: ${credentials
|
|
3076
|
+
console.log(chalk.gray(` Request signing: ${describeSigningStatus(credentials)}`));
|
|
3036
3077
|
if (settings?.machineId) {
|
|
3037
3078
|
console.log(chalk.green("\u2713 Machine registered"));
|
|
3038
3079
|
console.log(chalk.gray(` Machine ID: ${settings.machineId}`));
|
|
3039
3080
|
console.log(chalk.gray(` Host: ${os$1.hostname()}`));
|
|
3040
3081
|
} else {
|
|
3041
3082
|
console.log(chalk.yellow("\u26A0\uFE0F Machine not registered"));
|
|
3042
|
-
console.log(chalk.gray(' Run "
|
|
3083
|
+
console.log(chalk.gray(' Run "hicloud auth login --force" to fix this'));
|
|
3043
3084
|
}
|
|
3044
3085
|
console.log(chalk.gray(`
|
|
3045
3086
|
Data directory: ${configuration.happyCloudHomeDir}`));
|
|
@@ -3054,6 +3095,12 @@ async function handleAuthStatus() {
|
|
|
3054
3095
|
console.log(chalk.gray("\u2717 Daemon not running"));
|
|
3055
3096
|
}
|
|
3056
3097
|
}
|
|
3098
|
+
function describeSigningStatus(credentials) {
|
|
3099
|
+
if (credentials.signing) {
|
|
3100
|
+
return "ready";
|
|
3101
|
+
}
|
|
3102
|
+
return "not initialized (server write requests may still fail until signing bootstrap succeeds)";
|
|
3103
|
+
}
|
|
3057
3104
|
|
|
3058
3105
|
const CLIENT_ID$2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
3059
3106
|
const AUTH_BASE_URL = "https://auth.openai.com";
|
|
@@ -3570,14 +3617,14 @@ async function handleConnectCommand(args) {
|
|
|
3570
3617
|
}
|
|
3571
3618
|
function showConnectHelp() {
|
|
3572
3619
|
console.log(`
|
|
3573
|
-
${chalk.bold("
|
|
3620
|
+
${chalk.bold("hicloud connect")} - Connect AI vendor API keys to Happy cloud
|
|
3574
3621
|
|
|
3575
3622
|
${chalk.bold("Usage:")}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3623
|
+
hicloud connect codex Store your Codex API key in Happy cloud
|
|
3624
|
+
hicloud connect claude Store your Anthropic API key in Happy cloud
|
|
3625
|
+
hicloud connect gemini Store your Gemini API key in Happy cloud
|
|
3626
|
+
hicloud connect status Show connection status for all vendors
|
|
3627
|
+
hicloud connect help Show this help message
|
|
3581
3628
|
|
|
3582
3629
|
${chalk.bold("Description:")}
|
|
3583
3630
|
The connect command allows you to securely store your AI vendor API keys
|
|
@@ -3585,13 +3632,13 @@ ${chalk.bold("Description:")}
|
|
|
3585
3632
|
without exposing your API keys locally.
|
|
3586
3633
|
|
|
3587
3634
|
${chalk.bold("Examples:")}
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3635
|
+
hicloud connect codex
|
|
3636
|
+
hicloud connect claude
|
|
3637
|
+
hicloud connect gemini
|
|
3638
|
+
hicloud connect status
|
|
3592
3639
|
|
|
3593
3640
|
${chalk.bold("Notes:")}
|
|
3594
|
-
\u2022 You must be authenticated with Happy first (run '
|
|
3641
|
+
\u2022 You must be authenticated with Happy first (run 'hicloud auth login')
|
|
3595
3642
|
\u2022 API keys are encrypted and stored securely in Happy cloud
|
|
3596
3643
|
\u2022 You can manage your stored keys at https://happycloudcode.imou.com
|
|
3597
3644
|
`);
|
|
@@ -3603,7 +3650,7 @@ async function handleConnectVendor(vendor, displayName) {
|
|
|
3603
3650
|
const credentials = await readCredentials();
|
|
3604
3651
|
if (!credentials) {
|
|
3605
3652
|
console.log(chalk.yellow("\u26A0\uFE0F Not authenticated with Happy"));
|
|
3606
|
-
console.log(chalk.gray(' Please run "
|
|
3653
|
+
console.log(chalk.gray(' Please run "hicloud auth login" first'));
|
|
3607
3654
|
process.exit(1);
|
|
3608
3655
|
}
|
|
3609
3656
|
const api = await ApiClient.create(credentials);
|
|
@@ -3635,7 +3682,7 @@ async function handleConnectStatus() {
|
|
|
3635
3682
|
const credentials = await readCredentials();
|
|
3636
3683
|
if (!credentials) {
|
|
3637
3684
|
console.log(chalk.yellow("\u26A0\uFE0F Not authenticated with Happy"));
|
|
3638
|
-
console.log(chalk.gray(' Please run "
|
|
3685
|
+
console.log(chalk.gray(' Please run "hicloud auth login" first'));
|
|
3639
3686
|
process.exit(1);
|
|
3640
3687
|
}
|
|
3641
3688
|
const api = await ApiClient.create(credentials);
|
|
@@ -3670,8 +3717,8 @@ async function handleConnectStatus() {
|
|
|
3670
3717
|
}
|
|
3671
3718
|
}
|
|
3672
3719
|
console.log("");
|
|
3673
|
-
console.log(chalk.gray("To connect a vendor, run:
|
|
3674
|
-
console.log(chalk.gray("Example:
|
|
3720
|
+
console.log(chalk.gray("To connect a vendor, run: hicloud connect <vendor>"));
|
|
3721
|
+
console.log(chalk.gray("Example: hicloud connect gemini"));
|
|
3675
3722
|
console.log("");
|
|
3676
3723
|
}
|
|
3677
3724
|
function updateLocalGeminiCredentials(tokens) {
|
|
@@ -3696,19 +3743,17 @@ function updateLocalGeminiCredentials(tokens) {
|
|
|
3696
3743
|
}
|
|
3697
3744
|
}
|
|
3698
3745
|
|
|
3699
|
-
function getProjectPath(workingDirectory) {
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
const projectId = resolved.replace(/[\\\/\.: _]/g, "-");
|
|
3705
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir$1(), ".claude");
|
|
3746
|
+
function getProjectPath(workingDirectory, claudeConfigDirOverride) {
|
|
3747
|
+
const projectId = resolve$1(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
3748
|
+
const claudeConfigDirRaw = process.env.CLAUDE_CONFIG_DIR ?? "";
|
|
3749
|
+
const claudeConfigDirTrimmed = claudeConfigDirRaw.trim();
|
|
3750
|
+
const claudeConfigDir = claudeConfigDirTrimmed ? claudeConfigDirTrimmed : join(homedir$1(), ".claude");
|
|
3706
3751
|
return join(claudeConfigDir, "projects", projectId);
|
|
3707
3752
|
}
|
|
3708
3753
|
|
|
3709
|
-
function claudeCheckSession(sessionId, path) {
|
|
3754
|
+
function claudeCheckSession(sessionId, path, transcriptPath) {
|
|
3710
3755
|
const projectDir = getProjectPath(path);
|
|
3711
|
-
const sessionFile = join(projectDir, `${sessionId}.jsonl`);
|
|
3756
|
+
const sessionFile = transcriptPath ?? join(projectDir, `${sessionId}.jsonl`);
|
|
3712
3757
|
const sessionExists = existsSync(sessionFile);
|
|
3713
3758
|
if (!sessionExists) {
|
|
3714
3759
|
logger.debug(`[claudeCheckSession] Path ${sessionFile} does not exist`);
|
|
@@ -4056,6 +4101,77 @@ class AgentRegistry {
|
|
|
4056
4101
|
}
|
|
4057
4102
|
const agentRegistry = new AgentRegistry();
|
|
4058
4103
|
|
|
4104
|
+
const PREFERRED_MESSAGE_FIELDS = ["stderr", "error", "message", "detail", "stdout", "text", "reason"];
|
|
4105
|
+
function safeSerializeDisplayValue(value) {
|
|
4106
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
4107
|
+
try {
|
|
4108
|
+
const serialized = JSON.stringify(value, (_key, currentValue) => {
|
|
4109
|
+
if (typeof currentValue === "bigint") {
|
|
4110
|
+
return currentValue.toString();
|
|
4111
|
+
}
|
|
4112
|
+
if (currentValue instanceof Error) {
|
|
4113
|
+
return {
|
|
4114
|
+
name: currentValue.name,
|
|
4115
|
+
message: currentValue.message,
|
|
4116
|
+
stack: currentValue.stack
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
4119
|
+
if (typeof currentValue === "object" && currentValue !== null) {
|
|
4120
|
+
if (seen.has(currentValue)) {
|
|
4121
|
+
return "[Circular]";
|
|
4122
|
+
}
|
|
4123
|
+
seen.add(currentValue);
|
|
4124
|
+
}
|
|
4125
|
+
return currentValue;
|
|
4126
|
+
});
|
|
4127
|
+
return serialized ?? String(value);
|
|
4128
|
+
} catch {
|
|
4129
|
+
return String(value);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
function formatDisplayMessage(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
4133
|
+
if (typeof value === "string") {
|
|
4134
|
+
return value;
|
|
4135
|
+
}
|
|
4136
|
+
if (value instanceof Error) {
|
|
4137
|
+
return value.message || String(value);
|
|
4138
|
+
}
|
|
4139
|
+
if (value === null || value === void 0) {
|
|
4140
|
+
return "";
|
|
4141
|
+
}
|
|
4142
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
4143
|
+
return value.toString("utf8");
|
|
4144
|
+
}
|
|
4145
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
4146
|
+
return String(value);
|
|
4147
|
+
}
|
|
4148
|
+
if (typeof value === "object") {
|
|
4149
|
+
if (seen.has(value)) {
|
|
4150
|
+
return "[Circular]";
|
|
4151
|
+
}
|
|
4152
|
+
seen.add(value);
|
|
4153
|
+
const record = value;
|
|
4154
|
+
for (const field of PREFERRED_MESSAGE_FIELDS) {
|
|
4155
|
+
if (!(field in record)) {
|
|
4156
|
+
continue;
|
|
4157
|
+
}
|
|
4158
|
+
const formattedField = formatDisplayMessage(record[field], seen).trim();
|
|
4159
|
+
if (formattedField.length > 0) {
|
|
4160
|
+
return formattedField;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
return safeSerializeDisplayValue(value);
|
|
4164
|
+
}
|
|
4165
|
+
return String(value);
|
|
4166
|
+
}
|
|
4167
|
+
function truncateDisplayMessage(value, maxLength) {
|
|
4168
|
+
const formatted = formatDisplayMessage(value);
|
|
4169
|
+
if (formatted.length <= maxLength) {
|
|
4170
|
+
return formatted;
|
|
4171
|
+
}
|
|
4172
|
+
return `${formatted.substring(0, maxLength)}...`;
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4059
4175
|
const DEFAULT_TIMEOUTS = {
|
|
4060
4176
|
/** Default initialization timeout: 60 seconds */
|
|
4061
4177
|
init: 6e4,
|
|
@@ -4350,7 +4466,7 @@ class CodexTransport extends DefaultTransport {
|
|
|
4350
4466
|
return 800;
|
|
4351
4467
|
}
|
|
4352
4468
|
}
|
|
4353
|
-
|
|
4469
|
+
new CodexTransport();
|
|
4354
4470
|
|
|
4355
4471
|
class ClaudeTransport extends DefaultTransport {
|
|
4356
4472
|
constructor() {
|
|
@@ -4684,6 +4800,70 @@ const RETRY_CONFIG = {
|
|
|
4684
4800
|
/** Maximum delay between retries in ms */
|
|
4685
4801
|
maxDelayMs: 5e3
|
|
4686
4802
|
};
|
|
4803
|
+
function getSessionUpdates(params) {
|
|
4804
|
+
const notification = params;
|
|
4805
|
+
const updates = Array.isArray(notification.updates) ? notification.updates.filter((update) => Boolean(update && typeof update === "object")) : [];
|
|
4806
|
+
if (updates.length > 0) {
|
|
4807
|
+
return updates;
|
|
4808
|
+
}
|
|
4809
|
+
if (notification.update && typeof notification.update === "object") {
|
|
4810
|
+
return [notification.update];
|
|
4811
|
+
}
|
|
4812
|
+
return [];
|
|
4813
|
+
}
|
|
4814
|
+
function asNonNegativeFiniteNumber(value) {
|
|
4815
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
4816
|
+
}
|
|
4817
|
+
function extractUsageTokens(record) {
|
|
4818
|
+
const used = asNonNegativeFiniteNumber(record.used);
|
|
4819
|
+
const size = asNonNegativeFiniteNumber(record.size);
|
|
4820
|
+
if (used != null || size != null) {
|
|
4821
|
+
const tokens2 = {
|
|
4822
|
+
total: used ?? 0
|
|
4823
|
+
};
|
|
4824
|
+
if (used != null) tokens2.used = used;
|
|
4825
|
+
if (size != null) tokens2.size = size;
|
|
4826
|
+
return tokens2;
|
|
4827
|
+
}
|
|
4828
|
+
const input = asNonNegativeFiniteNumber(record.input_tokens) ?? asNonNegativeFiniteNumber(record.inputTokens) ?? asNonNegativeFiniteNumber(record.prompt_tokens) ?? asNonNegativeFiniteNumber(record.promptTokens);
|
|
4829
|
+
const output = asNonNegativeFiniteNumber(record.output_tokens) ?? asNonNegativeFiniteNumber(record.outputTokens) ?? asNonNegativeFiniteNumber(record.completion_tokens) ?? asNonNegativeFiniteNumber(record.completionTokens);
|
|
4830
|
+
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);
|
|
4831
|
+
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);
|
|
4832
|
+
const thought = asNonNegativeFiniteNumber(record.thought_tokens) ?? asNonNegativeFiniteNumber(record.thoughtTokens);
|
|
4833
|
+
const totalFromPayload = asNonNegativeFiniteNumber(record.total_tokens) ?? asNonNegativeFiniteNumber(record.totalTokens);
|
|
4834
|
+
const anyPresent = totalFromPayload != null || input != null || output != null || cacheRead != null || cacheCreation != null || thought != null;
|
|
4835
|
+
if (!anyPresent) {
|
|
4836
|
+
return null;
|
|
4837
|
+
}
|
|
4838
|
+
const total = totalFromPayload ?? (input ?? 0) + (output ?? 0) + (cacheRead ?? 0) + (cacheCreation ?? 0) + (thought ?? 0);
|
|
4839
|
+
const tokens = { total };
|
|
4840
|
+
if (input != null) tokens.input = input;
|
|
4841
|
+
if (output != null) tokens.output = output;
|
|
4842
|
+
if (cacheRead != null) tokens.cache_read = cacheRead;
|
|
4843
|
+
if (cacheCreation != null) tokens.cache_creation = cacheCreation;
|
|
4844
|
+
if (thought != null) tokens.thought = thought;
|
|
4845
|
+
return tokens;
|
|
4846
|
+
}
|
|
4847
|
+
function isApprovalOption(option) {
|
|
4848
|
+
if (option.optionId === "proceed_once" || option.optionId === "proceed_always" || option.optionId === "cancel") {
|
|
4849
|
+
return true;
|
|
4850
|
+
}
|
|
4851
|
+
const searchable = `${option.name ?? ""} ${option.label ?? ""}`.toLowerCase();
|
|
4852
|
+
return searchable.includes("once") || searchable.includes("always") || searchable.includes("cancel");
|
|
4853
|
+
}
|
|
4854
|
+
function isSelectionPermissionRequest(params) {
|
|
4855
|
+
if (Array.isArray(params.codex_command) && params.codex_command.length > 0) {
|
|
4856
|
+
return false;
|
|
4857
|
+
}
|
|
4858
|
+
if (params.requestedSchema?.properties?.optionId) {
|
|
4859
|
+
return true;
|
|
4860
|
+
}
|
|
4861
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
4862
|
+
if (options.length === 0) {
|
|
4863
|
+
return false;
|
|
4864
|
+
}
|
|
4865
|
+
return !options.every((option) => isApprovalOption(option));
|
|
4866
|
+
}
|
|
4687
4867
|
function nodeToWebStreams(stdin, stdout) {
|
|
4688
4868
|
const writable = new WritableStream({
|
|
4689
4869
|
write(chunk) {
|
|
@@ -4747,7 +4927,7 @@ async function withRetry(operation, options) {
|
|
|
4747
4927
|
try {
|
|
4748
4928
|
return await operation();
|
|
4749
4929
|
} catch (error) {
|
|
4750
|
-
lastError =
|
|
4930
|
+
lastError = normalizeAcpError(error);
|
|
4751
4931
|
if (attempt < options.maxAttempts) {
|
|
4752
4932
|
const delayMs = Math.min(
|
|
4753
4933
|
options.baseDelayMs * Math.pow(2, attempt - 1),
|
|
@@ -4761,6 +4941,49 @@ async function withRetry(operation, options) {
|
|
|
4761
4941
|
}
|
|
4762
4942
|
throw lastError;
|
|
4763
4943
|
}
|
|
4944
|
+
function formatAcpErrorMessage(error) {
|
|
4945
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4946
|
+
return error.message;
|
|
4947
|
+
}
|
|
4948
|
+
if (typeof error === "object" && error !== null) {
|
|
4949
|
+
const record = error;
|
|
4950
|
+
const message = formatDisplayMessage(error).trim();
|
|
4951
|
+
const code = record.code;
|
|
4952
|
+
const status = record.status;
|
|
4953
|
+
const prefix = [
|
|
4954
|
+
code !== void 0 && code !== null ? `[code=${String(code)}]` : "",
|
|
4955
|
+
status !== void 0 && status !== null ? `[status=${String(status)}]` : ""
|
|
4956
|
+
].filter(Boolean).join(" ");
|
|
4957
|
+
if (message.length > 0) {
|
|
4958
|
+
return prefix ? `${prefix} ${message}` : message;
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
const fallback = formatDisplayMessage(error).trim();
|
|
4962
|
+
return fallback || "Unknown ACP error";
|
|
4963
|
+
}
|
|
4964
|
+
function normalizeAcpError(error) {
|
|
4965
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4966
|
+
return error;
|
|
4967
|
+
}
|
|
4968
|
+
const normalized = new Error(formatAcpErrorMessage(error));
|
|
4969
|
+
normalized.name = error instanceof Error ? error.name : "Error";
|
|
4970
|
+
if (error && typeof error === "object") {
|
|
4971
|
+
const { message: _message, ...rest } = error;
|
|
4972
|
+
Object.assign(normalized, rest);
|
|
4973
|
+
}
|
|
4974
|
+
return normalized;
|
|
4975
|
+
}
|
|
4976
|
+
function enrichAcpError(error, stderrExcerpt) {
|
|
4977
|
+
const normalized = normalizeAcpError(error);
|
|
4978
|
+
if (!stderrExcerpt.trim()) {
|
|
4979
|
+
return normalized;
|
|
4980
|
+
}
|
|
4981
|
+
const existingStderr = normalized.stderr;
|
|
4982
|
+
if (typeof existingStderr !== "string" || existingStderr.trim().length === 0) {
|
|
4983
|
+
Object.assign(normalized, { stderr: stderrExcerpt });
|
|
4984
|
+
}
|
|
4985
|
+
return normalized;
|
|
4986
|
+
}
|
|
4764
4987
|
class AcpBackend {
|
|
4765
4988
|
constructor(options) {
|
|
4766
4989
|
this.options = options;
|
|
@@ -4788,6 +5011,21 @@ class AcpBackend {
|
|
|
4788
5011
|
idleTimeout = null;
|
|
4789
5012
|
/** Transport handler for agent-specific behavior */
|
|
4790
5013
|
transport;
|
|
5014
|
+
/** Keep a short rolling stderr buffer so startup failures can surface the real cause. */
|
|
5015
|
+
recentStderrLines = [];
|
|
5016
|
+
recordRecentStderr(text) {
|
|
5017
|
+
const normalized = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
5018
|
+
if (normalized.length === 0) {
|
|
5019
|
+
return;
|
|
5020
|
+
}
|
|
5021
|
+
this.recentStderrLines.push(...normalized);
|
|
5022
|
+
if (this.recentStderrLines.length > 12) {
|
|
5023
|
+
this.recentStderrLines = this.recentStderrLines.slice(-12);
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
getRecentStderrExcerpt() {
|
|
5027
|
+
return this.recentStderrLines.slice(-6).join("\n");
|
|
5028
|
+
}
|
|
4791
5029
|
onMessage(handler) {
|
|
4792
5030
|
this.listeners.push(handler);
|
|
4793
5031
|
}
|
|
@@ -4815,6 +5053,7 @@ class AcpBackend {
|
|
|
4815
5053
|
this.emit({ type: "status", status: "starting" });
|
|
4816
5054
|
try {
|
|
4817
5055
|
logger.debug(`[AcpBackend] Starting session: ${sessionId}`);
|
|
5056
|
+
this.recentStderrLines = [];
|
|
4818
5057
|
const args = this.options.args || [];
|
|
4819
5058
|
if (process.platform === "win32") {
|
|
4820
5059
|
const fullCommand = [this.options.command, ...args].join(" ");
|
|
@@ -4841,6 +5080,7 @@ class AcpBackend {
|
|
|
4841
5080
|
this.process.stderr.on("data", (data) => {
|
|
4842
5081
|
const text = data.toString();
|
|
4843
5082
|
if (!text.trim()) return;
|
|
5083
|
+
this.recordRecentStderr(text);
|
|
4844
5084
|
const hasActiveInvestigation = this.transport.isInvestigationTool ? Array.from(this.activeToolCalls).some((id) => this.transport.isInvestigationTool(id)) : false;
|
|
4845
5085
|
const context = {
|
|
4846
5086
|
activeToolCalls: this.activeToolCalls,
|
|
@@ -4951,6 +5191,7 @@ class AcpBackend {
|
|
|
4951
5191
|
}
|
|
4952
5192
|
this.toolCallCountSincePrompt++;
|
|
4953
5193
|
const options = extendedParams.options || [];
|
|
5194
|
+
const isSelectionRequest = isSelectionPermissionRequest(extendedParams);
|
|
4954
5195
|
logger.debug(`[AcpBackend] Permission request: tool=${toolName}, toolCallId=${toolCallId}, input=`, JSON.stringify(input));
|
|
4955
5196
|
logger.debug(`[AcpBackend] Permission request params structure:`, JSON.stringify({
|
|
4956
5197
|
hasToolCall: !!toolCall,
|
|
@@ -4959,6 +5200,42 @@ class AcpBackend {
|
|
|
4959
5200
|
paramsKind: extendedParams.kind,
|
|
4960
5201
|
paramsKeys: Object.keys(params)
|
|
4961
5202
|
}, null, 2));
|
|
5203
|
+
if (isSelectionRequest) {
|
|
5204
|
+
const selectionOptions = options.reduce((acc, option) => {
|
|
5205
|
+
if (!option.optionId) {
|
|
5206
|
+
return acc;
|
|
5207
|
+
}
|
|
5208
|
+
const displayOption = option;
|
|
5209
|
+
acc.push({
|
|
5210
|
+
optionId: option.optionId,
|
|
5211
|
+
label: displayOption.label || option.name || option.optionId,
|
|
5212
|
+
description: displayOption.description || option.name || option.optionId
|
|
5213
|
+
});
|
|
5214
|
+
return acc;
|
|
5215
|
+
}, []);
|
|
5216
|
+
if (selectionOptions.length === 0) {
|
|
5217
|
+
logger.debug("[AcpBackend] Selection request has no valid options, cancelling");
|
|
5218
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5219
|
+
}
|
|
5220
|
+
if (!this.options.selectionHandler) {
|
|
5221
|
+
logger.debug("[AcpBackend] No selection handler configured, cancelling selection request");
|
|
5222
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5223
|
+
}
|
|
5224
|
+
try {
|
|
5225
|
+
const selectionMessage = typeof extendedParams.message === "string" && extendedParams.message.trim().length > 0 ? extendedParams.message : formatDisplayMessage(input).trim() || toolName;
|
|
5226
|
+
const requestId = extendedParams.codex_event_id || extendedParams.codex_elicitation || toolCallId;
|
|
5227
|
+
const response = await this.options.selectionHandler.handleSelection({
|
|
5228
|
+
id: requestId,
|
|
5229
|
+
message: selectionMessage,
|
|
5230
|
+
options: selectionOptions,
|
|
5231
|
+
defaultOptionId: extendedParams.requestedSchema?.properties?.optionId?.default
|
|
5232
|
+
});
|
|
5233
|
+
return { outcome: { outcome: "selected", optionId: response.optionId } };
|
|
5234
|
+
} catch (error) {
|
|
5235
|
+
logger.debug("[AcpBackend] Error in selection handler:", error);
|
|
5236
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
4962
5239
|
this.emit({
|
|
4963
5240
|
type: "permission-request",
|
|
4964
5241
|
id: permissionId,
|
|
@@ -5132,18 +5409,19 @@ class AcpBackend {
|
|
|
5132
5409
|
if (initialPrompt) {
|
|
5133
5410
|
this.sendPrompt(sessionId, initialPrompt).catch((error) => {
|
|
5134
5411
|
logger.debug("[AcpBackend] Error sending initial prompt:", error);
|
|
5135
|
-
this.emit({ type: "status", status: "error", detail:
|
|
5412
|
+
this.emit({ type: "status", status: "error", detail: formatAcpErrorMessage(error) });
|
|
5136
5413
|
});
|
|
5137
5414
|
}
|
|
5138
5415
|
return { sessionId };
|
|
5139
5416
|
} catch (error) {
|
|
5140
|
-
|
|
5417
|
+
const enrichedError = enrichAcpError(error, this.getRecentStderrExcerpt());
|
|
5418
|
+
logger.debug("[AcpBackend] Error starting session:", enrichedError);
|
|
5141
5419
|
this.emit({
|
|
5142
5420
|
type: "status",
|
|
5143
5421
|
status: "error",
|
|
5144
|
-
detail:
|
|
5422
|
+
detail: formatAcpErrorMessage(enrichedError)
|
|
5145
5423
|
});
|
|
5146
|
-
throw
|
|
5424
|
+
throw enrichedError;
|
|
5147
5425
|
}
|
|
5148
5426
|
}
|
|
5149
5427
|
/**
|
|
@@ -5174,51 +5452,73 @@ class AcpBackend {
|
|
|
5174
5452
|
}
|
|
5175
5453
|
};
|
|
5176
5454
|
}
|
|
5455
|
+
emitUsageTelemetry(payload, source) {
|
|
5456
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
5457
|
+
return false;
|
|
5458
|
+
}
|
|
5459
|
+
const tokens = extractUsageTokens(payload);
|
|
5460
|
+
if (!tokens) {
|
|
5461
|
+
return false;
|
|
5462
|
+
}
|
|
5463
|
+
this.emit({
|
|
5464
|
+
type: "token-count",
|
|
5465
|
+
key: source,
|
|
5466
|
+
tokens,
|
|
5467
|
+
source
|
|
5468
|
+
});
|
|
5469
|
+
return true;
|
|
5470
|
+
}
|
|
5177
5471
|
handleSessionUpdate(params) {
|
|
5178
|
-
const
|
|
5179
|
-
|
|
5180
|
-
if (!update) {
|
|
5472
|
+
const updates = getSessionUpdates(params);
|
|
5473
|
+
if (updates.length === 0) {
|
|
5181
5474
|
logger.debug("[AcpBackend] Received session update without update field:", params);
|
|
5182
5475
|
return;
|
|
5183
5476
|
}
|
|
5184
|
-
const
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5477
|
+
for (const update of updates) {
|
|
5478
|
+
const sessionUpdateType = update.sessionUpdate;
|
|
5479
|
+
if (sessionUpdateType !== "agent_message_chunk") {
|
|
5480
|
+
logger.debug(`[AcpBackend] Received session update: ${sessionUpdateType}`, JSON.stringify({
|
|
5481
|
+
sessionUpdate: sessionUpdateType,
|
|
5482
|
+
toolCallId: update.toolCallId,
|
|
5483
|
+
status: update.status,
|
|
5484
|
+
kind: update.kind,
|
|
5485
|
+
hasContent: !!update.content,
|
|
5486
|
+
hasLocations: !!update.locations
|
|
5487
|
+
}, null, 2));
|
|
5488
|
+
}
|
|
5489
|
+
const ctx = this.createHandlerContext();
|
|
5490
|
+
if (sessionUpdateType === "agent_message_chunk") {
|
|
5491
|
+
handleAgentMessageChunk(update, ctx);
|
|
5492
|
+
continue;
|
|
5493
|
+
}
|
|
5494
|
+
if (sessionUpdateType === "tool_call_update") {
|
|
5495
|
+
const result = handleToolCallUpdate(update, ctx);
|
|
5496
|
+
if (result.toolCallCountSincePrompt !== void 0) {
|
|
5497
|
+
this.toolCallCountSincePrompt = result.toolCallCountSincePrompt;
|
|
5498
|
+
}
|
|
5499
|
+
continue;
|
|
5500
|
+
}
|
|
5501
|
+
if (sessionUpdateType === "agent_thought_chunk") {
|
|
5502
|
+
handleAgentThoughtChunk(update, ctx);
|
|
5503
|
+
continue;
|
|
5504
|
+
}
|
|
5505
|
+
if (sessionUpdateType === "tool_call") {
|
|
5506
|
+
handleToolCall(update, ctx);
|
|
5507
|
+
continue;
|
|
5508
|
+
}
|
|
5509
|
+
if (sessionUpdateType === "usage_update") {
|
|
5510
|
+
this.emitUsageTelemetry(update, "acp-usage-update");
|
|
5511
|
+
continue;
|
|
5512
|
+
}
|
|
5513
|
+
const handledLegacy = handleLegacyMessageChunk(update, ctx).handled;
|
|
5514
|
+
const handledPlan = handlePlanUpdate(update, ctx).handled;
|
|
5515
|
+
const handledThinking = handleThinkingUpdate(update, ctx).handled;
|
|
5516
|
+
const handledUsage = this.emitUsageTelemetry(update.usage, "acp-session-usage");
|
|
5517
|
+
const updateTypeStr = sessionUpdateType;
|
|
5518
|
+
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call", "usage_update"];
|
|
5519
|
+
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !handledLegacy && !handledPlan && !handledThinking && !handledUsage) {
|
|
5520
|
+
logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5204
5521
|
}
|
|
5205
|
-
return;
|
|
5206
|
-
}
|
|
5207
|
-
if (sessionUpdateType === "agent_thought_chunk") {
|
|
5208
|
-
handleAgentThoughtChunk(update, ctx);
|
|
5209
|
-
return;
|
|
5210
|
-
}
|
|
5211
|
-
if (sessionUpdateType === "tool_call") {
|
|
5212
|
-
handleToolCall(update, ctx);
|
|
5213
|
-
return;
|
|
5214
|
-
}
|
|
5215
|
-
handleLegacyMessageChunk(update, ctx);
|
|
5216
|
-
handlePlanUpdate(update, ctx);
|
|
5217
|
-
handleThinkingUpdate(update, ctx);
|
|
5218
|
-
const updateTypeStr = sessionUpdateType;
|
|
5219
|
-
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call"];
|
|
5220
|
-
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !update.messageChunk && !update.plan && !update.thinking) {
|
|
5221
|
-
logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5222
5522
|
}
|
|
5223
5523
|
}
|
|
5224
5524
|
// Promise resolver for waitForIdle - set when waiting for response to complete
|
|
@@ -5255,17 +5555,9 @@ class AcpBackend {
|
|
|
5255
5555
|
if (error instanceof Error) {
|
|
5256
5556
|
errorDetail = error.message;
|
|
5257
5557
|
} else if (typeof error === "object" && error !== null) {
|
|
5258
|
-
|
|
5259
|
-
const fallbackMessage = (typeof errObj.message === "string" ? errObj.message : void 0) || String(error);
|
|
5260
|
-
if (errObj.code !== void 0) {
|
|
5261
|
-
errorDetail = JSON.stringify({ code: errObj.code, message: fallbackMessage });
|
|
5262
|
-
} else if (typeof errObj.message === "string") {
|
|
5263
|
-
errorDetail = errObj.message;
|
|
5264
|
-
} else {
|
|
5265
|
-
errorDetail = String(error);
|
|
5266
|
-
}
|
|
5558
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5267
5559
|
} else {
|
|
5268
|
-
errorDetail =
|
|
5560
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5269
5561
|
}
|
|
5270
5562
|
this.emit({
|
|
5271
5563
|
type: "status",
|
|
@@ -5616,59 +5908,213 @@ function registerGeminiAgent() {
|
|
|
5616
5908
|
logger.debug("[Gemini] Registered with agent registry");
|
|
5617
5909
|
}
|
|
5618
5910
|
|
|
5619
|
-
function
|
|
5620
|
-
for (const
|
|
5621
|
-
|
|
5622
|
-
|
|
5911
|
+
function readFirstEnv(...names) {
|
|
5912
|
+
for (const name of names) {
|
|
5913
|
+
const raw = process.env[name];
|
|
5914
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
5915
|
+
return raw.trim();
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
return "";
|
|
5919
|
+
}
|
|
5920
|
+
function normalizeCommandPath(command) {
|
|
5921
|
+
if (isAbsolute(command)) {
|
|
5922
|
+
return command;
|
|
5923
|
+
}
|
|
5924
|
+
const resolved = resolve$1(process.cwd(), command);
|
|
5925
|
+
return existsSync(resolved) ? resolved : command;
|
|
5926
|
+
}
|
|
5927
|
+
function resolveCommandOnPath(command) {
|
|
5928
|
+
const pathValue = typeof process.env.PATH === "string" ? process.env.PATH : "";
|
|
5929
|
+
if (!pathValue) {
|
|
5930
|
+
return null;
|
|
5931
|
+
}
|
|
5932
|
+
const extensions = process.platform === "win32" ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter(Boolean) : [""];
|
|
5933
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
5934
|
+
const trimmedDir = dir.trim();
|
|
5935
|
+
if (!trimmedDir) {
|
|
5936
|
+
continue;
|
|
5937
|
+
}
|
|
5938
|
+
const directCandidate = join(trimmedDir, command);
|
|
5939
|
+
if (existsSync(directCandidate)) {
|
|
5940
|
+
return directCandidate;
|
|
5941
|
+
}
|
|
5942
|
+
if (process.platform !== "win32") {
|
|
5943
|
+
continue;
|
|
5944
|
+
}
|
|
5945
|
+
const hasExtension = /\.[^\\/]+$/.test(command);
|
|
5946
|
+
if (hasExtension) {
|
|
5947
|
+
continue;
|
|
5948
|
+
}
|
|
5949
|
+
for (const extension of extensions) {
|
|
5950
|
+
const candidate = join(trimmedDir, `${command}${extension.toLowerCase()}`);
|
|
5951
|
+
if (existsSync(candidate)) {
|
|
5623
5952
|
return candidate;
|
|
5624
5953
|
}
|
|
5625
|
-
} catch {
|
|
5626
5954
|
}
|
|
5627
5955
|
}
|
|
5628
5956
|
return null;
|
|
5629
5957
|
}
|
|
5630
|
-
function
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
const resolved = firstExistingPath([
|
|
5635
|
-
path.join(npmGlobalBin, "codex.cmd"),
|
|
5636
|
-
path.join(npmGlobalBin, "codex.ps1"),
|
|
5637
|
-
path.join(npmGlobalBin, "codex")
|
|
5638
|
-
]);
|
|
5639
|
-
if (resolved) {
|
|
5640
|
-
return resolved;
|
|
5641
|
-
}
|
|
5958
|
+
function readCodexAcpNpxMode() {
|
|
5959
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_NPX_MODE", "HAPPIER_CODEX_ACP_NPX_MODE").toLowerCase();
|
|
5960
|
+
if (raw === "auto" || raw === "never" || raw === "force") {
|
|
5961
|
+
return raw;
|
|
5642
5962
|
}
|
|
5643
|
-
return "
|
|
5963
|
+
return "auto";
|
|
5964
|
+
}
|
|
5965
|
+
function isBinOnPath(baseName) {
|
|
5966
|
+
return resolveCommandOnPath(baseName) !== null;
|
|
5644
5967
|
}
|
|
5645
|
-
function
|
|
5646
|
-
|
|
5968
|
+
function readCodexAcpConfigOverrides() {
|
|
5969
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_CONFIG_OVERRIDES", "HAPPIER_CODEX_ACP_CONFIG_OVERRIDES");
|
|
5970
|
+
return raw.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
5971
|
+
}
|
|
5972
|
+
function buildCodexAcpConfigArgs(options) {
|
|
5973
|
+
const args = [...options.baseArgs ?? []];
|
|
5974
|
+
const overrides = [...readCodexAcpConfigOverrides()];
|
|
5975
|
+
if (options.model) {
|
|
5976
|
+
overrides.push(`model=${JSON.stringify(options.model)}`);
|
|
5977
|
+
}
|
|
5978
|
+
if (options.approvalPolicy) {
|
|
5979
|
+
overrides.push(`approval_policy=${JSON.stringify(options.approvalPolicy)}`);
|
|
5980
|
+
}
|
|
5981
|
+
if (options.sandbox) {
|
|
5982
|
+
overrides.push(`sandbox_mode=${JSON.stringify(options.sandbox)}`);
|
|
5983
|
+
}
|
|
5984
|
+
for (const override of overrides) {
|
|
5985
|
+
args.push("-c", override);
|
|
5986
|
+
}
|
|
5987
|
+
return args;
|
|
5988
|
+
}
|
|
5989
|
+
function resolveNpxCommand() {
|
|
5990
|
+
return resolveCommandOnPath(process.platform === "win32" ? "npx.cmd" : "npx") ?? resolveCommandOnPath("npx") ?? (process.platform === "win32" ? "npx.cmd" : "npx");
|
|
5991
|
+
}
|
|
5992
|
+
function resolveCodexAcpSpawn(options = {}) {
|
|
5993
|
+
if (options.args) {
|
|
5994
|
+
const command = normalizeCommandPath(options.command || readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN") || "codex-acp");
|
|
5995
|
+
return {
|
|
5996
|
+
command,
|
|
5997
|
+
args: [...options.args]
|
|
5998
|
+
};
|
|
5999
|
+
}
|
|
6000
|
+
const directArgs = buildCodexAcpConfigArgs(options);
|
|
6001
|
+
if (options.command) {
|
|
6002
|
+
return {
|
|
6003
|
+
command: normalizeCommandPath(options.command),
|
|
6004
|
+
args: directArgs
|
|
6005
|
+
};
|
|
6006
|
+
}
|
|
6007
|
+
const envOverride = readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN");
|
|
6008
|
+
if (envOverride) {
|
|
6009
|
+
return {
|
|
6010
|
+
command: normalizeCommandPath(envOverride),
|
|
6011
|
+
args: directArgs
|
|
6012
|
+
};
|
|
6013
|
+
}
|
|
6014
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6015
|
+
const codexAcpOnPath = resolveCommandOnPath(process.platform === "win32" ? "codex-acp.cmd" : "codex-acp") ?? resolveCommandOnPath("codex-acp");
|
|
6016
|
+
if (npxMode !== "force" && codexAcpOnPath) {
|
|
6017
|
+
return {
|
|
6018
|
+
command: codexAcpOnPath,
|
|
6019
|
+
args: directArgs
|
|
6020
|
+
};
|
|
6021
|
+
}
|
|
6022
|
+
if (npxMode === "never") {
|
|
6023
|
+
return {
|
|
6024
|
+
command: process.platform === "win32" ? "codex-acp.cmd" : "codex-acp",
|
|
6025
|
+
args: directArgs
|
|
6026
|
+
};
|
|
6027
|
+
}
|
|
6028
|
+
return {
|
|
6029
|
+
command: resolveNpxCommand(),
|
|
6030
|
+
args: ["--prefer-offline", "-y", "@zed-industries/codex-acp", ...directArgs]
|
|
6031
|
+
};
|
|
6032
|
+
}
|
|
6033
|
+
function validateCodexAcpSpawn(options = {}) {
|
|
6034
|
+
let spawn;
|
|
6035
|
+
try {
|
|
6036
|
+
spawn = resolveCodexAcpSpawn(options);
|
|
6037
|
+
} catch (error) {
|
|
6038
|
+
return {
|
|
6039
|
+
ok: false,
|
|
6040
|
+
errorMessage: error instanceof Error ? error.message : "Codex ACP is enabled, but the command could not be resolved."
|
|
6041
|
+
};
|
|
6042
|
+
}
|
|
6043
|
+
const normalizedCommand = spawn.command.trim();
|
|
6044
|
+
const commandLower = normalizedCommand.toLowerCase();
|
|
6045
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6046
|
+
if (isAbsolute(normalizedCommand)) {
|
|
6047
|
+
if (!existsSync(normalizedCommand)) {
|
|
6048
|
+
return {
|
|
6049
|
+
ok: false,
|
|
6050
|
+
errorMessage: `Codex ACP is enabled, but the resolved command does not exist: ${normalizedCommand}`
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
return { ok: true, spawn };
|
|
6054
|
+
}
|
|
6055
|
+
if (commandLower.endsWith("npx") || commandLower.endsWith("npx.cmd")) {
|
|
6056
|
+
if (isBinOnPath("npx") || isBinOnPath("npx.cmd")) {
|
|
6057
|
+
return { ok: true, spawn };
|
|
6058
|
+
}
|
|
6059
|
+
return {
|
|
6060
|
+
ok: false,
|
|
6061
|
+
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."
|
|
6062
|
+
};
|
|
6063
|
+
}
|
|
6064
|
+
if (commandLower === "codex-acp" || commandLower === "codex-acp.cmd") {
|
|
6065
|
+
if (isBinOnPath("codex-acp") || isBinOnPath("codex-acp.cmd")) {
|
|
6066
|
+
return { ok: true, spawn };
|
|
6067
|
+
}
|
|
6068
|
+
return {
|
|
6069
|
+
ok: false,
|
|
6070
|
+
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."
|
|
6071
|
+
};
|
|
6072
|
+
}
|
|
6073
|
+
return { ok: true, spawn };
|
|
5647
6074
|
}
|
|
5648
6075
|
|
|
5649
|
-
|
|
5650
|
-
|
|
6076
|
+
class CodexAcpTransport extends CodexTransport {
|
|
6077
|
+
constructor(initTimeoutMs) {
|
|
6078
|
+
super();
|
|
6079
|
+
this.initTimeoutMs = initTimeoutMs;
|
|
6080
|
+
}
|
|
6081
|
+
getInitTimeout() {
|
|
6082
|
+
return this.initTimeoutMs;
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
function resolveCodexTransport(command) {
|
|
6086
|
+
if (/npx(?:\.cmd)?$/i.test(command)) {
|
|
6087
|
+
return new CodexAcpTransport(18e4);
|
|
6088
|
+
}
|
|
6089
|
+
return new CodexAcpTransport(6e4);
|
|
5651
6090
|
}
|
|
5652
6091
|
function createCodexBackend(options) {
|
|
5653
|
-
const
|
|
5654
|
-
|
|
6092
|
+
const spawn = resolveCodexAcpSpawn({
|
|
6093
|
+
command: options.command,
|
|
6094
|
+
args: options.args,
|
|
6095
|
+
baseArgs: options.baseArgs,
|
|
6096
|
+
model: options.model,
|
|
6097
|
+
sandbox: options.sandbox,
|
|
6098
|
+
approvalPolicy: options.approvalPolicy
|
|
6099
|
+
});
|
|
5655
6100
|
const backendOptions = {
|
|
5656
6101
|
agentName: "codex",
|
|
5657
6102
|
cwd: options.cwd,
|
|
5658
|
-
command,
|
|
5659
|
-
args,
|
|
6103
|
+
command: spawn.command,
|
|
6104
|
+
args: spawn.args,
|
|
5660
6105
|
env: {
|
|
5661
6106
|
...options.env,
|
|
5662
6107
|
NODE_ENV: "production"
|
|
5663
6108
|
},
|
|
5664
6109
|
mcpServers: options.mcpServers,
|
|
5665
6110
|
permissionHandler: options.permissionHandler,
|
|
5666
|
-
|
|
6111
|
+
selectionHandler: options.selectionHandler,
|
|
6112
|
+
transportHandler: resolveCodexTransport(spawn.command)
|
|
5667
6113
|
};
|
|
5668
6114
|
return {
|
|
5669
6115
|
backend: new AcpBackend(backendOptions),
|
|
5670
|
-
command,
|
|
5671
|
-
args
|
|
6116
|
+
command: spawn.command,
|
|
6117
|
+
args: spawn.args
|
|
5672
6118
|
};
|
|
5673
6119
|
}
|
|
5674
6120
|
function registerCodexAgent() {
|
|
@@ -5854,17 +6300,26 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
5854
6300
|
env: process.env
|
|
5855
6301
|
});
|
|
5856
6302
|
daemonProcess.unref();
|
|
5857
|
-
|
|
6303
|
+
for (let i = 0; i < 100; i++) {
|
|
6304
|
+
if (await isDaemonRunningCurrentlyInstalledHappyVersion()) {
|
|
6305
|
+
return;
|
|
6306
|
+
}
|
|
6307
|
+
if (await isDaemonControlServerResponsive(500)) {
|
|
6308
|
+
return;
|
|
6309
|
+
}
|
|
6310
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
6311
|
+
}
|
|
6312
|
+
throw new Error("Failed to start Happy background service.");
|
|
5858
6313
|
}
|
|
5859
6314
|
async function executeUnifiedProvider(opts) {
|
|
5860
6315
|
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials);
|
|
5861
6316
|
if (opts.provider === "claude") {
|
|
5862
|
-
const { runClaude } = await import('./runClaude-
|
|
6317
|
+
const { runClaude } = await import('./runClaude-Cii3R2Fv.mjs');
|
|
5863
6318
|
await runClaude(credentials, opts.claudeOptions ?? {});
|
|
5864
6319
|
return;
|
|
5865
6320
|
}
|
|
5866
6321
|
if (opts.provider === "codex") {
|
|
5867
|
-
const { runCodex } = await import('./runCodex-
|
|
6322
|
+
const { runCodex } = await import('./runCodex-C--ZwAhl.mjs');
|
|
5868
6323
|
await runCodex({
|
|
5869
6324
|
credentials,
|
|
5870
6325
|
startedBy: opts.startedBy,
|
|
@@ -5874,7 +6329,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5874
6329
|
return;
|
|
5875
6330
|
}
|
|
5876
6331
|
if (opts.provider === "gemini") {
|
|
5877
|
-
const { runGemini } = await import('./runGemini-
|
|
6332
|
+
const { runGemini } = await import('./runGemini-CQp7Nuzn.mjs');
|
|
5878
6333
|
await runGemini({
|
|
5879
6334
|
credentials,
|
|
5880
6335
|
startedBy: opts.startedBy
|
|
@@ -5887,6 +6342,10 @@ async function executeUnifiedProvider(opts) {
|
|
|
5887
6342
|
});
|
|
5888
6343
|
}
|
|
5889
6344
|
|
|
6345
|
+
function shouldRunMainClaudeFlow(opts) {
|
|
6346
|
+
return !opts.showHelp && !opts.showVersion;
|
|
6347
|
+
}
|
|
6348
|
+
|
|
5890
6349
|
(async () => {
|
|
5891
6350
|
const args = process.argv.slice(2);
|
|
5892
6351
|
const isRemoteMode = args.includes("--happy-starting-mode") && args.includes("remote");
|
|
@@ -5895,7 +6354,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5895
6354
|
Object.defineProperty(process.stderr, "isTTY", { value: false });
|
|
5896
6355
|
}
|
|
5897
6356
|
if (!args.includes("--version")) {
|
|
5898
|
-
logger.debug("Starting
|
|
6357
|
+
logger.debug("Starting hicloud CLI with args: ", process.argv);
|
|
5899
6358
|
}
|
|
5900
6359
|
const subcommand = args[0];
|
|
5901
6360
|
if (!args.includes("--version")) ;
|
|
@@ -5912,7 +6371,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5912
6371
|
return;
|
|
5913
6372
|
} else if (subcommand === "runtime") {
|
|
5914
6373
|
if (args[1] === "providers") {
|
|
5915
|
-
const { renderRuntimeProviders } = await import('./command-
|
|
6374
|
+
const { renderRuntimeProviders } = await import('./command-nmK6O-ab.mjs');
|
|
5916
6375
|
console.log(renderRuntimeProviders());
|
|
5917
6376
|
return;
|
|
5918
6377
|
}
|
|
@@ -5938,8 +6397,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
5938
6397
|
}
|
|
5939
6398
|
return;
|
|
5940
6399
|
}
|
|
5941
|
-
console.log("Usage:
|
|
5942
|
-
console.log("
|
|
6400
|
+
console.log("Usage: hicloud runtime providers");
|
|
6401
|
+
console.log(" hicloud runtime launch <claude|codex|gemini|cursor> [prompt]");
|
|
5943
6402
|
return;
|
|
5944
6403
|
} else if (subcommand === "auth") {
|
|
5945
6404
|
try {
|
|
@@ -6090,8 +6549,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
6090
6549
|
const projectId = args[3];
|
|
6091
6550
|
try {
|
|
6092
6551
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
6093
|
-
const { readCredentials: readCredentials2 } = await import('./persistence-
|
|
6094
|
-
const { ApiClient: ApiClient2 } = await import('./api-
|
|
6552
|
+
const { readCredentials: readCredentials2 } = await import('./persistence-Dkm7rm8k.mjs');
|
|
6553
|
+
const { ApiClient: ApiClient2 } = await import('./api-MYhAGPLn.mjs').then(function (n) { return n.q; });
|
|
6095
6554
|
let userEmail = void 0;
|
|
6096
6555
|
try {
|
|
6097
6556
|
const credentials = await readCredentials2();
|
|
@@ -6137,7 +6596,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6137
6596
|
console.log("No Google Cloud Project configured.");
|
|
6138
6597
|
console.log("");
|
|
6139
6598
|
console.log('If you see "Authentication required" error, you may need to set a project:');
|
|
6140
|
-
console.log("
|
|
6599
|
+
console.log(" hicloud gemini project set <your-project-id>");
|
|
6141
6600
|
console.log("");
|
|
6142
6601
|
console.log("This is required for Google Workspace accounts.");
|
|
6143
6602
|
console.log("Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca");
|
|
@@ -6149,7 +6608,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6149
6608
|
}
|
|
6150
6609
|
}
|
|
6151
6610
|
if (geminiSubcommand === "project" && !args[2]) {
|
|
6152
|
-
console.log("Usage:
|
|
6611
|
+
console.log("Usage: hicloud gemini project <command>");
|
|
6153
6612
|
console.log("");
|
|
6154
6613
|
console.log("Commands:");
|
|
6155
6614
|
console.log(" set <project-id> Set Google Cloud Project ID");
|
|
@@ -6181,7 +6640,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6181
6640
|
}
|
|
6182
6641
|
return;
|
|
6183
6642
|
} else if (subcommand === "logout") {
|
|
6184
|
-
console.log(chalk.yellow('Note: "
|
|
6643
|
+
console.log(chalk.yellow('Note: "hicloud logout" is deprecated. Use "hicloud auth logout" instead.\n'));
|
|
6185
6644
|
try {
|
|
6186
6645
|
await handleAuthCommand(["logout"]);
|
|
6187
6646
|
} catch (error) {
|
|
@@ -6240,7 +6699,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6240
6699
|
child.unref();
|
|
6241
6700
|
let started = false;
|
|
6242
6701
|
for (let i = 0; i < 100; i++) {
|
|
6243
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
6702
|
+
if (await checkIfDaemonRunningAndCleanupStaleState() && await isDaemonControlServerResponsive(500)) {
|
|
6244
6703
|
started = true;
|
|
6245
6704
|
break;
|
|
6246
6705
|
}
|
|
@@ -6297,20 +6756,20 @@ async function executeUnifiedProvider(opts) {
|
|
|
6297
6756
|
}
|
|
6298
6757
|
} else {
|
|
6299
6758
|
console.log(`
|
|
6300
|
-
${chalk.bold("
|
|
6759
|
+
${chalk.bold("hicloud daemon")} - Daemon management
|
|
6301
6760
|
|
|
6302
6761
|
${chalk.bold("Usage:")}
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6762
|
+
hicloud daemon start Start the daemon (detached)
|
|
6763
|
+
hicloud daemon stop Stop the daemon (sessions stay alive)
|
|
6764
|
+
hicloud daemon status Show daemon status
|
|
6765
|
+
hicloud daemon list List active sessions
|
|
6307
6766
|
|
|
6308
6767
|
If you want to kill all happy related processes run
|
|
6309
|
-
${chalk.cyan("
|
|
6768
|
+
${chalk.cyan("hicloud doctor clean")}
|
|
6310
6769
|
|
|
6311
6770
|
${chalk.bold("Note:")} The daemon runs in the background and manages Claude sessions.
|
|
6312
6771
|
|
|
6313
|
-
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("
|
|
6772
|
+
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("hicloud doctor clean")}
|
|
6314
6773
|
`);
|
|
6315
6774
|
}
|
|
6316
6775
|
return;
|
|
@@ -6385,33 +6844,33 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy-cloud do
|
|
|
6385
6844
|
${chalk.bold(BRAND_CONFIG.name)} - AI \u7F16\u7A0B\u52A9\u624B
|
|
6386
6845
|
|
|
6387
6846
|
${chalk.bold("Usage:")}
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6847
|
+
hicloud [options] Start Claude with mobile control
|
|
6848
|
+
hicloud auth Manage authentication
|
|
6849
|
+
hicloud codex Start Codex mode
|
|
6850
|
+
hicloud gemini Start Gemini mode (ACP)
|
|
6851
|
+
hicloud cursor Start Cursor mode (experimental ACP)
|
|
6852
|
+
hicloud connect Connect AI vendor API keys
|
|
6853
|
+
hicloud notify Send push notification
|
|
6854
|
+
hicloud daemon Manage background service that allows
|
|
6396
6855
|
to spawn new sessions away from your computer
|
|
6397
|
-
|
|
6856
|
+
hicloud doctor System diagnostics & troubleshooting
|
|
6398
6857
|
|
|
6399
6858
|
${chalk.bold("Examples:")}
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6859
|
+
hicloud Start session
|
|
6860
|
+
hicloud --yolo Start with bypassing permissions
|
|
6861
|
+
hicloud sugar for --dangerously-skip-permissions
|
|
6862
|
+
hicloud --chrome Enable Chrome browser access for this session
|
|
6863
|
+
hicloud --no-chrome Disable Chrome even if default is on
|
|
6864
|
+
hicloud --js-runtime bun Use bun instead of node to spawn Claude Code
|
|
6865
|
+
hicloud --claude-env ANTHROPIC_BASE_URL=http://127.0.0.1:3456
|
|
6407
6866
|
Use a custom API endpoint (e.g., claude-code-router)
|
|
6408
|
-
|
|
6409
|
-
|
|
6867
|
+
hicloud auth login --force Authenticate
|
|
6868
|
+
hicloud doctor Run diagnostics
|
|
6410
6869
|
|
|
6411
6870
|
${chalk.bold(`${BRAND_CONFIG.name} supports ALL Claude options!`)}
|
|
6412
|
-
Use any claude flag with
|
|
6871
|
+
Use any claude flag with hicloud as you would with claude. Our favorite:
|
|
6413
6872
|
|
|
6414
|
-
|
|
6873
|
+
hicloud --resume
|
|
6415
6874
|
|
|
6416
6875
|
${chalk.gray("\u2500".repeat(60))}
|
|
6417
6876
|
${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
@@ -6426,8 +6885,9 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
6426
6885
|
}
|
|
6427
6886
|
if (showVersion) {
|
|
6428
6887
|
console.log(getVersionString());
|
|
6888
|
+
return;
|
|
6429
6889
|
}
|
|
6430
|
-
if (
|
|
6890
|
+
if (shouldRunMainClaudeFlow({ showHelp, showVersion })) {
|
|
6431
6891
|
printBanner();
|
|
6432
6892
|
}
|
|
6433
6893
|
try {
|
|
@@ -6463,31 +6923,31 @@ async function handleNotifyCommand(args) {
|
|
|
6463
6923
|
}
|
|
6464
6924
|
if (showHelp) {
|
|
6465
6925
|
console.log(`
|
|
6466
|
-
${chalk.bold("
|
|
6926
|
+
${chalk.bold("hicloud notify")} - Send notification
|
|
6467
6927
|
|
|
6468
6928
|
${chalk.bold("Usage:")}
|
|
6469
|
-
|
|
6470
|
-
|
|
6929
|
+
hicloud notify -p <message> [-t <title>] Send notification with custom message and optional title
|
|
6930
|
+
hicloud notify -h, --help Show this help
|
|
6471
6931
|
|
|
6472
6932
|
${chalk.bold("Options:")}
|
|
6473
6933
|
-p <message> Notification message (required)
|
|
6474
6934
|
-t <title> Notification title (optional, defaults to "Happy")
|
|
6475
6935
|
|
|
6476
6936
|
${chalk.bold("Examples:")}
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6937
|
+
hicloud notify -p "Deployment complete!"
|
|
6938
|
+
hicloud notify -p "System update complete" -t "Server Status"
|
|
6939
|
+
hicloud notify -t "Alert" -p "Database connection restored"
|
|
6480
6940
|
`);
|
|
6481
6941
|
return;
|
|
6482
6942
|
}
|
|
6483
6943
|
if (!message) {
|
|
6484
6944
|
console.error(chalk.red('Error: Message is required. Use -p "your message" to specify the notification text.'));
|
|
6485
|
-
console.log(chalk.gray('Run "
|
|
6945
|
+
console.log(chalk.gray('Run "hicloud notify --help" for usage information.'));
|
|
6486
6946
|
process.exit(1);
|
|
6487
6947
|
}
|
|
6488
6948
|
let credentials = await readCredentials();
|
|
6489
6949
|
if (!credentials) {
|
|
6490
|
-
console.error(chalk.red('Error: Not authenticated. Please run "
|
|
6950
|
+
console.error(chalk.red('Error: Not authenticated. Please run "hicloud auth login" first.'));
|
|
6491
6951
|
process.exit(1);
|
|
6492
6952
|
}
|
|
6493
6953
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
@@ -6513,4 +6973,4 @@ ${chalk.bold("Examples:")}
|
|
|
6513
6973
|
}
|
|
6514
6974
|
}
|
|
6515
6975
|
|
|
6516
|
-
export { ExitCodeError as E, GEMINI_MODEL_ENV as G, createGeminiBackend as a, stopCaffeinate as b, createDefaultRuntimeShell as c,
|
|
6976
|
+
export { ExitCodeError as E, GEMINI_MODEL_ENV as G, createGeminiBackend as a, stopCaffeinate as b, createDefaultRuntimeShell as c, createCodexBackend as d, getProjectPath as e, formatDisplayMessage as f, getInitialGeminiModel as g, claudeLocal as h, initialMachineMetadata as i, isBun as j, trimIdent as k, claudeCheckSession as l, getEnvironmentInfo as m, notifyDaemonSessionStarted as n, startCaffeinate as o, projectPath as p, readGeminiLocalConfig as r, saveGeminiModelToConfig as s, truncateDisplayMessage as t, validateCodexAcpSpawn as v };
|