happy-imou-cloud 2.0.2 → 2.0.4
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-B6tJ_eL5.cjs → BaseReasoningProcessor-DEEfNi5Y.cjs} +4 -4
- package/dist/{BaseReasoningProcessor-D8VhEbs2.mjs → BaseReasoningProcessor-Di1yEMMv.mjs} +2 -2
- package/dist/{api-MYhAGPLn.mjs → api-CIHTNilH.mjs} +2 -2
- package/dist/{api-D2Njw9Im.cjs → api-CyJG1mr6.cjs} +43 -43
- package/dist/{command-nmK6O-ab.mjs → command-BERqmFB0.mjs} +3 -3
- package/dist/{command-CVldr51S.cjs → command-CPlJKXDn.cjs} +3 -3
- package/dist/{index-Bg-YziG2.cjs → index-1zlH6s7a.cjs} +313 -118
- package/dist/{index-B97L7qLD.mjs → index-vNYxNqVZ.mjs} +226 -31
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-D_2GkJAO.cjs → persistence-BeFVx6kI.cjs} +28 -28
- package/dist/{persistence-Dkm7rm8k.mjs → persistence-sLEqV8vk.mjs} +1 -1
- package/dist/{registerKillSessionHandler-BAXmJQRt.cjs → registerKillSessionHandler-CCxqGFjZ.cjs} +2 -2
- package/dist/{registerKillSessionHandler-5GbrO0FM.mjs → registerKillSessionHandler-uVHqIC4h.mjs} +2 -2
- package/dist/{runClaude-Cii3R2Fv.mjs → runClaude-Dl9nIRIg.mjs} +25 -5
- package/dist/{runClaude-B-GNEkKg.cjs → runClaude-Dz-PCSvb.cjs} +53 -33
- package/dist/{runCodex-CPHyGwj9.cjs → runCodex-BtZplK1R.cjs} +275 -408
- package/dist/{runCodex-C--ZwAhl.mjs → runCodex-DgKKw3IU.mjs} +273 -409
- package/dist/{runGemini-CQp7Nuzn.mjs → runGemini-CM1v3I24.mjs} +10 -8
- package/dist/{runGemini-DaDz1bzQ.cjs → runGemini-DUyH311Z.cjs} +10 -8
- package/package.json +1 -1
- package/dist/future-Dq4Ha1Dn.cjs +0 -24
- package/dist/future-xRdLl3vf.mjs +0 -22
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var api = require('./api-
|
|
5
|
-
var persistence = require('./persistence-
|
|
4
|
+
var api = require('./api-CyJG1mr6.cjs');
|
|
5
|
+
var persistence = require('./persistence-BeFVx6kI.cjs');
|
|
6
6
|
var z = require('zod');
|
|
7
|
-
var fs$
|
|
7
|
+
var fs$1 = require('fs/promises');
|
|
8
8
|
var os$1 = require('os');
|
|
9
9
|
var tmp = require('tmp');
|
|
10
10
|
var node_crypto = require('node:crypto');
|
|
@@ -14,18 +14,18 @@ var qrcode = require('qrcode-terminal');
|
|
|
14
14
|
var promises = require('node:fs/promises');
|
|
15
15
|
var node_module = require('node:module');
|
|
16
16
|
var os = require('node:os');
|
|
17
|
-
var
|
|
17
|
+
var node_path = require('node:path');
|
|
18
18
|
var open = require('open');
|
|
19
19
|
var React = require('react');
|
|
20
20
|
var ink = require('ink');
|
|
21
21
|
var child_process = require('child_process');
|
|
22
|
-
var path
|
|
22
|
+
var path = require('path');
|
|
23
23
|
var url = require('url');
|
|
24
|
-
var fs
|
|
24
|
+
var fs = require('fs');
|
|
25
25
|
var node_child_process = require('node:child_process');
|
|
26
26
|
var psList = require('ps-list');
|
|
27
27
|
var spawn = require('cross-spawn');
|
|
28
|
-
var
|
|
28
|
+
var node_fs = require('node:fs');
|
|
29
29
|
var fastify = require('fastify');
|
|
30
30
|
var fastifyTypeProviderZod = require('fastify-type-provider-zod');
|
|
31
31
|
var node_readline = require('node:readline');
|
|
@@ -70,7 +70,7 @@ async function openBrowser(url) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
73
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-1zlH6s7a.cjs', document.baseURI).href)));
|
|
74
74
|
const QRCode = require$1("qrcode-terminal/vendor/QRCode");
|
|
75
75
|
const QRErrorCorrectLevel = require$1("qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel");
|
|
76
76
|
const pendingTempFiles = /* @__PURE__ */ new Set();
|
|
@@ -164,7 +164,7 @@ async function openWindowsQrCode(value) {
|
|
|
164
164
|
"</body>",
|
|
165
165
|
"</html>"
|
|
166
166
|
].join("");
|
|
167
|
-
const filePath =
|
|
167
|
+
const filePath = node_path.join(os.tmpdir(), `happy-auth-qrcode-${node_crypto.randomUUID()}.html`);
|
|
168
168
|
await promises.writeFile(filePath, html, "utf8");
|
|
169
169
|
const opened = await openBrowser(filePath);
|
|
170
170
|
if (opened) {
|
|
@@ -693,18 +693,18 @@ function setupCleanupHandlers() {
|
|
|
693
693
|
});
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
-
const __dirname$1 = path
|
|
696
|
+
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-1zlH6s7a.cjs', document.baseURI).href))));
|
|
697
697
|
function projectPath() {
|
|
698
|
-
const path = path
|
|
699
|
-
return path;
|
|
698
|
+
const path$1 = path.resolve(__dirname$1, "..");
|
|
699
|
+
return path$1;
|
|
700
700
|
}
|
|
701
701
|
|
|
702
702
|
function getDaemonPid() {
|
|
703
703
|
try {
|
|
704
|
-
if (!
|
|
704
|
+
if (!node_fs.existsSync(api.configuration.daemonStateFile)) {
|
|
705
705
|
return null;
|
|
706
706
|
}
|
|
707
|
-
const state = JSON.parse(
|
|
707
|
+
const state = JSON.parse(node_fs.readFileSync(api.configuration.daemonStateFile, "utf-8"));
|
|
708
708
|
return typeof state.pid === "number" ? state.pid : null;
|
|
709
709
|
} catch {
|
|
710
710
|
return null;
|
|
@@ -1030,8 +1030,8 @@ async function isDaemonRunningCurrentlyInstalledHappyVersion() {
|
|
|
1030
1030
|
return false;
|
|
1031
1031
|
}
|
|
1032
1032
|
try {
|
|
1033
|
-
const packageJsonPath = path
|
|
1034
|
-
const packageJson = JSON.parse(fs
|
|
1033
|
+
const packageJsonPath = path.join(projectPath(), "package.json");
|
|
1034
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1035
1035
|
const currentCliVersion = packageJson.version;
|
|
1036
1036
|
api.logger.debug(`[DAEMON CONTROL] Current CLI version: ${currentCliVersion}, Daemon started with version: ${state.startedWithCliVersion}`);
|
|
1037
1037
|
if (currentCliVersion !== state.startedWithCliVersion) {
|
|
@@ -1131,14 +1131,14 @@ function getEnvironmentInfo() {
|
|
|
1131
1131
|
};
|
|
1132
1132
|
}
|
|
1133
1133
|
function getLogFiles(logDir) {
|
|
1134
|
-
if (!
|
|
1134
|
+
if (!node_fs.existsSync(logDir)) {
|
|
1135
1135
|
return [];
|
|
1136
1136
|
}
|
|
1137
1137
|
try {
|
|
1138
|
-
return
|
|
1139
|
-
const path
|
|
1140
|
-
const stats =
|
|
1141
|
-
return { file, path
|
|
1138
|
+
return node_fs.readdirSync(logDir).filter((file) => file.endsWith(".log")).map((file) => {
|
|
1139
|
+
const path = node_path.join(logDir, file);
|
|
1140
|
+
const stats = node_fs.statSync(path);
|
|
1141
|
+
return { file, path, modified: stats.mtime };
|
|
1142
1142
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
1143
1143
|
} catch {
|
|
1144
1144
|
return [];
|
|
@@ -1157,13 +1157,13 @@ async function runDoctorCommand(filter) {
|
|
|
1157
1157
|
console.log("");
|
|
1158
1158
|
console.log(chalk.bold("\u{1F527} Daemon Spawn Diagnostics"));
|
|
1159
1159
|
const projectRoot = projectPath();
|
|
1160
|
-
const wrapperPath =
|
|
1161
|
-
const cliEntrypoint =
|
|
1160
|
+
const wrapperPath = node_path.join(projectRoot, "bin", "happy.mjs");
|
|
1161
|
+
const cliEntrypoint = node_path.join(projectRoot, "dist", "index.mjs");
|
|
1162
1162
|
console.log(`Project Root: ${chalk.blue(projectRoot)}`);
|
|
1163
1163
|
console.log(`Wrapper Script: ${chalk.blue(wrapperPath)}`);
|
|
1164
1164
|
console.log(`CLI Entrypoint: ${chalk.blue(cliEntrypoint)}`);
|
|
1165
|
-
console.log(`Wrapper Exists: ${
|
|
1166
|
-
console.log(`CLI Exists: ${
|
|
1165
|
+
console.log(`Wrapper Exists: ${node_fs.existsSync(wrapperPath) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1166
|
+
console.log(`CLI Exists: ${node_fs.existsSync(cliEntrypoint) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
|
|
1167
1167
|
console.log("");
|
|
1168
1168
|
console.log(chalk.bold("\u2699\uFE0F Configuration"));
|
|
1169
1169
|
console.log(`Happy Home: ${chalk.blue(api.configuration.happyCloudHomeDir)}`);
|
|
@@ -1329,7 +1329,7 @@ const isBun = () => getRuntime() === "bun";
|
|
|
1329
1329
|
|
|
1330
1330
|
function spawnHappyCLI(args, options = {}) {
|
|
1331
1331
|
const projectRoot = projectPath();
|
|
1332
|
-
const entrypoint =
|
|
1332
|
+
const entrypoint = node_path.join(projectRoot, "dist", "index.mjs");
|
|
1333
1333
|
let directory;
|
|
1334
1334
|
if ("cwd" in options) {
|
|
1335
1335
|
directory = options.cwd;
|
|
@@ -1344,7 +1344,7 @@ function spawnHappyCLI(args, options = {}) {
|
|
|
1344
1344
|
entrypoint,
|
|
1345
1345
|
...args
|
|
1346
1346
|
];
|
|
1347
|
-
if (!
|
|
1347
|
+
if (!node_fs.existsSync(entrypoint)) {
|
|
1348
1348
|
const errorMessage = `Entrypoint ${entrypoint} does not exist`;
|
|
1349
1349
|
api.logger.debug(`[SPAWN HAPPY CLOUD CLI] ${errorMessage}`);
|
|
1350
1350
|
throw new Error(errorMessage);
|
|
@@ -2404,7 +2404,7 @@ async function startDaemon() {
|
|
|
2404
2404
|
const { directory, sessionId, machineId: machineId2, approvedNewDirectoryCreation = true } = options;
|
|
2405
2405
|
let directoryCreated = false;
|
|
2406
2406
|
try {
|
|
2407
|
-
await fs$
|
|
2407
|
+
await fs$1.access(directory);
|
|
2408
2408
|
api.logger.debug(`[DAEMON RUN] Directory exists: ${directory}`);
|
|
2409
2409
|
} catch (error) {
|
|
2410
2410
|
api.logger.debug(`[DAEMON RUN] Directory doesn't exist, creating: ${directory}`);
|
|
@@ -2416,7 +2416,7 @@ async function startDaemon() {
|
|
|
2416
2416
|
};
|
|
2417
2417
|
}
|
|
2418
2418
|
try {
|
|
2419
|
-
await fs$
|
|
2419
|
+
await fs$1.mkdir(directory, { recursive: true });
|
|
2420
2420
|
api.logger.debug(`[DAEMON RUN] Successfully created directory: ${directory}`);
|
|
2421
2421
|
directoryCreated = true;
|
|
2422
2422
|
} catch (mkdirError) {
|
|
@@ -2445,7 +2445,7 @@ async function startDaemon() {
|
|
|
2445
2445
|
if (options.token) {
|
|
2446
2446
|
if (options.agent === "codex") {
|
|
2447
2447
|
const codexHomeDir = tmp__namespace.dirSync();
|
|
2448
|
-
fs$
|
|
2448
|
+
fs$1.writeFile(path.join(codexHomeDir.name, "auth.json"), options.token);
|
|
2449
2449
|
authEnv.CODEX_HOME = codexHomeDir.name;
|
|
2450
2450
|
} else {
|
|
2451
2451
|
authEnv.CLAUDE_CODE_OAUTH_TOKEN = options.token;
|
|
@@ -2511,7 +2511,7 @@ async function startDaemon() {
|
|
|
2511
2511
|
const sessionDesc = tmuxSessionName || "current/most recent session";
|
|
2512
2512
|
api.logger.debug(`[DAEMON RUN] Attempting to spawn session in tmux: ${sessionDesc}`);
|
|
2513
2513
|
const tmux = getTmuxUtilities(tmuxSessionName);
|
|
2514
|
-
const cliPath = path
|
|
2514
|
+
const cliPath = path.join(projectPath(), "dist", "index.mjs");
|
|
2515
2515
|
const agent = resolveDaemonSpawnAgent(options.agent);
|
|
2516
2516
|
const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${buildDaemonSpawnArgs(agent).join(" ")}`;
|
|
2517
2517
|
const windowName = `happy-${Date.now()}-${agent}`;
|
|
@@ -2757,7 +2757,7 @@ async function startDaemon() {
|
|
|
2757
2757
|
pidToTrackedSession.delete(pid);
|
|
2758
2758
|
}
|
|
2759
2759
|
}
|
|
2760
|
-
const projectVersion = JSON.parse(fs
|
|
2760
|
+
const projectVersion = JSON.parse(fs.readFileSync(path.join(projectPath(), "package.json"), "utf-8")).version;
|
|
2761
2761
|
if (projectVersion !== api.configuration.currentCliVersion) {
|
|
2762
2762
|
api.logger.debug("[DAEMON RUN] Daemon is outdated, triggering self-restart with latest version, clearing heartbeat interval");
|
|
2763
2763
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
@@ -2850,7 +2850,7 @@ const PLIST_LABEL$1 = "com.happy-cloud.daemon";
|
|
|
2850
2850
|
const PLIST_FILE$1 = `/Library/LaunchDaemons/${PLIST_LABEL$1}.plist`;
|
|
2851
2851
|
async function install$1() {
|
|
2852
2852
|
try {
|
|
2853
|
-
if (fs
|
|
2853
|
+
if (fs.existsSync(PLIST_FILE$1)) {
|
|
2854
2854
|
api.logger.info("Daemon plist already exists. Uninstalling first...");
|
|
2855
2855
|
child_process.execSync(`launchctl unload ${PLIST_FILE$1}`, { stdio: "inherit" });
|
|
2856
2856
|
}
|
|
@@ -2894,8 +2894,8 @@ async function install$1() {
|
|
|
2894
2894
|
</dict>
|
|
2895
2895
|
</plist>
|
|
2896
2896
|
`);
|
|
2897
|
-
fs
|
|
2898
|
-
fs
|
|
2897
|
+
fs.writeFileSync(PLIST_FILE$1, plistContent);
|
|
2898
|
+
fs.chmodSync(PLIST_FILE$1, 420);
|
|
2899
2899
|
api.logger.info(`Created daemon plist at ${PLIST_FILE$1}`);
|
|
2900
2900
|
child_process.execSync(`launchctl load ${PLIST_FILE$1}`, { stdio: "inherit" });
|
|
2901
2901
|
api.logger.info("Daemon installed and started successfully");
|
|
@@ -2921,7 +2921,7 @@ const PLIST_LABEL = "com.happy-cli.daemon";
|
|
|
2921
2921
|
const PLIST_FILE = `/Library/LaunchDaemons/${PLIST_LABEL}.plist`;
|
|
2922
2922
|
async function uninstall$1() {
|
|
2923
2923
|
try {
|
|
2924
|
-
if (!fs
|
|
2924
|
+
if (!fs.existsSync(PLIST_FILE)) {
|
|
2925
2925
|
api.logger.info("Daemon plist not found. Nothing to uninstall.");
|
|
2926
2926
|
return;
|
|
2927
2927
|
}
|
|
@@ -2931,7 +2931,7 @@ async function uninstall$1() {
|
|
|
2931
2931
|
} catch (error) {
|
|
2932
2932
|
api.logger.info("Failed to unload daemon (it might not be running)");
|
|
2933
2933
|
}
|
|
2934
|
-
fs
|
|
2934
|
+
fs.unlinkSync(PLIST_FILE);
|
|
2935
2935
|
api.logger.info(`Removed daemon plist from ${PLIST_FILE}`);
|
|
2936
2936
|
api.logger.info("Daemon uninstalled successfully");
|
|
2937
2937
|
} catch (error) {
|
|
@@ -3071,8 +3071,8 @@ async function handleAuthLogout(args = []) {
|
|
|
3071
3071
|
console.log(chalk.gray("Stopped daemon"));
|
|
3072
3072
|
} catch {
|
|
3073
3073
|
}
|
|
3074
|
-
if (
|
|
3075
|
-
|
|
3074
|
+
if (node_fs.existsSync(happyDir)) {
|
|
3075
|
+
node_fs.rmSync(happyDir, { recursive: true, force: true });
|
|
3076
3076
|
}
|
|
3077
3077
|
console.log(chalk.green("\u2713 Successfully logged out"));
|
|
3078
3078
|
console.log(chalk.gray(' Run "hicloud auth login" to authenticate again'));
|
|
@@ -3745,10 +3745,10 @@ async function handleConnectStatus() {
|
|
|
3745
3745
|
}
|
|
3746
3746
|
function updateLocalGeminiCredentials(tokens) {
|
|
3747
3747
|
try {
|
|
3748
|
-
const geminiDir = path
|
|
3749
|
-
const credentialsPath = path
|
|
3750
|
-
if (!fs
|
|
3751
|
-
fs
|
|
3748
|
+
const geminiDir = path.join(os$1.homedir(), ".gemini");
|
|
3749
|
+
const credentialsPath = path.join(geminiDir, "oauth_creds.json");
|
|
3750
|
+
if (!fs.existsSync(geminiDir)) {
|
|
3751
|
+
fs.mkdirSync(geminiDir, { recursive: true });
|
|
3752
3752
|
}
|
|
3753
3753
|
const credentials = {
|
|
3754
3754
|
access_token: tokens.access_token,
|
|
@@ -3758,7 +3758,7 @@ function updateLocalGeminiCredentials(tokens) {
|
|
|
3758
3758
|
...tokens.id_token && { id_token: tokens.id_token },
|
|
3759
3759
|
...tokens.expires_in && { expires_in: tokens.expires_in }
|
|
3760
3760
|
};
|
|
3761
|
-
fs
|
|
3761
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), "utf-8");
|
|
3762
3762
|
console.log(chalk.gray(` Updated local credentials: ${credentialsPath}`));
|
|
3763
3763
|
} catch (error) {
|
|
3764
3764
|
console.log(chalk.yellow(` \u26A0\uFE0F Could not update local credentials: ${error}`));
|
|
@@ -3766,22 +3766,22 @@ function updateLocalGeminiCredentials(tokens) {
|
|
|
3766
3766
|
}
|
|
3767
3767
|
|
|
3768
3768
|
function getProjectPath(workingDirectory, claudeConfigDirOverride) {
|
|
3769
|
-
const projectId =
|
|
3769
|
+
const projectId = node_path.resolve(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
3770
3770
|
const claudeConfigDirRaw = process.env.CLAUDE_CONFIG_DIR ?? "";
|
|
3771
3771
|
const claudeConfigDirTrimmed = claudeConfigDirRaw.trim();
|
|
3772
|
-
const claudeConfigDir = claudeConfigDirTrimmed ? claudeConfigDirTrimmed :
|
|
3773
|
-
return
|
|
3772
|
+
const claudeConfigDir = claudeConfigDirTrimmed ? claudeConfigDirTrimmed : node_path.join(os.homedir(), ".claude");
|
|
3773
|
+
return node_path.join(claudeConfigDir, "projects", projectId);
|
|
3774
3774
|
}
|
|
3775
3775
|
|
|
3776
|
-
function claudeCheckSession(sessionId, path
|
|
3777
|
-
const projectDir = getProjectPath(path
|
|
3778
|
-
const sessionFile = transcriptPath ??
|
|
3779
|
-
const sessionExists =
|
|
3776
|
+
function claudeCheckSession(sessionId, path, transcriptPath) {
|
|
3777
|
+
const projectDir = getProjectPath(path);
|
|
3778
|
+
const sessionFile = transcriptPath ?? node_path.join(projectDir, `${sessionId}.jsonl`);
|
|
3779
|
+
const sessionExists = node_fs.existsSync(sessionFile);
|
|
3780
3780
|
if (!sessionExists) {
|
|
3781
3781
|
api.logger.debug(`[claudeCheckSession] Path ${sessionFile} does not exist`);
|
|
3782
3782
|
return false;
|
|
3783
3783
|
}
|
|
3784
|
-
const sessionData =
|
|
3784
|
+
const sessionData = node_fs.readFileSync(sessionFile, "utf-8").split("\n");
|
|
3785
3785
|
const hasGoodMessage = !!sessionData.find((v, index) => {
|
|
3786
3786
|
if (!v.trim()) return false;
|
|
3787
3787
|
try {
|
|
@@ -3802,7 +3802,7 @@ function claudeFindLastSession(workingDirectory) {
|
|
|
3802
3802
|
try {
|
|
3803
3803
|
const projectDir = getProjectPath(workingDirectory);
|
|
3804
3804
|
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3805
|
-
const files =
|
|
3805
|
+
const files = node_fs.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
3806
3806
|
const sessionId = f.replace(".jsonl", "");
|
|
3807
3807
|
if (!uuidPattern.test(sessionId)) {
|
|
3808
3808
|
return null;
|
|
@@ -3811,7 +3811,7 @@ function claudeFindLastSession(workingDirectory) {
|
|
|
3811
3811
|
return {
|
|
3812
3812
|
name: f,
|
|
3813
3813
|
sessionId,
|
|
3814
|
-
mtime:
|
|
3814
|
+
mtime: node_fs.statSync(node_path.join(projectDir, f)).mtime.getTime()
|
|
3815
3815
|
};
|
|
3816
3816
|
}
|
|
3817
3817
|
return null;
|
|
@@ -3831,10 +3831,10 @@ class ExitCodeError extends Error {
|
|
|
3831
3831
|
this.exitCode = exitCode;
|
|
3832
3832
|
}
|
|
3833
3833
|
}
|
|
3834
|
-
const claudeCliPath =
|
|
3834
|
+
const claudeCliPath = node_path.resolve(node_path.join(projectPath(), "scripts", "claude_local_launcher.cjs"));
|
|
3835
3835
|
async function claudeLocal(opts) {
|
|
3836
3836
|
const projectDir = getProjectPath(opts.path);
|
|
3837
|
-
|
|
3837
|
+
node_fs.mkdirSync(projectDir, { recursive: true });
|
|
3838
3838
|
const hasContinueFlag = opts.claudeArgs?.includes("--continue");
|
|
3839
3839
|
const hasResumeFlag = opts.claudeArgs?.includes("--resume");
|
|
3840
3840
|
const hasUserSessionControl = hasContinueFlag || hasResumeFlag;
|
|
@@ -3953,7 +3953,7 @@ async function claudeLocal(opts) {
|
|
|
3953
3953
|
args.push("--settings", opts.hookSettingsPath);
|
|
3954
3954
|
api.logger.debug(`[ClaudeLocal] Using hook settings: ${opts.hookSettingsPath}`);
|
|
3955
3955
|
}
|
|
3956
|
-
if (!claudeCliPath || !
|
|
3956
|
+
if (!claudeCliPath || !node_fs.existsSync(claudeCliPath)) {
|
|
3957
3957
|
throw new Error("Claude local launcher not found. Please ensure HAPPY_PROJECT_ROOT is set correctly for development.");
|
|
3958
3958
|
}
|
|
3959
3959
|
const env = {
|
|
@@ -4527,6 +4527,107 @@ function parseArgsFromContent(content) {
|
|
|
4527
4527
|
}
|
|
4528
4528
|
return {};
|
|
4529
4529
|
}
|
|
4530
|
+
function appendToolOutput(existing, next) {
|
|
4531
|
+
if (!existing || existing.length === 0) {
|
|
4532
|
+
return next;
|
|
4533
|
+
}
|
|
4534
|
+
if (next === existing || existing.endsWith(next)) {
|
|
4535
|
+
return existing;
|
|
4536
|
+
}
|
|
4537
|
+
if (next.startsWith(existing)) {
|
|
4538
|
+
return next;
|
|
4539
|
+
}
|
|
4540
|
+
return `${existing}${next}`;
|
|
4541
|
+
}
|
|
4542
|
+
function isRecord(value) {
|
|
4543
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4544
|
+
}
|
|
4545
|
+
function hasMeaningfulContent(value) {
|
|
4546
|
+
if (value === null || value === void 0) {
|
|
4547
|
+
return false;
|
|
4548
|
+
}
|
|
4549
|
+
if (typeof value === "string") {
|
|
4550
|
+
return value.length > 0;
|
|
4551
|
+
}
|
|
4552
|
+
if (Array.isArray(value)) {
|
|
4553
|
+
return value.length > 0;
|
|
4554
|
+
}
|
|
4555
|
+
if (isRecord(value)) {
|
|
4556
|
+
return Object.keys(value).length > 0;
|
|
4557
|
+
}
|
|
4558
|
+
return true;
|
|
4559
|
+
}
|
|
4560
|
+
function looksLikeToolMetadata(record) {
|
|
4561
|
+
const metadataKeys = [
|
|
4562
|
+
"command",
|
|
4563
|
+
"cmd",
|
|
4564
|
+
"script",
|
|
4565
|
+
"argv",
|
|
4566
|
+
"cwd",
|
|
4567
|
+
"workingDirectory",
|
|
4568
|
+
"description",
|
|
4569
|
+
"title",
|
|
4570
|
+
"parsed_cmd"
|
|
4571
|
+
];
|
|
4572
|
+
if (metadataKeys.some((key) => key in record)) {
|
|
4573
|
+
return true;
|
|
4574
|
+
}
|
|
4575
|
+
const nestedKeys = ["input", "toolCall", "arguments", "content"];
|
|
4576
|
+
for (const key of nestedKeys) {
|
|
4577
|
+
const nested = record[key];
|
|
4578
|
+
if (isRecord(nested) && looksLikeToolMetadata(nested)) {
|
|
4579
|
+
return true;
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return false;
|
|
4583
|
+
}
|
|
4584
|
+
function extractToolOutputChunk(content) {
|
|
4585
|
+
if (typeof content === "string") {
|
|
4586
|
+
return content.length > 0 ? content : null;
|
|
4587
|
+
}
|
|
4588
|
+
if (Array.isArray(content)) {
|
|
4589
|
+
const parts = content.map((item) => extractToolOutputChunk(item)).filter((item) => Boolean(item));
|
|
4590
|
+
return parts.length > 0 ? parts.join("") : null;
|
|
4591
|
+
}
|
|
4592
|
+
if (!isRecord(content)) {
|
|
4593
|
+
return null;
|
|
4594
|
+
}
|
|
4595
|
+
const outputKeys = ["stdout", "stderr", "output", "text", "message", "data", "error", "reason"];
|
|
4596
|
+
const hasOutputKey = outputKeys.some((key) => key in content);
|
|
4597
|
+
if (!hasOutputKey && looksLikeToolMetadata(content)) {
|
|
4598
|
+
return null;
|
|
4599
|
+
}
|
|
4600
|
+
for (const key of outputKeys) {
|
|
4601
|
+
if (!(key in content)) {
|
|
4602
|
+
continue;
|
|
4603
|
+
}
|
|
4604
|
+
const value = content[key];
|
|
4605
|
+
const formatted2 = typeof value === "string" ? value : formatDisplayMessage(value);
|
|
4606
|
+
if (formatted2.length > 0) {
|
|
4607
|
+
return formatted2;
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
const formatted = formatDisplayMessage(content);
|
|
4611
|
+
return formatted.length > 0 ? formatted : null;
|
|
4612
|
+
}
|
|
4613
|
+
function mergeStreamedOutputWithResult(content, streamedOutput) {
|
|
4614
|
+
if (!streamedOutput || streamedOutput.length === 0) {
|
|
4615
|
+
return content;
|
|
4616
|
+
}
|
|
4617
|
+
if (!hasMeaningfulContent(content)) {
|
|
4618
|
+
return streamedOutput;
|
|
4619
|
+
}
|
|
4620
|
+
if (isRecord(content)) {
|
|
4621
|
+
const hasStructuredOutput = ["stdout", "stderr", "output", "text", "message", "data"].some((key) => key in content);
|
|
4622
|
+
if (!hasStructuredOutput) {
|
|
4623
|
+
return {
|
|
4624
|
+
...content,
|
|
4625
|
+
stdout: streamedOutput
|
|
4626
|
+
};
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
return content;
|
|
4630
|
+
}
|
|
4530
4631
|
function extractErrorDetail(content) {
|
|
4531
4632
|
if (!content) return void 0;
|
|
4532
4633
|
if (typeof content === "string") {
|
|
@@ -4674,11 +4775,13 @@ function completeToolCall(toolCallId, toolKind, content, ctx) {
|
|
|
4674
4775
|
clearTimeout(timeout);
|
|
4675
4776
|
ctx.toolCallTimeouts.delete(toolCallId);
|
|
4676
4777
|
}
|
|
4778
|
+
const streamedOutput = ctx.toolCallOutputs.get(toolCallId);
|
|
4779
|
+
ctx.toolCallOutputs.delete(toolCallId);
|
|
4677
4780
|
api.logger.debug(`[AcpBackend] \u2705 Tool call COMPLETED: ${toolCallId} (${toolKindStr}) - Duration: ${duration}. Active tool calls: ${ctx.activeToolCalls.size}`);
|
|
4678
4781
|
ctx.emit({
|
|
4679
4782
|
type: "tool-result",
|
|
4680
4783
|
toolName: toolKindStr,
|
|
4681
|
-
result: content,
|
|
4784
|
+
result: mergeStreamedOutputWithResult(content, streamedOutput),
|
|
4682
4785
|
callId: toolCallId
|
|
4683
4786
|
});
|
|
4684
4787
|
if (ctx.activeToolCalls.size === 0) {
|
|
@@ -4720,6 +4823,8 @@ function failToolCall(toolCallId, status, toolKind, content, ctx) {
|
|
|
4720
4823
|
}
|
|
4721
4824
|
const durationStr = formatDuration(startTime);
|
|
4722
4825
|
api.logger.debug(`[AcpBackend] \u274C Tool call ${status.toUpperCase()}: ${toolCallId} (${toolKindStr}) - Duration: ${durationStr}. Active tool calls: ${ctx.activeToolCalls.size}`);
|
|
4826
|
+
const streamedOutput = ctx.toolCallOutputs.get(toolCallId);
|
|
4827
|
+
ctx.toolCallOutputs.delete(toolCallId);
|
|
4723
4828
|
const errorDetail = extractErrorDetail(content);
|
|
4724
4829
|
if (errorDetail) {
|
|
4725
4830
|
api.logger.debug(`[AcpBackend] \u274C Tool call error details: ${errorDetail.substring(0, 500)}`);
|
|
@@ -4729,7 +4834,11 @@ function failToolCall(toolCallId, status, toolKind, content, ctx) {
|
|
|
4729
4834
|
ctx.emit({
|
|
4730
4835
|
type: "tool-result",
|
|
4731
4836
|
toolName: toolKindStr,
|
|
4732
|
-
result:
|
|
4837
|
+
result: streamedOutput ? {
|
|
4838
|
+
stdout: streamedOutput,
|
|
4839
|
+
error: errorDetail || `Tool call ${status}`,
|
|
4840
|
+
status
|
|
4841
|
+
} : errorDetail ? { error: errorDetail, status } : { error: `Tool call ${status}`, status },
|
|
4733
4842
|
callId: toolCallId
|
|
4734
4843
|
});
|
|
4735
4844
|
if (ctx.activeToolCalls.size === 0) {
|
|
@@ -4745,8 +4854,13 @@ function handleToolCallUpdate(update, ctx) {
|
|
|
4745
4854
|
api.logger.debug("[AcpBackend] Tool call update without toolCallId:", update);
|
|
4746
4855
|
return { handled: false };
|
|
4747
4856
|
}
|
|
4748
|
-
const toolKind = update.kind || "unknown";
|
|
4857
|
+
const toolKind = update.kind || ctx.toolCallIdToNameMap.get(toolCallId) || "unknown";
|
|
4749
4858
|
let toolCallCountSincePrompt = ctx.toolCallCountSincePrompt;
|
|
4859
|
+
const outputChunk = extractToolOutputChunk(update.content);
|
|
4860
|
+
if (outputChunk) {
|
|
4861
|
+
const nextOutput = appendToolOutput(ctx.toolCallOutputs.get(toolCallId), outputChunk);
|
|
4862
|
+
ctx.toolCallOutputs.set(toolCallId, nextOutput);
|
|
4863
|
+
}
|
|
4750
4864
|
if (status === "in_progress" || status === "pending") {
|
|
4751
4865
|
if (!ctx.activeToolCalls.has(toolCallId)) {
|
|
4752
4866
|
toolCallCountSincePrompt++;
|
|
@@ -4995,6 +5109,11 @@ function normalizeAcpError(error) {
|
|
|
4995
5109
|
}
|
|
4996
5110
|
return normalized;
|
|
4997
5111
|
}
|
|
5112
|
+
function createAcpAbortError(message) {
|
|
5113
|
+
const error = new Error(message);
|
|
5114
|
+
error.name = "AbortError";
|
|
5115
|
+
return error;
|
|
5116
|
+
}
|
|
4998
5117
|
function enrichAcpError(error, stderrExcerpt) {
|
|
4999
5118
|
const normalized = normalizeAcpError(error);
|
|
5000
5119
|
if (!stderrExcerpt.trim()) {
|
|
@@ -5021,6 +5140,8 @@ class AcpBackend {
|
|
|
5021
5140
|
toolCallTimeouts = /* @__PURE__ */ new Map();
|
|
5022
5141
|
/** Track tool call start times for performance monitoring */
|
|
5023
5142
|
toolCallStartTimes = /* @__PURE__ */ new Map();
|
|
5143
|
+
/** Track streamed tool output between ACP updates and final completion */
|
|
5144
|
+
toolCallOutputs = /* @__PURE__ */ new Map();
|
|
5024
5145
|
/** Pending permission requests that need response */
|
|
5025
5146
|
pendingPermissions = /* @__PURE__ */ new Map();
|
|
5026
5147
|
/** Map from permission request ID to real tool call ID for tracking */
|
|
@@ -5031,6 +5152,14 @@ class AcpBackend {
|
|
|
5031
5152
|
toolCallCountSincePrompt = 0;
|
|
5032
5153
|
/** Timeout for emitting 'idle' status after last message chunk */
|
|
5033
5154
|
idleTimeout = null;
|
|
5155
|
+
/** Promise resolver for waitForResponseComplete */
|
|
5156
|
+
idleResolver = null;
|
|
5157
|
+
/** Promise rejecter for waitForResponseComplete */
|
|
5158
|
+
idleRejecter = null;
|
|
5159
|
+
/** Completion signal captured before waitForResponseComplete is attached */
|
|
5160
|
+
responseCompletionOutcome = null;
|
|
5161
|
+
/** Whether the current prompt is still waiting for completion */
|
|
5162
|
+
waitingForResponse = false;
|
|
5034
5163
|
/** Transport handler for agent-specific behavior */
|
|
5035
5164
|
transport;
|
|
5036
5165
|
/** Keep a short rolling stderr buffer so startup failures can surface the real cause. */
|
|
@@ -5048,6 +5177,44 @@ class AcpBackend {
|
|
|
5048
5177
|
getRecentStderrExcerpt() {
|
|
5049
5178
|
return this.recentStderrLines.slice(-6).join("\n");
|
|
5050
5179
|
}
|
|
5180
|
+
clearIdleTimeoutState() {
|
|
5181
|
+
if (this.idleTimeout) {
|
|
5182
|
+
clearTimeout(this.idleTimeout);
|
|
5183
|
+
this.idleTimeout = null;
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
clearToolCallTracking() {
|
|
5187
|
+
this.activeToolCalls.clear();
|
|
5188
|
+
for (const timeout of this.toolCallTimeouts.values()) {
|
|
5189
|
+
clearTimeout(timeout);
|
|
5190
|
+
}
|
|
5191
|
+
this.toolCallTimeouts.clear();
|
|
5192
|
+
this.toolCallStartTimes.clear();
|
|
5193
|
+
this.toolCallIdToNameMap.clear();
|
|
5194
|
+
this.toolCallOutputs.clear();
|
|
5195
|
+
this.toolCallCountSincePrompt = 0;
|
|
5196
|
+
}
|
|
5197
|
+
resetResponseTrackingForNewPrompt() {
|
|
5198
|
+
this.responseCompletionOutcome = null;
|
|
5199
|
+
this.clearIdleTimeoutState();
|
|
5200
|
+
this.clearToolCallTracking();
|
|
5201
|
+
}
|
|
5202
|
+
settleResponseWaiter(outcome) {
|
|
5203
|
+
const hasActiveWaiter = Boolean(this.idleResolver || this.idleRejecter);
|
|
5204
|
+
if (!this.waitingForResponse && !hasActiveWaiter) {
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
if (!hasActiveWaiter) {
|
|
5208
|
+
this.waitingForResponse = false;
|
|
5209
|
+
this.responseCompletionOutcome = outcome;
|
|
5210
|
+
return;
|
|
5211
|
+
}
|
|
5212
|
+
if (outcome.kind === "resolved") {
|
|
5213
|
+
this.idleResolver?.();
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5216
|
+
this.idleRejecter?.(outcome.error);
|
|
5217
|
+
}
|
|
5051
5218
|
onMessage(handler) {
|
|
5052
5219
|
this.listeners.push(handler);
|
|
5053
5220
|
}
|
|
@@ -5122,10 +5289,15 @@ class AcpBackend {
|
|
|
5122
5289
|
});
|
|
5123
5290
|
this.process.on("error", (err) => {
|
|
5124
5291
|
api.logger.debug(`[AcpBackend] Process error:`, err);
|
|
5292
|
+
this.settleResponseWaiter({ kind: "rejected", error: err });
|
|
5125
5293
|
this.emit({ type: "status", status: "error", detail: err.message });
|
|
5126
5294
|
});
|
|
5127
5295
|
this.process.on("exit", (code, signal) => {
|
|
5128
5296
|
if (!this.disposed && code !== 0 && code !== null) {
|
|
5297
|
+
this.settleResponseWaiter({
|
|
5298
|
+
kind: "rejected",
|
|
5299
|
+
error: new Error(`ACP process exited with code ${code}${signal ? ` (${signal})` : ""}`)
|
|
5300
|
+
});
|
|
5129
5301
|
api.logger.debug(`[AcpBackend] Process exited with code ${code}, signal ${signal}`);
|
|
5130
5302
|
this.emit({ type: "status", status: "stopped", detail: `Exit code: ${code}` });
|
|
5131
5303
|
}
|
|
@@ -5456,6 +5628,7 @@ class AcpBackend {
|
|
|
5456
5628
|
toolCallStartTimes: this.toolCallStartTimes,
|
|
5457
5629
|
toolCallTimeouts: this.toolCallTimeouts,
|
|
5458
5630
|
toolCallIdToNameMap: this.toolCallIdToNameMap,
|
|
5631
|
+
toolCallOutputs: this.toolCallOutputs,
|
|
5459
5632
|
idleTimeout: this.idleTimeout,
|
|
5460
5633
|
toolCallCountSincePrompt: this.toolCallCountSincePrompt,
|
|
5461
5634
|
emit: (msg) => this.emit(msg),
|
|
@@ -5532,20 +5705,24 @@ class AcpBackend {
|
|
|
5532
5705
|
this.emitUsageTelemetry(update, "acp-usage-update");
|
|
5533
5706
|
continue;
|
|
5534
5707
|
}
|
|
5708
|
+
if (sessionUpdateType === "task_complete") {
|
|
5709
|
+
this.emitUsageTelemetry(update.usage, "acp-session-usage");
|
|
5710
|
+
ctx.clearIdleTimeout();
|
|
5711
|
+
api.logger.debug("[AcpBackend] task_complete received, emitting idle status");
|
|
5712
|
+
this.emitIdleStatus();
|
|
5713
|
+
continue;
|
|
5714
|
+
}
|
|
5535
5715
|
const handledLegacy = handleLegacyMessageChunk(update, ctx).handled;
|
|
5536
5716
|
const handledPlan = handlePlanUpdate(update, ctx).handled;
|
|
5537
5717
|
const handledThinking = handleThinkingUpdate(update, ctx).handled;
|
|
5538
5718
|
const handledUsage = this.emitUsageTelemetry(update.usage, "acp-session-usage");
|
|
5539
5719
|
const updateTypeStr = sessionUpdateType;
|
|
5540
|
-
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call", "usage_update"];
|
|
5720
|
+
const handledTypes = ["agent_message_chunk", "tool_call_update", "agent_thought_chunk", "tool_call", "usage_update", "task_complete"];
|
|
5541
5721
|
if (updateTypeStr && !handledTypes.includes(updateTypeStr) && !handledLegacy && !handledPlan && !handledThinking && !handledUsage) {
|
|
5542
5722
|
api.logger.debug(`[AcpBackend] Unhandled session update type: ${updateTypeStr}`, JSON.stringify(update, null, 2));
|
|
5543
5723
|
}
|
|
5544
5724
|
}
|
|
5545
5725
|
}
|
|
5546
|
-
// Promise resolver for waitForIdle - set when waiting for response to complete
|
|
5547
|
-
idleResolver = null;
|
|
5548
|
-
waitingForResponse = false;
|
|
5549
5726
|
async sendPrompt(sessionId, prompt) {
|
|
5550
5727
|
this.toolCallCountSincePrompt = 0;
|
|
5551
5728
|
if (this.disposed) {
|
|
@@ -5554,6 +5731,7 @@ class AcpBackend {
|
|
|
5554
5731
|
if (!this.connection || !this.acpSessionId) {
|
|
5555
5732
|
throw new Error("Session not started");
|
|
5556
5733
|
}
|
|
5734
|
+
this.resetResponseTrackingForNewPrompt();
|
|
5557
5735
|
this.emit({ type: "status", status: "running" });
|
|
5558
5736
|
this.waitingForResponse = true;
|
|
5559
5737
|
try {
|
|
@@ -5572,7 +5750,6 @@ class AcpBackend {
|
|
|
5572
5750
|
api.logger.debug("[AcpBackend] Prompt request sent to ACP connection");
|
|
5573
5751
|
} catch (error) {
|
|
5574
5752
|
api.logger.debug("[AcpBackend] Error sending prompt:", error);
|
|
5575
|
-
this.waitingForResponse = false;
|
|
5576
5753
|
let errorDetail;
|
|
5577
5754
|
if (error instanceof Error) {
|
|
5578
5755
|
errorDetail = error.message;
|
|
@@ -5586,6 +5763,10 @@ class AcpBackend {
|
|
|
5586
5763
|
status: "error",
|
|
5587
5764
|
detail: errorDetail
|
|
5588
5765
|
});
|
|
5766
|
+
this.settleResponseWaiter({
|
|
5767
|
+
kind: "rejected",
|
|
5768
|
+
error: error instanceof Error ? error : normalizeAcpError(error)
|
|
5769
|
+
});
|
|
5589
5770
|
throw error;
|
|
5590
5771
|
}
|
|
5591
5772
|
}
|
|
@@ -5594,21 +5775,38 @@ class AcpBackend {
|
|
|
5594
5775
|
* Call this after sendPrompt to wait for Gemini to finish responding
|
|
5595
5776
|
*/
|
|
5596
5777
|
async waitForResponseComplete(timeoutMs = 12e4) {
|
|
5778
|
+
const pendingOutcome = this.responseCompletionOutcome;
|
|
5779
|
+
if (pendingOutcome) {
|
|
5780
|
+
this.responseCompletionOutcome = null;
|
|
5781
|
+
if (pendingOutcome.kind === "rejected") {
|
|
5782
|
+
throw pendingOutcome.error;
|
|
5783
|
+
}
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5597
5786
|
if (!this.waitingForResponse) {
|
|
5598
5787
|
return;
|
|
5599
5788
|
}
|
|
5600
5789
|
return new Promise((resolve, reject) => {
|
|
5601
5790
|
const timeout = setTimeout(() => {
|
|
5602
5791
|
this.idleResolver = null;
|
|
5792
|
+
this.idleRejecter = null;
|
|
5603
5793
|
this.waitingForResponse = false;
|
|
5604
5794
|
reject(new Error("Timeout waiting for response to complete"));
|
|
5605
5795
|
}, timeoutMs);
|
|
5606
5796
|
this.idleResolver = () => {
|
|
5607
5797
|
clearTimeout(timeout);
|
|
5608
5798
|
this.idleResolver = null;
|
|
5799
|
+
this.idleRejecter = null;
|
|
5609
5800
|
this.waitingForResponse = false;
|
|
5610
5801
|
resolve();
|
|
5611
5802
|
};
|
|
5803
|
+
this.idleRejecter = (error) => {
|
|
5804
|
+
clearTimeout(timeout);
|
|
5805
|
+
this.idleResolver = null;
|
|
5806
|
+
this.idleRejecter = null;
|
|
5807
|
+
this.waitingForResponse = false;
|
|
5808
|
+
reject(error);
|
|
5809
|
+
};
|
|
5612
5810
|
});
|
|
5613
5811
|
}
|
|
5614
5812
|
/**
|
|
@@ -5616,18 +5814,19 @@ class AcpBackend {
|
|
|
5616
5814
|
*/
|
|
5617
5815
|
emitIdleStatus() {
|
|
5618
5816
|
this.emit({ type: "status", status: "idle" });
|
|
5619
|
-
|
|
5620
|
-
api.logger.debug("[AcpBackend] Resolving idle waiter");
|
|
5621
|
-
this.idleResolver();
|
|
5622
|
-
}
|
|
5817
|
+
this.settleResponseWaiter({ kind: "resolved" });
|
|
5623
5818
|
}
|
|
5624
5819
|
async cancel(sessionId) {
|
|
5820
|
+
const cancelError = createAcpAbortError("Cancelled by user");
|
|
5821
|
+
this.clearIdleTimeoutState();
|
|
5822
|
+
this.clearToolCallTracking();
|
|
5823
|
+
this.settleResponseWaiter({ kind: "rejected", error: cancelError });
|
|
5824
|
+
this.emit({ type: "status", status: "stopped", detail: "Cancelled by user" });
|
|
5625
5825
|
if (!this.connection || !this.acpSessionId) {
|
|
5626
5826
|
return;
|
|
5627
5827
|
}
|
|
5628
5828
|
try {
|
|
5629
5829
|
await this.connection.cancel({ sessionId: this.acpSessionId });
|
|
5630
|
-
this.emit({ type: "status", status: "stopped", detail: "Cancelled by user" });
|
|
5631
5830
|
} catch (error) {
|
|
5632
5831
|
api.logger.debug("[AcpBackend] Error cancelling:", error);
|
|
5633
5832
|
}
|
|
@@ -5655,6 +5854,10 @@ class AcpBackend {
|
|
|
5655
5854
|
if (this.disposed) return;
|
|
5656
5855
|
api.logger.debug("[AcpBackend] Disposing backend");
|
|
5657
5856
|
this.disposed = true;
|
|
5857
|
+
this.settleResponseWaiter({
|
|
5858
|
+
kind: "rejected",
|
|
5859
|
+
error: createAcpAbortError("ACP backend disposed")
|
|
5860
|
+
});
|
|
5658
5861
|
if (this.connection && this.acpSessionId) {
|
|
5659
5862
|
try {
|
|
5660
5863
|
await Promise.race([
|
|
@@ -5683,19 +5886,11 @@ class AcpBackend {
|
|
|
5683
5886
|
});
|
|
5684
5887
|
this.process = null;
|
|
5685
5888
|
}
|
|
5686
|
-
|
|
5687
|
-
clearTimeout(this.idleTimeout);
|
|
5688
|
-
this.idleTimeout = null;
|
|
5689
|
-
}
|
|
5889
|
+
this.clearIdleTimeoutState();
|
|
5690
5890
|
this.listeners = [];
|
|
5691
5891
|
this.connection = null;
|
|
5692
5892
|
this.acpSessionId = null;
|
|
5693
|
-
this.
|
|
5694
|
-
for (const timeout of this.toolCallTimeouts.values()) {
|
|
5695
|
-
clearTimeout(timeout);
|
|
5696
|
-
}
|
|
5697
|
-
this.toolCallTimeouts.clear();
|
|
5698
|
-
this.toolCallStartTimes.clear();
|
|
5893
|
+
this.clearToolCallTracking();
|
|
5699
5894
|
this.pendingPermissions.clear();
|
|
5700
5895
|
}
|
|
5701
5896
|
}
|
|
@@ -5711,17 +5906,17 @@ function readGeminiLocalConfig() {
|
|
|
5711
5906
|
let googleCloudProject = null;
|
|
5712
5907
|
let googleCloudProjectEmail = null;
|
|
5713
5908
|
const possiblePaths = [
|
|
5714
|
-
path
|
|
5909
|
+
path.join(os$1.homedir(), ".gemini", "oauth_creds.json"),
|
|
5715
5910
|
// Main OAuth credentials file
|
|
5716
|
-
path
|
|
5717
|
-
path
|
|
5718
|
-
path
|
|
5719
|
-
path
|
|
5911
|
+
path.join(os$1.homedir(), ".gemini", "config.json"),
|
|
5912
|
+
path.join(os$1.homedir(), ".config", "gemini", "config.json"),
|
|
5913
|
+
path.join(os$1.homedir(), ".gemini", "auth.json"),
|
|
5914
|
+
path.join(os$1.homedir(), ".config", "gemini", "auth.json")
|
|
5720
5915
|
];
|
|
5721
5916
|
for (const configPath of possiblePaths) {
|
|
5722
|
-
if (fs
|
|
5917
|
+
if (fs.existsSync(configPath)) {
|
|
5723
5918
|
try {
|
|
5724
|
-
const config = JSON.parse(fs
|
|
5919
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
5725
5920
|
if (!token) {
|
|
5726
5921
|
const foundToken = config.access_token || config.token || config.apiKey || config.GEMINI_API_KEY;
|
|
5727
5922
|
if (foundToken && typeof foundToken === "string") {
|
|
@@ -5793,22 +5988,22 @@ function determineGeminiModel(explicitModel, localConfig) {
|
|
|
5793
5988
|
}
|
|
5794
5989
|
function saveGeminiModelToConfig(model) {
|
|
5795
5990
|
try {
|
|
5796
|
-
const configDir = path
|
|
5797
|
-
const configPath = path
|
|
5798
|
-
if (!fs
|
|
5799
|
-
fs
|
|
5991
|
+
const configDir = path.join(os$1.homedir(), ".gemini");
|
|
5992
|
+
const configPath = path.join(configDir, "config.json");
|
|
5993
|
+
if (!fs.existsSync(configDir)) {
|
|
5994
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
5800
5995
|
}
|
|
5801
5996
|
let config = {};
|
|
5802
|
-
if (fs
|
|
5997
|
+
if (fs.existsSync(configPath)) {
|
|
5803
5998
|
try {
|
|
5804
|
-
config = JSON.parse(fs
|
|
5999
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
5805
6000
|
} catch (error) {
|
|
5806
6001
|
api.logger.debug(`[Gemini] Failed to read existing config, creating new one`);
|
|
5807
6002
|
config = {};
|
|
5808
6003
|
}
|
|
5809
6004
|
}
|
|
5810
6005
|
config.model = model;
|
|
5811
|
-
fs
|
|
6006
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5812
6007
|
api.logger.debug(`[Gemini] Saved model "${model}" to ${configPath}`);
|
|
5813
6008
|
} catch (error) {
|
|
5814
6009
|
api.logger.debug(`[Gemini] Failed to save model to config:`, error);
|
|
@@ -5816,15 +6011,15 @@ function saveGeminiModelToConfig(model) {
|
|
|
5816
6011
|
}
|
|
5817
6012
|
function saveGoogleCloudProjectToConfig(projectId, email) {
|
|
5818
6013
|
try {
|
|
5819
|
-
const configDir = path
|
|
5820
|
-
const configPath = path
|
|
5821
|
-
if (!fs
|
|
5822
|
-
fs
|
|
6014
|
+
const configDir = path.join(os$1.homedir(), ".gemini");
|
|
6015
|
+
const configPath = path.join(configDir, "config.json");
|
|
6016
|
+
if (!fs.existsSync(configDir)) {
|
|
6017
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
5823
6018
|
}
|
|
5824
6019
|
let config = {};
|
|
5825
|
-
if (fs
|
|
6020
|
+
if (fs.existsSync(configPath)) {
|
|
5826
6021
|
try {
|
|
5827
|
-
config = JSON.parse(fs
|
|
6022
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
5828
6023
|
} catch {
|
|
5829
6024
|
config = {};
|
|
5830
6025
|
}
|
|
@@ -5833,7 +6028,7 @@ function saveGoogleCloudProjectToConfig(projectId, email) {
|
|
|
5833
6028
|
if (email) {
|
|
5834
6029
|
config.googleCloudProjectEmail = email;
|
|
5835
6030
|
}
|
|
5836
|
-
fs
|
|
6031
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5837
6032
|
api.logger.debug(`[Gemini] Saved Google Cloud Project "${projectId}"${email ? ` for ${email}` : ""} to ${configPath}`);
|
|
5838
6033
|
} catch (error) {
|
|
5839
6034
|
api.logger.debug(`[Gemini] Failed to save Google Cloud Project to config:`, error);
|
|
@@ -5940,11 +6135,11 @@ function readFirstEnv(...names) {
|
|
|
5940
6135
|
return "";
|
|
5941
6136
|
}
|
|
5942
6137
|
function normalizeCommandPath(command) {
|
|
5943
|
-
if (
|
|
6138
|
+
if (node_path.isAbsolute(command)) {
|
|
5944
6139
|
return command;
|
|
5945
6140
|
}
|
|
5946
|
-
const resolved =
|
|
5947
|
-
return
|
|
6141
|
+
const resolved = node_path.resolve(process.cwd(), command);
|
|
6142
|
+
return node_fs.existsSync(resolved) ? resolved : command;
|
|
5948
6143
|
}
|
|
5949
6144
|
function resolveCommandOnPath(command) {
|
|
5950
6145
|
const pathValue = typeof process.env.PATH === "string" ? process.env.PATH : "";
|
|
@@ -5952,13 +6147,13 @@ function resolveCommandOnPath(command) {
|
|
|
5952
6147
|
return null;
|
|
5953
6148
|
}
|
|
5954
6149
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter(Boolean) : [""];
|
|
5955
|
-
for (const dir of pathValue.split(
|
|
6150
|
+
for (const dir of pathValue.split(node_path.delimiter)) {
|
|
5956
6151
|
const trimmedDir = dir.trim();
|
|
5957
6152
|
if (!trimmedDir) {
|
|
5958
6153
|
continue;
|
|
5959
6154
|
}
|
|
5960
|
-
const directCandidate =
|
|
5961
|
-
if (
|
|
6155
|
+
const directCandidate = node_path.join(trimmedDir, command);
|
|
6156
|
+
if (node_fs.existsSync(directCandidate)) {
|
|
5962
6157
|
return directCandidate;
|
|
5963
6158
|
}
|
|
5964
6159
|
if (process.platform !== "win32") {
|
|
@@ -5969,8 +6164,8 @@ function resolveCommandOnPath(command) {
|
|
|
5969
6164
|
continue;
|
|
5970
6165
|
}
|
|
5971
6166
|
for (const extension of extensions) {
|
|
5972
|
-
const candidate =
|
|
5973
|
-
if (
|
|
6167
|
+
const candidate = node_path.join(trimmedDir, `${command}${extension.toLowerCase()}`);
|
|
6168
|
+
if (node_fs.existsSync(candidate)) {
|
|
5974
6169
|
return candidate;
|
|
5975
6170
|
}
|
|
5976
6171
|
}
|
|
@@ -6065,8 +6260,8 @@ function validateCodexAcpSpawn(options = {}) {
|
|
|
6065
6260
|
const normalizedCommand = spawn.command.trim();
|
|
6066
6261
|
const commandLower = normalizedCommand.toLowerCase();
|
|
6067
6262
|
const npxMode = readCodexAcpNpxMode();
|
|
6068
|
-
if (
|
|
6069
|
-
if (!
|
|
6263
|
+
if (node_path.isAbsolute(normalizedCommand)) {
|
|
6264
|
+
if (!node_fs.existsSync(normalizedCommand)) {
|
|
6070
6265
|
return {
|
|
6071
6266
|
ok: false,
|
|
6072
6267
|
errorMessage: `Codex ACP is enabled, but the resolved command does not exist: ${normalizedCommand}`
|
|
@@ -6336,12 +6531,12 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
6336
6531
|
async function executeUnifiedProvider(opts) {
|
|
6337
6532
|
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials);
|
|
6338
6533
|
if (opts.provider === "claude") {
|
|
6339
|
-
const { runClaude } = await Promise.resolve().then(function () { return require('./runClaude-
|
|
6534
|
+
const { runClaude } = await Promise.resolve().then(function () { return require('./runClaude-Dz-PCSvb.cjs'); });
|
|
6340
6535
|
await runClaude(credentials, opts.claudeOptions ?? {});
|
|
6341
6536
|
return;
|
|
6342
6537
|
}
|
|
6343
6538
|
if (opts.provider === "codex") {
|
|
6344
|
-
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-
|
|
6539
|
+
const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-BtZplK1R.cjs'); });
|
|
6345
6540
|
await runCodex({
|
|
6346
6541
|
credentials,
|
|
6347
6542
|
startedBy: opts.startedBy,
|
|
@@ -6351,7 +6546,7 @@ async function executeUnifiedProvider(opts) {
|
|
|
6351
6546
|
return;
|
|
6352
6547
|
}
|
|
6353
6548
|
if (opts.provider === "gemini") {
|
|
6354
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
6549
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-DUyH311Z.cjs'); });
|
|
6355
6550
|
await runGemini({
|
|
6356
6551
|
credentials,
|
|
6357
6552
|
startedBy: opts.startedBy
|
|
@@ -6393,7 +6588,7 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
6393
6588
|
return;
|
|
6394
6589
|
} else if (subcommand === "runtime") {
|
|
6395
6590
|
if (args[1] === "providers") {
|
|
6396
|
-
const { renderRuntimeProviders } = await Promise.resolve().then(function () { return require('./command-
|
|
6591
|
+
const { renderRuntimeProviders } = await Promise.resolve().then(function () { return require('./command-CPlJKXDn.cjs'); });
|
|
6397
6592
|
console.log(renderRuntimeProviders());
|
|
6398
6593
|
return;
|
|
6399
6594
|
}
|
|
@@ -6571,8 +6766,8 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
6571
6766
|
const projectId = args[3];
|
|
6572
6767
|
try {
|
|
6573
6768
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
6574
|
-
const { readCredentials: readCredentials2 } = await Promise.resolve().then(function () { return require('./persistence-
|
|
6575
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./api-
|
|
6769
|
+
const { readCredentials: readCredentials2 } = await Promise.resolve().then(function () { return require('./persistence-BeFVx6kI.cjs'); });
|
|
6770
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./api-CyJG1mr6.cjs'); }).then(function (n) { return n.api; });
|
|
6576
6771
|
let userEmail = void 0;
|
|
6577
6772
|
try {
|
|
6578
6773
|
const credentials = await readCredentials2();
|
|
@@ -6735,7 +6930,7 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
6735
6930
|
if (latest) {
|
|
6736
6931
|
console.error(`Latest daemon log: ${latest.path}`);
|
|
6737
6932
|
try {
|
|
6738
|
-
const logContent =
|
|
6933
|
+
const logContent = node_fs.readFileSync(latest.path, "utf-8");
|
|
6739
6934
|
if (logContent.includes("EADDRINUSE")) {
|
|
6740
6935
|
console.error("Daemon control port is already in use. Retry after stopping the stale daemon or run `hicloud doctor clean`.");
|
|
6741
6936
|
}
|