happy-imou-cloud 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseReasoningProcessor-BRCQXCZY.cjs → BaseReasoningProcessor-01KqA3Kz.cjs} +96 -9
- package/dist/{BaseReasoningProcessor-BKLRCKTU.mjs → BaseReasoningProcessor-DQE2l7Xu.mjs} +95 -10
- package/dist/{api-BGXYX0yH.mjs → api-B5Ui8Fw0.mjs} +68 -6
- package/dist/{api-D7OK-mML.cjs → api-B8v4tczT.cjs} +70 -5
- package/dist/{command-CnLtKtP-.mjs → command-BfIuJmeo.mjs} +3 -3
- package/dist/{command-G85giEAF.cjs → command-D8yNlaDo.cjs} +3 -3
- package/dist/{index-C7Y0R-MI.mjs → index-BByhFIIq.mjs} +682 -220
- package/dist/{index-B_wlQBy2.cjs → index-BOqJ9hwi.cjs} +684 -220
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +1 -0
- package/dist/lib.d.mts +1 -0
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-DHgf1CTG.cjs → persistence-C33AMdtv.cjs} +1 -1
- package/dist/{persistence-BA_unuca.mjs → persistence-CzpZpiL3.mjs} +1 -1
- package/dist/{registerKillSessionHandler-CLREXN11.cjs → registerKillSessionHandler-BkzQulD9.cjs} +6 -4
- package/dist/{registerKillSessionHandler-C2-yHm1V.mjs → registerKillSessionHandler-BtSK7IOa.mjs} +6 -4
- package/dist/{runClaude-CwAitpX-.cjs → runClaude-CNVufgZb.cjs} +14 -6
- package/dist/{runClaude-uNC5Eym4.mjs → runClaude-C_WLfM6c.mjs} +13 -5
- package/dist/{runCodex-B-05E-YZ.mjs → runCodex-8eWjTPJr.mjs} +636 -819
- package/dist/{runCodex-Cm0VTqw_.cjs → runCodex-Dzy8anlX.cjs} +639 -819
- package/dist/{runGemini-CLWjwDYS.cjs → runGemini-CgsVKP7m.cjs} +20 -16
- package/dist/{runGemini-_biXvQAH.mjs → runGemini-nbr0mm-S.mjs} +20 -16
- package/package.json +14 -14
- 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-B5Ui8Fw0.mjs';
|
|
3
|
+
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-CzpZpiL3.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) {
|
|
@@ -4056,6 +4103,77 @@ class AgentRegistry {
|
|
|
4056
4103
|
}
|
|
4057
4104
|
const agentRegistry = new AgentRegistry();
|
|
4058
4105
|
|
|
4106
|
+
const PREFERRED_MESSAGE_FIELDS = ["stderr", "error", "message", "detail", "stdout", "text", "reason"];
|
|
4107
|
+
function safeSerializeDisplayValue(value) {
|
|
4108
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
4109
|
+
try {
|
|
4110
|
+
const serialized = JSON.stringify(value, (_key, currentValue) => {
|
|
4111
|
+
if (typeof currentValue === "bigint") {
|
|
4112
|
+
return currentValue.toString();
|
|
4113
|
+
}
|
|
4114
|
+
if (currentValue instanceof Error) {
|
|
4115
|
+
return {
|
|
4116
|
+
name: currentValue.name,
|
|
4117
|
+
message: currentValue.message,
|
|
4118
|
+
stack: currentValue.stack
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
4121
|
+
if (typeof currentValue === "object" && currentValue !== null) {
|
|
4122
|
+
if (seen.has(currentValue)) {
|
|
4123
|
+
return "[Circular]";
|
|
4124
|
+
}
|
|
4125
|
+
seen.add(currentValue);
|
|
4126
|
+
}
|
|
4127
|
+
return currentValue;
|
|
4128
|
+
});
|
|
4129
|
+
return serialized ?? String(value);
|
|
4130
|
+
} catch {
|
|
4131
|
+
return String(value);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
function formatDisplayMessage(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
4135
|
+
if (typeof value === "string") {
|
|
4136
|
+
return value;
|
|
4137
|
+
}
|
|
4138
|
+
if (value instanceof Error) {
|
|
4139
|
+
return value.message || String(value);
|
|
4140
|
+
}
|
|
4141
|
+
if (value === null || value === void 0) {
|
|
4142
|
+
return "";
|
|
4143
|
+
}
|
|
4144
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
4145
|
+
return value.toString("utf8");
|
|
4146
|
+
}
|
|
4147
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
4148
|
+
return String(value);
|
|
4149
|
+
}
|
|
4150
|
+
if (typeof value === "object") {
|
|
4151
|
+
if (seen.has(value)) {
|
|
4152
|
+
return "[Circular]";
|
|
4153
|
+
}
|
|
4154
|
+
seen.add(value);
|
|
4155
|
+
const record = value;
|
|
4156
|
+
for (const field of PREFERRED_MESSAGE_FIELDS) {
|
|
4157
|
+
if (!(field in record)) {
|
|
4158
|
+
continue;
|
|
4159
|
+
}
|
|
4160
|
+
const formattedField = formatDisplayMessage(record[field], seen).trim();
|
|
4161
|
+
if (formattedField.length > 0) {
|
|
4162
|
+
return formattedField;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
return safeSerializeDisplayValue(value);
|
|
4166
|
+
}
|
|
4167
|
+
return String(value);
|
|
4168
|
+
}
|
|
4169
|
+
function truncateDisplayMessage(value, maxLength) {
|
|
4170
|
+
const formatted = formatDisplayMessage(value);
|
|
4171
|
+
if (formatted.length <= maxLength) {
|
|
4172
|
+
return formatted;
|
|
4173
|
+
}
|
|
4174
|
+
return `${formatted.substring(0, maxLength)}...`;
|
|
4175
|
+
}
|
|
4176
|
+
|
|
4059
4177
|
const DEFAULT_TIMEOUTS = {
|
|
4060
4178
|
/** Default initialization timeout: 60 seconds */
|
|
4061
4179
|
init: 6e4,
|
|
@@ -4350,7 +4468,7 @@ class CodexTransport extends DefaultTransport {
|
|
|
4350
4468
|
return 800;
|
|
4351
4469
|
}
|
|
4352
4470
|
}
|
|
4353
|
-
|
|
4471
|
+
new CodexTransport();
|
|
4354
4472
|
|
|
4355
4473
|
class ClaudeTransport extends DefaultTransport {
|
|
4356
4474
|
constructor() {
|
|
@@ -4684,6 +4802,70 @@ const RETRY_CONFIG = {
|
|
|
4684
4802
|
/** Maximum delay between retries in ms */
|
|
4685
4803
|
maxDelayMs: 5e3
|
|
4686
4804
|
};
|
|
4805
|
+
function getSessionUpdates(params) {
|
|
4806
|
+
const notification = params;
|
|
4807
|
+
const updates = Array.isArray(notification.updates) ? notification.updates.filter((update) => Boolean(update && typeof update === "object")) : [];
|
|
4808
|
+
if (updates.length > 0) {
|
|
4809
|
+
return updates;
|
|
4810
|
+
}
|
|
4811
|
+
if (notification.update && typeof notification.update === "object") {
|
|
4812
|
+
return [notification.update];
|
|
4813
|
+
}
|
|
4814
|
+
return [];
|
|
4815
|
+
}
|
|
4816
|
+
function asNonNegativeFiniteNumber(value) {
|
|
4817
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
4818
|
+
}
|
|
4819
|
+
function extractUsageTokens(record) {
|
|
4820
|
+
const used = asNonNegativeFiniteNumber(record.used);
|
|
4821
|
+
const size = asNonNegativeFiniteNumber(record.size);
|
|
4822
|
+
if (used != null || size != null) {
|
|
4823
|
+
const tokens2 = {
|
|
4824
|
+
total: used ?? 0
|
|
4825
|
+
};
|
|
4826
|
+
if (used != null) tokens2.used = used;
|
|
4827
|
+
if (size != null) tokens2.size = size;
|
|
4828
|
+
return tokens2;
|
|
4829
|
+
}
|
|
4830
|
+
const input = asNonNegativeFiniteNumber(record.input_tokens) ?? asNonNegativeFiniteNumber(record.inputTokens) ?? asNonNegativeFiniteNumber(record.prompt_tokens) ?? asNonNegativeFiniteNumber(record.promptTokens);
|
|
4831
|
+
const output = asNonNegativeFiniteNumber(record.output_tokens) ?? asNonNegativeFiniteNumber(record.outputTokens) ?? asNonNegativeFiniteNumber(record.completion_tokens) ?? asNonNegativeFiniteNumber(record.completionTokens);
|
|
4832
|
+
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);
|
|
4833
|
+
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);
|
|
4834
|
+
const thought = asNonNegativeFiniteNumber(record.thought_tokens) ?? asNonNegativeFiniteNumber(record.thoughtTokens);
|
|
4835
|
+
const totalFromPayload = asNonNegativeFiniteNumber(record.total_tokens) ?? asNonNegativeFiniteNumber(record.totalTokens);
|
|
4836
|
+
const anyPresent = totalFromPayload != null || input != null || output != null || cacheRead != null || cacheCreation != null || thought != null;
|
|
4837
|
+
if (!anyPresent) {
|
|
4838
|
+
return null;
|
|
4839
|
+
}
|
|
4840
|
+
const total = totalFromPayload ?? (input ?? 0) + (output ?? 0) + (cacheRead ?? 0) + (cacheCreation ?? 0) + (thought ?? 0);
|
|
4841
|
+
const tokens = { total };
|
|
4842
|
+
if (input != null) tokens.input = input;
|
|
4843
|
+
if (output != null) tokens.output = output;
|
|
4844
|
+
if (cacheRead != null) tokens.cache_read = cacheRead;
|
|
4845
|
+
if (cacheCreation != null) tokens.cache_creation = cacheCreation;
|
|
4846
|
+
if (thought != null) tokens.thought = thought;
|
|
4847
|
+
return tokens;
|
|
4848
|
+
}
|
|
4849
|
+
function isApprovalOption(option) {
|
|
4850
|
+
if (option.optionId === "proceed_once" || option.optionId === "proceed_always" || option.optionId === "cancel") {
|
|
4851
|
+
return true;
|
|
4852
|
+
}
|
|
4853
|
+
const searchable = `${option.name ?? ""} ${option.label ?? ""}`.toLowerCase();
|
|
4854
|
+
return searchable.includes("once") || searchable.includes("always") || searchable.includes("cancel");
|
|
4855
|
+
}
|
|
4856
|
+
function isSelectionPermissionRequest(params) {
|
|
4857
|
+
if (Array.isArray(params.codex_command) && params.codex_command.length > 0) {
|
|
4858
|
+
return false;
|
|
4859
|
+
}
|
|
4860
|
+
if (params.requestedSchema?.properties?.optionId) {
|
|
4861
|
+
return true;
|
|
4862
|
+
}
|
|
4863
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
4864
|
+
if (options.length === 0) {
|
|
4865
|
+
return false;
|
|
4866
|
+
}
|
|
4867
|
+
return !options.every((option) => isApprovalOption(option));
|
|
4868
|
+
}
|
|
4687
4869
|
function nodeToWebStreams(stdin, stdout) {
|
|
4688
4870
|
const writable = new WritableStream({
|
|
4689
4871
|
write(chunk) {
|
|
@@ -4747,7 +4929,7 @@ async function withRetry(operation, options) {
|
|
|
4747
4929
|
try {
|
|
4748
4930
|
return await operation();
|
|
4749
4931
|
} catch (error) {
|
|
4750
|
-
lastError =
|
|
4932
|
+
lastError = normalizeAcpError(error);
|
|
4751
4933
|
if (attempt < options.maxAttempts) {
|
|
4752
4934
|
const delayMs = Math.min(
|
|
4753
4935
|
options.baseDelayMs * Math.pow(2, attempt - 1),
|
|
@@ -4761,6 +4943,49 @@ async function withRetry(operation, options) {
|
|
|
4761
4943
|
}
|
|
4762
4944
|
throw lastError;
|
|
4763
4945
|
}
|
|
4946
|
+
function formatAcpErrorMessage(error) {
|
|
4947
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4948
|
+
return error.message;
|
|
4949
|
+
}
|
|
4950
|
+
if (typeof error === "object" && error !== null) {
|
|
4951
|
+
const record = error;
|
|
4952
|
+
const message = formatDisplayMessage(error).trim();
|
|
4953
|
+
const code = record.code;
|
|
4954
|
+
const status = record.status;
|
|
4955
|
+
const prefix = [
|
|
4956
|
+
code !== void 0 && code !== null ? `[code=${String(code)}]` : "",
|
|
4957
|
+
status !== void 0 && status !== null ? `[status=${String(status)}]` : ""
|
|
4958
|
+
].filter(Boolean).join(" ");
|
|
4959
|
+
if (message.length > 0) {
|
|
4960
|
+
return prefix ? `${prefix} ${message}` : message;
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
const fallback = formatDisplayMessage(error).trim();
|
|
4964
|
+
return fallback || "Unknown ACP error";
|
|
4965
|
+
}
|
|
4966
|
+
function normalizeAcpError(error) {
|
|
4967
|
+
if (error instanceof Error && error.message && error.message !== "[object Object]") {
|
|
4968
|
+
return error;
|
|
4969
|
+
}
|
|
4970
|
+
const normalized = new Error(formatAcpErrorMessage(error));
|
|
4971
|
+
normalized.name = error instanceof Error ? error.name : "Error";
|
|
4972
|
+
if (error && typeof error === "object") {
|
|
4973
|
+
const { message: _message, ...rest } = error;
|
|
4974
|
+
Object.assign(normalized, rest);
|
|
4975
|
+
}
|
|
4976
|
+
return normalized;
|
|
4977
|
+
}
|
|
4978
|
+
function enrichAcpError(error, stderrExcerpt) {
|
|
4979
|
+
const normalized = normalizeAcpError(error);
|
|
4980
|
+
if (!stderrExcerpt.trim()) {
|
|
4981
|
+
return normalized;
|
|
4982
|
+
}
|
|
4983
|
+
const existingStderr = normalized.stderr;
|
|
4984
|
+
if (typeof existingStderr !== "string" || existingStderr.trim().length === 0) {
|
|
4985
|
+
Object.assign(normalized, { stderr: stderrExcerpt });
|
|
4986
|
+
}
|
|
4987
|
+
return normalized;
|
|
4988
|
+
}
|
|
4764
4989
|
class AcpBackend {
|
|
4765
4990
|
constructor(options) {
|
|
4766
4991
|
this.options = options;
|
|
@@ -4788,6 +5013,21 @@ class AcpBackend {
|
|
|
4788
5013
|
idleTimeout = null;
|
|
4789
5014
|
/** Transport handler for agent-specific behavior */
|
|
4790
5015
|
transport;
|
|
5016
|
+
/** Keep a short rolling stderr buffer so startup failures can surface the real cause. */
|
|
5017
|
+
recentStderrLines = [];
|
|
5018
|
+
recordRecentStderr(text) {
|
|
5019
|
+
const normalized = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
5020
|
+
if (normalized.length === 0) {
|
|
5021
|
+
return;
|
|
5022
|
+
}
|
|
5023
|
+
this.recentStderrLines.push(...normalized);
|
|
5024
|
+
if (this.recentStderrLines.length > 12) {
|
|
5025
|
+
this.recentStderrLines = this.recentStderrLines.slice(-12);
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
getRecentStderrExcerpt() {
|
|
5029
|
+
return this.recentStderrLines.slice(-6).join("\n");
|
|
5030
|
+
}
|
|
4791
5031
|
onMessage(handler) {
|
|
4792
5032
|
this.listeners.push(handler);
|
|
4793
5033
|
}
|
|
@@ -4815,6 +5055,7 @@ class AcpBackend {
|
|
|
4815
5055
|
this.emit({ type: "status", status: "starting" });
|
|
4816
5056
|
try {
|
|
4817
5057
|
logger.debug(`[AcpBackend] Starting session: ${sessionId}`);
|
|
5058
|
+
this.recentStderrLines = [];
|
|
4818
5059
|
const args = this.options.args || [];
|
|
4819
5060
|
if (process.platform === "win32") {
|
|
4820
5061
|
const fullCommand = [this.options.command, ...args].join(" ");
|
|
@@ -4841,6 +5082,7 @@ class AcpBackend {
|
|
|
4841
5082
|
this.process.stderr.on("data", (data) => {
|
|
4842
5083
|
const text = data.toString();
|
|
4843
5084
|
if (!text.trim()) return;
|
|
5085
|
+
this.recordRecentStderr(text);
|
|
4844
5086
|
const hasActiveInvestigation = this.transport.isInvestigationTool ? Array.from(this.activeToolCalls).some((id) => this.transport.isInvestigationTool(id)) : false;
|
|
4845
5087
|
const context = {
|
|
4846
5088
|
activeToolCalls: this.activeToolCalls,
|
|
@@ -4951,6 +5193,7 @@ class AcpBackend {
|
|
|
4951
5193
|
}
|
|
4952
5194
|
this.toolCallCountSincePrompt++;
|
|
4953
5195
|
const options = extendedParams.options || [];
|
|
5196
|
+
const isSelectionRequest = isSelectionPermissionRequest(extendedParams);
|
|
4954
5197
|
logger.debug(`[AcpBackend] Permission request: tool=${toolName}, toolCallId=${toolCallId}, input=`, JSON.stringify(input));
|
|
4955
5198
|
logger.debug(`[AcpBackend] Permission request params structure:`, JSON.stringify({
|
|
4956
5199
|
hasToolCall: !!toolCall,
|
|
@@ -4959,6 +5202,42 @@ class AcpBackend {
|
|
|
4959
5202
|
paramsKind: extendedParams.kind,
|
|
4960
5203
|
paramsKeys: Object.keys(params)
|
|
4961
5204
|
}, null, 2));
|
|
5205
|
+
if (isSelectionRequest) {
|
|
5206
|
+
const selectionOptions = options.reduce((acc, option) => {
|
|
5207
|
+
if (!option.optionId) {
|
|
5208
|
+
return acc;
|
|
5209
|
+
}
|
|
5210
|
+
const displayOption = option;
|
|
5211
|
+
acc.push({
|
|
5212
|
+
optionId: option.optionId,
|
|
5213
|
+
label: displayOption.label || option.name || option.optionId,
|
|
5214
|
+
description: displayOption.description || option.name || option.optionId
|
|
5215
|
+
});
|
|
5216
|
+
return acc;
|
|
5217
|
+
}, []);
|
|
5218
|
+
if (selectionOptions.length === 0) {
|
|
5219
|
+
logger.debug("[AcpBackend] Selection request has no valid options, cancelling");
|
|
5220
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5221
|
+
}
|
|
5222
|
+
if (!this.options.selectionHandler) {
|
|
5223
|
+
logger.debug("[AcpBackend] No selection handler configured, cancelling selection request");
|
|
5224
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5225
|
+
}
|
|
5226
|
+
try {
|
|
5227
|
+
const selectionMessage = typeof extendedParams.message === "string" && extendedParams.message.trim().length > 0 ? extendedParams.message : formatDisplayMessage(input).trim() || toolName;
|
|
5228
|
+
const requestId = extendedParams.codex_event_id || extendedParams.codex_elicitation || toolCallId;
|
|
5229
|
+
const response = await this.options.selectionHandler.handleSelection({
|
|
5230
|
+
id: requestId,
|
|
5231
|
+
message: selectionMessage,
|
|
5232
|
+
options: selectionOptions,
|
|
5233
|
+
defaultOptionId: extendedParams.requestedSchema?.properties?.optionId?.default
|
|
5234
|
+
});
|
|
5235
|
+
return { outcome: { outcome: "selected", optionId: response.optionId } };
|
|
5236
|
+
} catch (error) {
|
|
5237
|
+
logger.debug("[AcpBackend] Error in selection handler:", error);
|
|
5238
|
+
return { outcome: { outcome: "selected", optionId: "cancel" } };
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
4962
5241
|
this.emit({
|
|
4963
5242
|
type: "permission-request",
|
|
4964
5243
|
id: permissionId,
|
|
@@ -5132,18 +5411,19 @@ class AcpBackend {
|
|
|
5132
5411
|
if (initialPrompt) {
|
|
5133
5412
|
this.sendPrompt(sessionId, initialPrompt).catch((error) => {
|
|
5134
5413
|
logger.debug("[AcpBackend] Error sending initial prompt:", error);
|
|
5135
|
-
this.emit({ type: "status", status: "error", detail:
|
|
5414
|
+
this.emit({ type: "status", status: "error", detail: formatAcpErrorMessage(error) });
|
|
5136
5415
|
});
|
|
5137
5416
|
}
|
|
5138
5417
|
return { sessionId };
|
|
5139
5418
|
} catch (error) {
|
|
5140
|
-
|
|
5419
|
+
const enrichedError = enrichAcpError(error, this.getRecentStderrExcerpt());
|
|
5420
|
+
logger.debug("[AcpBackend] Error starting session:", enrichedError);
|
|
5141
5421
|
this.emit({
|
|
5142
5422
|
type: "status",
|
|
5143
5423
|
status: "error",
|
|
5144
|
-
detail:
|
|
5424
|
+
detail: formatAcpErrorMessage(enrichedError)
|
|
5145
5425
|
});
|
|
5146
|
-
throw
|
|
5426
|
+
throw enrichedError;
|
|
5147
5427
|
}
|
|
5148
5428
|
}
|
|
5149
5429
|
/**
|
|
@@ -5174,51 +5454,73 @@ class AcpBackend {
|
|
|
5174
5454
|
}
|
|
5175
5455
|
};
|
|
5176
5456
|
}
|
|
5457
|
+
emitUsageTelemetry(payload, source) {
|
|
5458
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
5459
|
+
return false;
|
|
5460
|
+
}
|
|
5461
|
+
const tokens = extractUsageTokens(payload);
|
|
5462
|
+
if (!tokens) {
|
|
5463
|
+
return false;
|
|
5464
|
+
}
|
|
5465
|
+
this.emit({
|
|
5466
|
+
type: "token-count",
|
|
5467
|
+
key: source,
|
|
5468
|
+
tokens,
|
|
5469
|
+
source
|
|
5470
|
+
});
|
|
5471
|
+
return true;
|
|
5472
|
+
}
|
|
5177
5473
|
handleSessionUpdate(params) {
|
|
5178
|
-
const
|
|
5179
|
-
|
|
5180
|
-
if (!update) {
|
|
5474
|
+
const updates = getSessionUpdates(params);
|
|
5475
|
+
if (updates.length === 0) {
|
|
5181
5476
|
logger.debug("[AcpBackend] Received session update without update field:", params);
|
|
5182
5477
|
return;
|
|
5183
5478
|
}
|
|
5184
|
-
const
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5479
|
+
for (const update of updates) {
|
|
5480
|
+
const sessionUpdateType = update.sessionUpdate;
|
|
5481
|
+
if (sessionUpdateType !== "agent_message_chunk") {
|
|
5482
|
+
logger.debug(`[AcpBackend] Received session update: ${sessionUpdateType}`, JSON.stringify({
|
|
5483
|
+
sessionUpdate: sessionUpdateType,
|
|
5484
|
+
toolCallId: update.toolCallId,
|
|
5485
|
+
status: update.status,
|
|
5486
|
+
kind: update.kind,
|
|
5487
|
+
hasContent: !!update.content,
|
|
5488
|
+
hasLocations: !!update.locations
|
|
5489
|
+
}, null, 2));
|
|
5490
|
+
}
|
|
5491
|
+
const ctx = this.createHandlerContext();
|
|
5492
|
+
if (sessionUpdateType === "agent_message_chunk") {
|
|
5493
|
+
handleAgentMessageChunk(update, ctx);
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
if (sessionUpdateType === "tool_call_update") {
|
|
5497
|
+
const result = handleToolCallUpdate(update, ctx);
|
|
5498
|
+
if (result.toolCallCountSincePrompt !== void 0) {
|
|
5499
|
+
this.toolCallCountSincePrompt = result.toolCallCountSincePrompt;
|
|
5500
|
+
}
|
|
5501
|
+
continue;
|
|
5502
|
+
}
|
|
5503
|
+
if (sessionUpdateType === "agent_thought_chunk") {
|
|
5504
|
+
handleAgentThoughtChunk(update, ctx);
|
|
5505
|
+
continue;
|
|
5506
|
+
}
|
|
5507
|
+
if (sessionUpdateType === "tool_call") {
|
|
5508
|
+
handleToolCall(update, ctx);
|
|
5509
|
+
continue;
|
|
5510
|
+
}
|
|
5511
|
+
if (sessionUpdateType === "usage_update") {
|
|
5512
|
+
this.emitUsageTelemetry(update, "acp-usage-update");
|
|
5513
|
+
continue;
|
|
5514
|
+
}
|
|
5515
|
+
const handledLegacy = handleLegacyMessageChunk(update, ctx).handled;
|
|
5516
|
+
const handledPlan = handlePlanUpdate(update, ctx).handled;
|
|
5517
|
+
const handledThinking = handleThinkingUpdate(update, ctx).handled;
|
|
5518
|
+
const handledUsage = this.emitUsageTelemetry(update.usage, "acp-session-usage");
|
|
5519
|
+
const updateTypeStr = sessionUpdateType;
|
|
5520
|
+
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call", "usage_update"];
|
|
5521
|
+
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !handledLegacy && !handledPlan && !handledThinking && !handledUsage) {
|
|
5522
|
+
logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5204
5523
|
}
|
|
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
5524
|
}
|
|
5223
5525
|
}
|
|
5224
5526
|
// Promise resolver for waitForIdle - set when waiting for response to complete
|
|
@@ -5255,17 +5557,9 @@ class AcpBackend {
|
|
|
5255
5557
|
if (error instanceof Error) {
|
|
5256
5558
|
errorDetail = error.message;
|
|
5257
5559
|
} 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
|
-
}
|
|
5560
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5267
5561
|
} else {
|
|
5268
|
-
errorDetail =
|
|
5562
|
+
errorDetail = formatAcpErrorMessage(error);
|
|
5269
5563
|
}
|
|
5270
5564
|
this.emit({
|
|
5271
5565
|
type: "status",
|
|
@@ -5616,59 +5910,213 @@ function registerGeminiAgent() {
|
|
|
5616
5910
|
logger.debug("[Gemini] Registered with agent registry");
|
|
5617
5911
|
}
|
|
5618
5912
|
|
|
5619
|
-
function
|
|
5620
|
-
for (const
|
|
5621
|
-
|
|
5622
|
-
|
|
5913
|
+
function readFirstEnv(...names) {
|
|
5914
|
+
for (const name of names) {
|
|
5915
|
+
const raw = process.env[name];
|
|
5916
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
5917
|
+
return raw.trim();
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
return "";
|
|
5921
|
+
}
|
|
5922
|
+
function normalizeCommandPath(command) {
|
|
5923
|
+
if (isAbsolute(command)) {
|
|
5924
|
+
return command;
|
|
5925
|
+
}
|
|
5926
|
+
const resolved = resolve$1(process.cwd(), command);
|
|
5927
|
+
return existsSync(resolved) ? resolved : command;
|
|
5928
|
+
}
|
|
5929
|
+
function resolveCommandOnPath(command) {
|
|
5930
|
+
const pathValue = typeof process.env.PATH === "string" ? process.env.PATH : "";
|
|
5931
|
+
if (!pathValue) {
|
|
5932
|
+
return null;
|
|
5933
|
+
}
|
|
5934
|
+
const extensions = process.platform === "win32" ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter(Boolean) : [""];
|
|
5935
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
5936
|
+
const trimmedDir = dir.trim();
|
|
5937
|
+
if (!trimmedDir) {
|
|
5938
|
+
continue;
|
|
5939
|
+
}
|
|
5940
|
+
const directCandidate = join(trimmedDir, command);
|
|
5941
|
+
if (existsSync(directCandidate)) {
|
|
5942
|
+
return directCandidate;
|
|
5943
|
+
}
|
|
5944
|
+
if (process.platform !== "win32") {
|
|
5945
|
+
continue;
|
|
5946
|
+
}
|
|
5947
|
+
const hasExtension = /\.[^\\/]+$/.test(command);
|
|
5948
|
+
if (hasExtension) {
|
|
5949
|
+
continue;
|
|
5950
|
+
}
|
|
5951
|
+
for (const extension of extensions) {
|
|
5952
|
+
const candidate = join(trimmedDir, `${command}${extension.toLowerCase()}`);
|
|
5953
|
+
if (existsSync(candidate)) {
|
|
5623
5954
|
return candidate;
|
|
5624
5955
|
}
|
|
5625
|
-
} catch {
|
|
5626
5956
|
}
|
|
5627
5957
|
}
|
|
5628
5958
|
return null;
|
|
5629
5959
|
}
|
|
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
|
-
}
|
|
5960
|
+
function readCodexAcpNpxMode() {
|
|
5961
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_NPX_MODE", "HAPPIER_CODEX_ACP_NPX_MODE").toLowerCase();
|
|
5962
|
+
if (raw === "auto" || raw === "never" || raw === "force") {
|
|
5963
|
+
return raw;
|
|
5642
5964
|
}
|
|
5643
|
-
return "
|
|
5965
|
+
return "auto";
|
|
5966
|
+
}
|
|
5967
|
+
function isBinOnPath(baseName) {
|
|
5968
|
+
return resolveCommandOnPath(baseName) !== null;
|
|
5644
5969
|
}
|
|
5645
|
-
function
|
|
5646
|
-
|
|
5970
|
+
function readCodexAcpConfigOverrides() {
|
|
5971
|
+
const raw = readFirstEnv("HAPPY_CODEX_ACP_CONFIG_OVERRIDES", "HAPPIER_CODEX_ACP_CONFIG_OVERRIDES");
|
|
5972
|
+
return raw.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
5973
|
+
}
|
|
5974
|
+
function buildCodexAcpConfigArgs(options) {
|
|
5975
|
+
const args = [...options.baseArgs ?? []];
|
|
5976
|
+
const overrides = [...readCodexAcpConfigOverrides()];
|
|
5977
|
+
if (options.model) {
|
|
5978
|
+
overrides.push(`model=${JSON.stringify(options.model)}`);
|
|
5979
|
+
}
|
|
5980
|
+
if (options.approvalPolicy) {
|
|
5981
|
+
overrides.push(`approval_policy=${JSON.stringify(options.approvalPolicy)}`);
|
|
5982
|
+
}
|
|
5983
|
+
if (options.sandbox) {
|
|
5984
|
+
overrides.push(`sandbox_mode=${JSON.stringify(options.sandbox)}`);
|
|
5985
|
+
}
|
|
5986
|
+
for (const override of overrides) {
|
|
5987
|
+
args.push("-c", override);
|
|
5988
|
+
}
|
|
5989
|
+
return args;
|
|
5990
|
+
}
|
|
5991
|
+
function resolveNpxCommand() {
|
|
5992
|
+
return resolveCommandOnPath(process.platform === "win32" ? "npx.cmd" : "npx") ?? resolveCommandOnPath("npx") ?? (process.platform === "win32" ? "npx.cmd" : "npx");
|
|
5993
|
+
}
|
|
5994
|
+
function resolveCodexAcpSpawn(options = {}) {
|
|
5995
|
+
if (options.args) {
|
|
5996
|
+
const command = normalizeCommandPath(options.command || readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN") || "codex-acp");
|
|
5997
|
+
return {
|
|
5998
|
+
command,
|
|
5999
|
+
args: [...options.args]
|
|
6000
|
+
};
|
|
6001
|
+
}
|
|
6002
|
+
const directArgs = buildCodexAcpConfigArgs(options);
|
|
6003
|
+
if (options.command) {
|
|
6004
|
+
return {
|
|
6005
|
+
command: normalizeCommandPath(options.command),
|
|
6006
|
+
args: directArgs
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
6009
|
+
const envOverride = readFirstEnv("HAPPY_CODEX_ACP_BIN", "HAPPY_CODEX_ACP_COMMAND", "HAPPIER_CODEX_ACP_BIN");
|
|
6010
|
+
if (envOverride) {
|
|
6011
|
+
return {
|
|
6012
|
+
command: normalizeCommandPath(envOverride),
|
|
6013
|
+
args: directArgs
|
|
6014
|
+
};
|
|
6015
|
+
}
|
|
6016
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6017
|
+
const codexAcpOnPath = resolveCommandOnPath(process.platform === "win32" ? "codex-acp.cmd" : "codex-acp") ?? resolveCommandOnPath("codex-acp");
|
|
6018
|
+
if (npxMode !== "force" && codexAcpOnPath) {
|
|
6019
|
+
return {
|
|
6020
|
+
command: codexAcpOnPath,
|
|
6021
|
+
args: directArgs
|
|
6022
|
+
};
|
|
6023
|
+
}
|
|
6024
|
+
if (npxMode === "never") {
|
|
6025
|
+
return {
|
|
6026
|
+
command: process.platform === "win32" ? "codex-acp.cmd" : "codex-acp",
|
|
6027
|
+
args: directArgs
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
return {
|
|
6031
|
+
command: resolveNpxCommand(),
|
|
6032
|
+
args: ["--prefer-offline", "-y", "@zed-industries/codex-acp", ...directArgs]
|
|
6033
|
+
};
|
|
6034
|
+
}
|
|
6035
|
+
function validateCodexAcpSpawn(options = {}) {
|
|
6036
|
+
let spawn;
|
|
6037
|
+
try {
|
|
6038
|
+
spawn = resolveCodexAcpSpawn(options);
|
|
6039
|
+
} catch (error) {
|
|
6040
|
+
return {
|
|
6041
|
+
ok: false,
|
|
6042
|
+
errorMessage: error instanceof Error ? error.message : "Codex ACP is enabled, but the command could not be resolved."
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
const normalizedCommand = spawn.command.trim();
|
|
6046
|
+
const commandLower = normalizedCommand.toLowerCase();
|
|
6047
|
+
const npxMode = readCodexAcpNpxMode();
|
|
6048
|
+
if (isAbsolute(normalizedCommand)) {
|
|
6049
|
+
if (!existsSync(normalizedCommand)) {
|
|
6050
|
+
return {
|
|
6051
|
+
ok: false,
|
|
6052
|
+
errorMessage: `Codex ACP is enabled, but the resolved command does not exist: ${normalizedCommand}`
|
|
6053
|
+
};
|
|
6054
|
+
}
|
|
6055
|
+
return { ok: true, spawn };
|
|
6056
|
+
}
|
|
6057
|
+
if (commandLower.endsWith("npx") || commandLower.endsWith("npx.cmd")) {
|
|
6058
|
+
if (isBinOnPath("npx") || isBinOnPath("npx.cmd")) {
|
|
6059
|
+
return { ok: true, spawn };
|
|
6060
|
+
}
|
|
6061
|
+
return {
|
|
6062
|
+
ok: false,
|
|
6063
|
+
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."
|
|
6064
|
+
};
|
|
6065
|
+
}
|
|
6066
|
+
if (commandLower === "codex-acp" || commandLower === "codex-acp.cmd") {
|
|
6067
|
+
if (isBinOnPath("codex-acp") || isBinOnPath("codex-acp.cmd")) {
|
|
6068
|
+
return { ok: true, spawn };
|
|
6069
|
+
}
|
|
6070
|
+
return {
|
|
6071
|
+
ok: false,
|
|
6072
|
+
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."
|
|
6073
|
+
};
|
|
6074
|
+
}
|
|
6075
|
+
return { ok: true, spawn };
|
|
5647
6076
|
}
|
|
5648
6077
|
|
|
5649
|
-
|
|
5650
|
-
|
|
6078
|
+
class CodexAcpTransport extends CodexTransport {
|
|
6079
|
+
constructor(initTimeoutMs) {
|
|
6080
|
+
super();
|
|
6081
|
+
this.initTimeoutMs = initTimeoutMs;
|
|
6082
|
+
}
|
|
6083
|
+
getInitTimeout() {
|
|
6084
|
+
return this.initTimeoutMs;
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
function resolveCodexTransport(command) {
|
|
6088
|
+
if (/npx(?:\.cmd)?$/i.test(command)) {
|
|
6089
|
+
return new CodexAcpTransport(18e4);
|
|
6090
|
+
}
|
|
6091
|
+
return new CodexAcpTransport(6e4);
|
|
5651
6092
|
}
|
|
5652
6093
|
function createCodexBackend(options) {
|
|
5653
|
-
const
|
|
5654
|
-
|
|
6094
|
+
const spawn = resolveCodexAcpSpawn({
|
|
6095
|
+
command: options.command,
|
|
6096
|
+
args: options.args,
|
|
6097
|
+
baseArgs: options.baseArgs,
|
|
6098
|
+
model: options.model,
|
|
6099
|
+
sandbox: options.sandbox,
|
|
6100
|
+
approvalPolicy: options.approvalPolicy
|
|
6101
|
+
});
|
|
5655
6102
|
const backendOptions = {
|
|
5656
6103
|
agentName: "codex",
|
|
5657
6104
|
cwd: options.cwd,
|
|
5658
|
-
command,
|
|
5659
|
-
args,
|
|
6105
|
+
command: spawn.command,
|
|
6106
|
+
args: spawn.args,
|
|
5660
6107
|
env: {
|
|
5661
6108
|
...options.env,
|
|
5662
6109
|
NODE_ENV: "production"
|
|
5663
6110
|
},
|
|
5664
6111
|
mcpServers: options.mcpServers,
|
|
5665
6112
|
permissionHandler: options.permissionHandler,
|
|
5666
|
-
|
|
6113
|
+
selectionHandler: options.selectionHandler,
|
|
6114
|
+
transportHandler: resolveCodexTransport(spawn.command)
|
|
5667
6115
|
};
|
|
5668
6116
|
return {
|
|
5669
6117
|
backend: new AcpBackend(backendOptions),
|
|
5670
|
-
command,
|
|
5671
|
-
args
|
|
6118
|
+
command: spawn.command,
|
|
6119
|
+
args: spawn.args
|
|
5672
6120
|
};
|
|
5673
6121
|
}
|
|
5674
6122
|
function registerCodexAgent() {
|
|
@@ -5854,17 +6302,26 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
5854
6302
|
env: process.env
|
|
5855
6303
|
});
|
|
5856
6304
|
daemonProcess.unref();
|
|
5857
|
-
|
|
6305
|
+
for (let i = 0; i < 100; i++) {
|
|
6306
|
+
if (await isDaemonRunningCurrentlyInstalledHappyVersion()) {
|
|
6307
|
+
return;
|
|
6308
|
+
}
|
|
6309
|
+
if (await isDaemonControlServerResponsive(500)) {
|
|
6310
|
+
return;
|
|
6311
|
+
}
|
|
6312
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
6313
|
+
}
|
|
6314
|
+
throw new Error("Failed to start Happy background service.");
|
|
5858
6315
|
}
|
|
5859
6316
|
async function executeUnifiedProvider(opts) {
|
|
5860
6317
|
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials);
|
|
5861
6318
|
if (opts.provider === "claude") {
|
|
5862
|
-
const { runClaude } = await import('./runClaude-
|
|
6319
|
+
const { runClaude } = await import('./runClaude-C_WLfM6c.mjs');
|
|
5863
6320
|
await runClaude(credentials, opts.claudeOptions ?? {});
|
|
5864
6321
|
return;
|
|
5865
6322
|
}
|
|
5866
6323
|
if (opts.provider === "codex") {
|
|
5867
|
-
const { runCodex } = await import('./runCodex-
|
|
6324
|
+
const { runCodex } = await import('./runCodex-8eWjTPJr.mjs');
|
|
5868
6325
|
await runCodex({
|
|
5869
6326
|
credentials,
|
|
5870
6327
|
startedBy: opts.startedBy,
|
|
@@ -5874,7 +6331,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5874
6331
|
return;
|
|
5875
6332
|
}
|
|
5876
6333
|
if (opts.provider === "gemini") {
|
|
5877
|
-
const { runGemini } = await import('./runGemini-
|
|
6334
|
+
const { runGemini } = await import('./runGemini-nbr0mm-S.mjs');
|
|
5878
6335
|
await runGemini({
|
|
5879
6336
|
credentials,
|
|
5880
6337
|
startedBy: opts.startedBy
|
|
@@ -5887,6 +6344,10 @@ async function executeUnifiedProvider(opts) {
|
|
|
5887
6344
|
});
|
|
5888
6345
|
}
|
|
5889
6346
|
|
|
6347
|
+
function shouldRunMainClaudeFlow(opts) {
|
|
6348
|
+
return !opts.showHelp && !opts.showVersion;
|
|
6349
|
+
}
|
|
6350
|
+
|
|
5890
6351
|
(async () => {
|
|
5891
6352
|
const args = process.argv.slice(2);
|
|
5892
6353
|
const isRemoteMode = args.includes("--happy-starting-mode") && args.includes("remote");
|
|
@@ -5895,7 +6356,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5895
6356
|
Object.defineProperty(process.stderr, "isTTY", { value: false });
|
|
5896
6357
|
}
|
|
5897
6358
|
if (!args.includes("--version")) {
|
|
5898
|
-
logger.debug("Starting
|
|
6359
|
+
logger.debug("Starting hicloud CLI with args: ", process.argv);
|
|
5899
6360
|
}
|
|
5900
6361
|
const subcommand = args[0];
|
|
5901
6362
|
if (!args.includes("--version")) ;
|
|
@@ -5912,7 +6373,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
5912
6373
|
return;
|
|
5913
6374
|
} else if (subcommand === "runtime") {
|
|
5914
6375
|
if (args[1] === "providers") {
|
|
5915
|
-
const { renderRuntimeProviders } = await import('./command-
|
|
6376
|
+
const { renderRuntimeProviders } = await import('./command-BfIuJmeo.mjs');
|
|
5916
6377
|
console.log(renderRuntimeProviders());
|
|
5917
6378
|
return;
|
|
5918
6379
|
}
|
|
@@ -5938,8 +6399,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
5938
6399
|
}
|
|
5939
6400
|
return;
|
|
5940
6401
|
}
|
|
5941
|
-
console.log("Usage:
|
|
5942
|
-
console.log("
|
|
6402
|
+
console.log("Usage: hicloud runtime providers");
|
|
6403
|
+
console.log(" hicloud runtime launch <claude|codex|gemini|cursor> [prompt]");
|
|
5943
6404
|
return;
|
|
5944
6405
|
} else if (subcommand === "auth") {
|
|
5945
6406
|
try {
|
|
@@ -6090,8 +6551,8 @@ async function executeUnifiedProvider(opts) {
|
|
|
6090
6551
|
const projectId = args[3];
|
|
6091
6552
|
try {
|
|
6092
6553
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
6093
|
-
const { readCredentials: readCredentials2 } = await import('./persistence-
|
|
6094
|
-
const { ApiClient: ApiClient2 } = await import('./api-
|
|
6554
|
+
const { readCredentials: readCredentials2 } = await import('./persistence-CzpZpiL3.mjs');
|
|
6555
|
+
const { ApiClient: ApiClient2 } = await import('./api-B5Ui8Fw0.mjs').then(function (n) { return n.q; });
|
|
6095
6556
|
let userEmail = void 0;
|
|
6096
6557
|
try {
|
|
6097
6558
|
const credentials = await readCredentials2();
|
|
@@ -6137,7 +6598,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6137
6598
|
console.log("No Google Cloud Project configured.");
|
|
6138
6599
|
console.log("");
|
|
6139
6600
|
console.log('If you see "Authentication required" error, you may need to set a project:');
|
|
6140
|
-
console.log("
|
|
6601
|
+
console.log(" hicloud gemini project set <your-project-id>");
|
|
6141
6602
|
console.log("");
|
|
6142
6603
|
console.log("This is required for Google Workspace accounts.");
|
|
6143
6604
|
console.log("Guide: https://goo.gle/gemini-cli-auth-docs#workspace-gca");
|
|
@@ -6149,7 +6610,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6149
6610
|
}
|
|
6150
6611
|
}
|
|
6151
6612
|
if (geminiSubcommand === "project" && !args[2]) {
|
|
6152
|
-
console.log("Usage:
|
|
6613
|
+
console.log("Usage: hicloud gemini project <command>");
|
|
6153
6614
|
console.log("");
|
|
6154
6615
|
console.log("Commands:");
|
|
6155
6616
|
console.log(" set <project-id> Set Google Cloud Project ID");
|
|
@@ -6181,7 +6642,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6181
6642
|
}
|
|
6182
6643
|
return;
|
|
6183
6644
|
} else if (subcommand === "logout") {
|
|
6184
|
-
console.log(chalk.yellow('Note: "
|
|
6645
|
+
console.log(chalk.yellow('Note: "hicloud logout" is deprecated. Use "hicloud auth logout" instead.\n'));
|
|
6185
6646
|
try {
|
|
6186
6647
|
await handleAuthCommand(["logout"]);
|
|
6187
6648
|
} catch (error) {
|
|
@@ -6240,7 +6701,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6240
6701
|
child.unref();
|
|
6241
6702
|
let started = false;
|
|
6242
6703
|
for (let i = 0; i < 100; i++) {
|
|
6243
|
-
if (await checkIfDaemonRunningAndCleanupStaleState()) {
|
|
6704
|
+
if (await checkIfDaemonRunningAndCleanupStaleState() && await isDaemonControlServerResponsive(500)) {
|
|
6244
6705
|
started = true;
|
|
6245
6706
|
break;
|
|
6246
6707
|
}
|
|
@@ -6297,20 +6758,20 @@ async function executeUnifiedProvider(opts) {
|
|
|
6297
6758
|
}
|
|
6298
6759
|
} else {
|
|
6299
6760
|
console.log(`
|
|
6300
|
-
${chalk.bold("
|
|
6761
|
+
${chalk.bold("hicloud daemon")} - Daemon management
|
|
6301
6762
|
|
|
6302
6763
|
${chalk.bold("Usage:")}
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6764
|
+
hicloud daemon start Start the daemon (detached)
|
|
6765
|
+
hicloud daemon stop Stop the daemon (sessions stay alive)
|
|
6766
|
+
hicloud daemon status Show daemon status
|
|
6767
|
+
hicloud daemon list List active sessions
|
|
6307
6768
|
|
|
6308
6769
|
If you want to kill all happy related processes run
|
|
6309
|
-
${chalk.cyan("
|
|
6770
|
+
${chalk.cyan("hicloud doctor clean")}
|
|
6310
6771
|
|
|
6311
6772
|
${chalk.bold("Note:")} The daemon runs in the background and manages Claude sessions.
|
|
6312
6773
|
|
|
6313
|
-
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("
|
|
6774
|
+
${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("hicloud doctor clean")}
|
|
6314
6775
|
`);
|
|
6315
6776
|
}
|
|
6316
6777
|
return;
|
|
@@ -6385,33 +6846,33 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy-cloud do
|
|
|
6385
6846
|
${chalk.bold(BRAND_CONFIG.name)} - AI \u7F16\u7A0B\u52A9\u624B
|
|
6386
6847
|
|
|
6387
6848
|
${chalk.bold("Usage:")}
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6849
|
+
hicloud [options] Start Claude with mobile control
|
|
6850
|
+
hicloud auth Manage authentication
|
|
6851
|
+
hicloud codex Start Codex mode
|
|
6852
|
+
hicloud gemini Start Gemini mode (ACP)
|
|
6853
|
+
hicloud cursor Start Cursor mode (experimental ACP)
|
|
6854
|
+
hicloud connect Connect AI vendor API keys
|
|
6855
|
+
hicloud notify Send push notification
|
|
6856
|
+
hicloud daemon Manage background service that allows
|
|
6396
6857
|
to spawn new sessions away from your computer
|
|
6397
|
-
|
|
6858
|
+
hicloud doctor System diagnostics & troubleshooting
|
|
6398
6859
|
|
|
6399
6860
|
${chalk.bold("Examples:")}
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6861
|
+
hicloud Start session
|
|
6862
|
+
hicloud --yolo Start with bypassing permissions
|
|
6863
|
+
hicloud sugar for --dangerously-skip-permissions
|
|
6864
|
+
hicloud --chrome Enable Chrome browser access for this session
|
|
6865
|
+
hicloud --no-chrome Disable Chrome even if default is on
|
|
6866
|
+
hicloud --js-runtime bun Use bun instead of node to spawn Claude Code
|
|
6867
|
+
hicloud --claude-env ANTHROPIC_BASE_URL=http://127.0.0.1:3456
|
|
6407
6868
|
Use a custom API endpoint (e.g., claude-code-router)
|
|
6408
|
-
|
|
6409
|
-
|
|
6869
|
+
hicloud auth login --force Authenticate
|
|
6870
|
+
hicloud doctor Run diagnostics
|
|
6410
6871
|
|
|
6411
6872
|
${chalk.bold(`${BRAND_CONFIG.name} supports ALL Claude options!`)}
|
|
6412
|
-
Use any claude flag with
|
|
6873
|
+
Use any claude flag with hicloud as you would with claude. Our favorite:
|
|
6413
6874
|
|
|
6414
|
-
|
|
6875
|
+
hicloud --resume
|
|
6415
6876
|
|
|
6416
6877
|
${chalk.gray("\u2500".repeat(60))}
|
|
6417
6878
|
${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
@@ -6426,8 +6887,9 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
6426
6887
|
}
|
|
6427
6888
|
if (showVersion) {
|
|
6428
6889
|
console.log(getVersionString());
|
|
6890
|
+
return;
|
|
6429
6891
|
}
|
|
6430
|
-
if (
|
|
6892
|
+
if (shouldRunMainClaudeFlow({ showHelp, showVersion })) {
|
|
6431
6893
|
printBanner();
|
|
6432
6894
|
}
|
|
6433
6895
|
try {
|
|
@@ -6463,31 +6925,31 @@ async function handleNotifyCommand(args) {
|
|
|
6463
6925
|
}
|
|
6464
6926
|
if (showHelp) {
|
|
6465
6927
|
console.log(`
|
|
6466
|
-
${chalk.bold("
|
|
6928
|
+
${chalk.bold("hicloud notify")} - Send notification
|
|
6467
6929
|
|
|
6468
6930
|
${chalk.bold("Usage:")}
|
|
6469
|
-
|
|
6470
|
-
|
|
6931
|
+
hicloud notify -p <message> [-t <title>] Send notification with custom message and optional title
|
|
6932
|
+
hicloud notify -h, --help Show this help
|
|
6471
6933
|
|
|
6472
6934
|
${chalk.bold("Options:")}
|
|
6473
6935
|
-p <message> Notification message (required)
|
|
6474
6936
|
-t <title> Notification title (optional, defaults to "Happy")
|
|
6475
6937
|
|
|
6476
6938
|
${chalk.bold("Examples:")}
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6939
|
+
hicloud notify -p "Deployment complete!"
|
|
6940
|
+
hicloud notify -p "System update complete" -t "Server Status"
|
|
6941
|
+
hicloud notify -t "Alert" -p "Database connection restored"
|
|
6480
6942
|
`);
|
|
6481
6943
|
return;
|
|
6482
6944
|
}
|
|
6483
6945
|
if (!message) {
|
|
6484
6946
|
console.error(chalk.red('Error: Message is required. Use -p "your message" to specify the notification text.'));
|
|
6485
|
-
console.log(chalk.gray('Run "
|
|
6947
|
+
console.log(chalk.gray('Run "hicloud notify --help" for usage information.'));
|
|
6486
6948
|
process.exit(1);
|
|
6487
6949
|
}
|
|
6488
6950
|
let credentials = await readCredentials();
|
|
6489
6951
|
if (!credentials) {
|
|
6490
|
-
console.error(chalk.red('Error: Not authenticated. Please run "
|
|
6952
|
+
console.error(chalk.red('Error: Not authenticated. Please run "hicloud auth login" first.'));
|
|
6491
6953
|
process.exit(1);
|
|
6492
6954
|
}
|
|
6493
6955
|
console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
|
|
@@ -6513,4 +6975,4 @@ ${chalk.bold("Examples:")}
|
|
|
6513
6975
|
}
|
|
6514
6976
|
}
|
|
6515
6977
|
|
|
6516
|
-
export { ExitCodeError as E, GEMINI_MODEL_ENV as G, createGeminiBackend as a, stopCaffeinate as b, createDefaultRuntimeShell as c,
|
|
6978
|
+
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 };
|