happy-imou-cloud 2.1.7 → 2.1.9
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-C0A6Jr8V.mjs → BaseReasoningProcessor-CgEO6Vh-.mjs} +4 -3
- package/dist/{BaseReasoningProcessor-iSuaFeZH.cjs → BaseReasoningProcessor-DV6TAtd7.cjs} +4 -3
- package/dist/{ProviderSelectionHandler-DI9QK1iY.cjs → ProviderSelectionHandler-C6ILAmE3.cjs} +2 -2
- package/dist/{ProviderSelectionHandler-WT-tvePy.mjs → ProviderSelectionHandler-dczuX21U.mjs} +2 -2
- package/dist/{api-BYKV7vX6.mjs → api-CfHYTLZX.mjs} +141 -35
- package/dist/{api-CdGT5hsH.cjs → api-CfmTDha2.cjs} +141 -35
- package/dist/{command-C1cKqmsa.mjs → command-B0HfYWYW.mjs} +3 -3
- package/dist/{command-uyA8cYmL.cjs → command-Bw2XKjA9.cjs} +3 -3
- package/dist/{index-BAhlFq45.mjs → index-BKjWLXkN.mjs} +146 -30
- package/dist/{index-D_S_7bqK.cjs → index-C3bSe5_d.cjs} +149 -33
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +8 -4
- package/dist/lib.d.mts +8 -4
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-CMvrZ4SA.cjs → persistence-Db4p7gsX.cjs} +1 -1
- package/dist/{persistence-B8Wgn6aN.mjs → persistence-Nun5c360.mjs} +1 -1
- package/dist/{registerKillSessionHandler-5DVlZaJx.mjs → registerKillSessionHandler-C1S9ytML.mjs} +3 -3
- package/dist/{registerKillSessionHandler-DjxtkGMv.cjs → registerKillSessionHandler-FIQEW2Rx.cjs} +3 -3
- package/dist/{runClaude-WB884iOO.mjs → runClaude-ClXU2LZA.mjs} +9 -5
- package/dist/{runClaude-DNZFsQY0.cjs → runClaude-Dxye_oZf.cjs} +9 -5
- package/dist/{runCodex-MJar-238.mjs → runCodex-CXu7_Wgk.mjs} +96 -21
- package/dist/{runCodex-WZ91ZLK7.cjs → runCodex-IaL6Vqwc.cjs} +96 -21
- package/dist/{runGemini-B_wCHgPU.cjs → runGemini-BgcZcZsv.cjs} +11 -5
- package/dist/{runGemini-BPGpwljp.mjs → runGemini-BlJjRWN1.mjs} +11 -5
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ var node_child_process = require('node:child_process');
|
|
|
18
18
|
var expoServerSdk = require('expo-server-sdk');
|
|
19
19
|
|
|
20
20
|
var name = "happy-imou-cloud";
|
|
21
|
-
var version = "2.1.
|
|
21
|
+
var version = "2.1.9";
|
|
22
22
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
23
23
|
var author = "long.zhu";
|
|
24
24
|
var license = "MIT";
|
|
@@ -433,7 +433,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
433
433
|
return { file, path: fullPath, modified: stats.mtime };
|
|
434
434
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
435
435
|
try {
|
|
436
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-
|
|
436
|
+
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-Db4p7gsX.cjs'); });
|
|
437
437
|
const state = await readDaemonState();
|
|
438
438
|
if (!state) {
|
|
439
439
|
return logs;
|
|
@@ -1734,8 +1734,8 @@ function buildSocketAuth(opts) {
|
|
|
1734
1734
|
return auth;
|
|
1735
1735
|
}
|
|
1736
1736
|
|
|
1737
|
-
const
|
|
1738
|
-
const
|
|
1737
|
+
const MAX_PENDING_RELIABLE_SESSION_MESSAGES = 200;
|
|
1738
|
+
const MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES = 512 * 1024;
|
|
1739
1739
|
const PROTOCOL_V3_INITIAL_SNAPSHOT_LIMIT = 150;
|
|
1740
1740
|
const PROTOCOL_V3_CHANGES_PAGE_LIMIT = 200;
|
|
1741
1741
|
const PROTOCOL_V3_CAPABILITIES_WAIT_MS = 250;
|
|
@@ -1754,8 +1754,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1754
1754
|
metadataLock = new AsyncLock();
|
|
1755
1755
|
encryptionKey;
|
|
1756
1756
|
encryptionVariant;
|
|
1757
|
-
|
|
1758
|
-
|
|
1757
|
+
pendingReliableSessionMessages = [];
|
|
1758
|
+
pendingReliableSessionMessageBytes = 0;
|
|
1759
1759
|
reconnectAfterServerDisconnectTimer = null;
|
|
1760
1760
|
lastSocketServerError = null;
|
|
1761
1761
|
protocolV3SessionSync = null;
|
|
@@ -1784,7 +1784,14 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1784
1784
|
encryptionVariant: this.encryptionVariant,
|
|
1785
1785
|
logger: (msg, data) => logger.debug(msg, data)
|
|
1786
1786
|
});
|
|
1787
|
-
|
|
1787
|
+
const workingDirectory = this.metadata?.path ?? process.cwd();
|
|
1788
|
+
if (!this.metadata?.path) {
|
|
1789
|
+
logger.debug("[API] Session metadata missing path during client initialization, falling back to process.cwd()", {
|
|
1790
|
+
sessionId: this.sessionId,
|
|
1791
|
+
workingDirectory
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
registerCommonHandlers(this.rpcHandlerManager, workingDirectory);
|
|
1788
1795
|
this.socket = socket_ioClient.io(configuration.serverUrl, {
|
|
1789
1796
|
auth: (cb) => cb(buildSocketAuth({
|
|
1790
1797
|
credentials: this.credentials,
|
|
@@ -1805,7 +1812,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1805
1812
|
this.clearReconnectAfterServerDisconnectTimer();
|
|
1806
1813
|
this.lastSocketServerError = null;
|
|
1807
1814
|
this.rpcHandlerManager.onSocketConnect(this.socket);
|
|
1808
|
-
this.
|
|
1815
|
+
this.flushReliableSessionMessages();
|
|
1809
1816
|
this.protocolV3Descriptor = null;
|
|
1810
1817
|
this.protocolV3SocketCapabilities = null;
|
|
1811
1818
|
this.scheduleProtocolV3SessionSync();
|
|
@@ -1988,9 +1995,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1988
1995
|
if (!this.socket.connected) {
|
|
1989
1996
|
if (this.shouldBufferReliableCodexMessage(body)) {
|
|
1990
1997
|
logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
|
|
1991
|
-
this.
|
|
1992
|
-
this.pendingReliableCodexMessageBytes += encrypted.length;
|
|
1993
|
-
this.trimPendingReliableCodexMessages();
|
|
1998
|
+
this.bufferReliableSessionMessage({ encrypted, type: `codex:${eventType}` });
|
|
1994
1999
|
} else {
|
|
1995
2000
|
logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
|
|
1996
2001
|
}
|
|
@@ -2019,10 +2024,23 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2019
2024
|
};
|
|
2020
2025
|
logger.debug(`[SOCKET] Sending ACP message from ${provider}:`, { type: body.type, hasMessage: "message" in body });
|
|
2021
2026
|
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2027
|
+
const eventType = typeof body?.type === "string" ? body.type : "unknown";
|
|
2028
|
+
if (!this.socket.connected) {
|
|
2029
|
+
if (this.shouldBufferReliableAcpMessage(body)) {
|
|
2030
|
+
logger.debug("[API] Socket not connected, buffering reliable ACP message:", {
|
|
2031
|
+
provider,
|
|
2032
|
+
type: eventType
|
|
2033
|
+
});
|
|
2034
|
+
this.bufferReliableSessionMessage({ encrypted, type: `acp:${provider}:${eventType}` });
|
|
2035
|
+
} else {
|
|
2036
|
+
logger.debug("[API] Socket not connected, dropping non-critical ACP message:", {
|
|
2037
|
+
provider,
|
|
2038
|
+
type: eventType
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
this.emitEncryptedSessionMessage(encrypted);
|
|
2026
2044
|
}
|
|
2027
2045
|
sendSessionEvent(event, id) {
|
|
2028
2046
|
let content = {
|
|
@@ -2034,10 +2052,16 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2034
2052
|
}
|
|
2035
2053
|
};
|
|
2036
2054
|
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
2037
|
-
this.socket.
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2055
|
+
if (!this.socket.connected) {
|
|
2056
|
+
if (this.shouldBufferReliableSessionEvent(event)) {
|
|
2057
|
+
logger.debug("[API] Socket not connected, buffering session event:", { type: event.type });
|
|
2058
|
+
this.bufferReliableSessionMessage({ encrypted, type: `event:${event.type}` });
|
|
2059
|
+
} else {
|
|
2060
|
+
logger.debug("[API] Socket not connected, dropping session event:", { type: event.type });
|
|
2061
|
+
}
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
this.emitEncryptedSessionMessage(encrypted);
|
|
2041
2065
|
}
|
|
2042
2066
|
/**
|
|
2043
2067
|
* Send a ping message to keep the connection alive
|
|
@@ -2091,7 +2115,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2091
2115
|
* @param handler - Handler function that returns the updated metadata
|
|
2092
2116
|
*/
|
|
2093
2117
|
updateMetadata(handler) {
|
|
2094
|
-
this.metadataLock.inLock(async () => {
|
|
2118
|
+
void this.metadataLock.inLock(async () => {
|
|
2095
2119
|
await backoff(async () => {
|
|
2096
2120
|
let updated = handler(this.metadata);
|
|
2097
2121
|
const sessionIndex = buildSessionRuntimeIndex(updated);
|
|
@@ -2114,6 +2138,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2114
2138
|
throw new Error("Metadata version mismatch");
|
|
2115
2139
|
} else if (answer.result === "error") ;
|
|
2116
2140
|
});
|
|
2141
|
+
}).catch((error) => {
|
|
2142
|
+
logger.debug("[API] Metadata update failed unexpectedly", error);
|
|
2117
2143
|
});
|
|
2118
2144
|
}
|
|
2119
2145
|
/**
|
|
@@ -2122,7 +2148,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2122
2148
|
*/
|
|
2123
2149
|
updateAgentState(handler) {
|
|
2124
2150
|
logger.debugLargeJson("Updating agent state", this.agentState);
|
|
2125
|
-
this.agentStateLock.inLock(async () => {
|
|
2151
|
+
void this.agentStateLock.inLock(async () => {
|
|
2126
2152
|
await backoff(async () => {
|
|
2127
2153
|
let updated = handler(this.agentState || {});
|
|
2128
2154
|
const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
|
|
@@ -2140,6 +2166,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2140
2166
|
throw new Error("Agent state version mismatch");
|
|
2141
2167
|
} else if (answer.result === "error") ;
|
|
2142
2168
|
});
|
|
2169
|
+
}).catch((error) => {
|
|
2170
|
+
logger.debug("[API] Agent state update failed unexpectedly", error);
|
|
2143
2171
|
});
|
|
2144
2172
|
}
|
|
2145
2173
|
/**
|
|
@@ -2441,13 +2469,18 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2441
2469
|
message: encrypted
|
|
2442
2470
|
});
|
|
2443
2471
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2472
|
+
bufferReliableSessionMessage(message) {
|
|
2473
|
+
this.pendingReliableSessionMessages.push(message);
|
|
2474
|
+
this.pendingReliableSessionMessageBytes += message.encrypted.length;
|
|
2475
|
+
this.trimPendingReliableSessionMessages();
|
|
2476
|
+
}
|
|
2477
|
+
flushReliableSessionMessages() {
|
|
2478
|
+
if (!this.socket.connected || this.pendingReliableSessionMessages.length === 0) {
|
|
2446
2479
|
return;
|
|
2447
2480
|
}
|
|
2448
|
-
const buffered = this.
|
|
2449
|
-
this.
|
|
2450
|
-
logger.debug("[API] Flushing buffered
|
|
2481
|
+
const buffered = this.pendingReliableSessionMessages.splice(0, this.pendingReliableSessionMessages.length);
|
|
2482
|
+
this.pendingReliableSessionMessageBytes = 0;
|
|
2483
|
+
logger.debug("[API] Flushing buffered session messages after reconnect", {
|
|
2451
2484
|
count: buffered.length,
|
|
2452
2485
|
types: buffered.map((message) => message.type)
|
|
2453
2486
|
});
|
|
@@ -2471,21 +2504,49 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
2471
2504
|
return false;
|
|
2472
2505
|
}
|
|
2473
2506
|
}
|
|
2474
|
-
|
|
2507
|
+
shouldBufferReliableAcpMessage(body) {
|
|
2508
|
+
switch (body?.type) {
|
|
2509
|
+
case "message":
|
|
2510
|
+
case "tool-call":
|
|
2511
|
+
case "tool-result":
|
|
2512
|
+
case "file-edit":
|
|
2513
|
+
case "terminal-output":
|
|
2514
|
+
case "task_started":
|
|
2515
|
+
case "task_complete":
|
|
2516
|
+
case "turn_aborted":
|
|
2517
|
+
case "turn-report":
|
|
2518
|
+
case "permission-request":
|
|
2519
|
+
return true;
|
|
2520
|
+
default:
|
|
2521
|
+
return false;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
shouldBufferReliableSessionEvent(event) {
|
|
2525
|
+
switch (event.type) {
|
|
2526
|
+
case "switch":
|
|
2527
|
+
case "message":
|
|
2528
|
+
case "permission-mode-changed":
|
|
2529
|
+
case "ready":
|
|
2530
|
+
return true;
|
|
2531
|
+
default:
|
|
2532
|
+
return false;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
trimPendingReliableSessionMessages() {
|
|
2475
2536
|
let dropped = 0;
|
|
2476
|
-
while (this.
|
|
2477
|
-
const removed = this.
|
|
2537
|
+
while (this.pendingReliableSessionMessages.length > MAX_PENDING_RELIABLE_SESSION_MESSAGES || this.pendingReliableSessionMessageBytes > MAX_PENDING_RELIABLE_SESSION_MESSAGE_BYTES) {
|
|
2538
|
+
const removed = this.pendingReliableSessionMessages.shift();
|
|
2478
2539
|
if (!removed) {
|
|
2479
2540
|
break;
|
|
2480
2541
|
}
|
|
2481
|
-
this.
|
|
2542
|
+
this.pendingReliableSessionMessageBytes -= removed.encrypted.length;
|
|
2482
2543
|
dropped += 1;
|
|
2483
2544
|
}
|
|
2484
2545
|
if (dropped > 0) {
|
|
2485
|
-
logger.debug("[API] Dropped oldest buffered
|
|
2546
|
+
logger.debug("[API] Dropped oldest buffered session messages to cap reconnect memory usage", {
|
|
2486
2547
|
dropped,
|
|
2487
|
-
remaining: this.
|
|
2488
|
-
bytes: this.
|
|
2548
|
+
remaining: this.pendingReliableSessionMessages.length,
|
|
2549
|
+
bytes: this.pendingReliableSessionMessageBytes
|
|
2489
2550
|
});
|
|
2490
2551
|
}
|
|
2491
2552
|
}
|
|
@@ -3205,6 +3266,39 @@ class OfflineState {
|
|
|
3205
3266
|
}
|
|
3206
3267
|
const connectionState = new OfflineState();
|
|
3207
3268
|
|
|
3269
|
+
async function hmac_sha512(key, data) {
|
|
3270
|
+
const hmac = node_crypto.createHmac("sha512", key);
|
|
3271
|
+
hmac.update(data);
|
|
3272
|
+
return new Uint8Array(hmac.digest());
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
async function deriveSecretKeyTreeRoot(seed, usage) {
|
|
3276
|
+
const I = await hmac_sha512(new TextEncoder().encode(usage + " Master Seed"), seed);
|
|
3277
|
+
return {
|
|
3278
|
+
key: I.slice(0, 32),
|
|
3279
|
+
chainCode: I.slice(32)
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
async function deriveSecretKeyTreeChild(chainCode, index) {
|
|
3283
|
+
const data = new Uint8Array([0, ...new TextEncoder().encode(index)]);
|
|
3284
|
+
const I = await hmac_sha512(chainCode, data);
|
|
3285
|
+
return {
|
|
3286
|
+
key: I.subarray(0, 32),
|
|
3287
|
+
chainCode: I.subarray(32)
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
async function deriveKey(master, usage, path) {
|
|
3291
|
+
let state = await deriveSecretKeyTreeRoot(master, usage);
|
|
3292
|
+
let remaining = [...path];
|
|
3293
|
+
while (remaining.length > 0) {
|
|
3294
|
+
let index = remaining[0];
|
|
3295
|
+
remaining = remaining.slice(1);
|
|
3296
|
+
state = await deriveSecretKeyTreeChild(state.chainCode, index);
|
|
3297
|
+
}
|
|
3298
|
+
return state.key;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
const SESSION_DATA_KEY_USAGE = "Happy Session Data Key";
|
|
3208
3302
|
class ApiClient {
|
|
3209
3303
|
static async create(credential) {
|
|
3210
3304
|
return new ApiClient(credential);
|
|
@@ -3346,7 +3440,15 @@ class ApiClient {
|
|
|
3346
3440
|
let encryptionKey;
|
|
3347
3441
|
let encryptionVariant;
|
|
3348
3442
|
if (this.credential.encryption.type === "dataKey") {
|
|
3349
|
-
|
|
3443
|
+
if (opts.stableDataKeyPath && opts.stableDataKeyPath.length > 0) {
|
|
3444
|
+
encryptionKey = await deriveKey(
|
|
3445
|
+
this.credential.encryption.machineKey,
|
|
3446
|
+
SESSION_DATA_KEY_USAGE,
|
|
3447
|
+
opts.stableDataKeyPath
|
|
3448
|
+
);
|
|
3449
|
+
} else {
|
|
3450
|
+
encryptionKey = getRandomBytes(32);
|
|
3451
|
+
}
|
|
3350
3452
|
encryptionVariant = "dataKey";
|
|
3351
3453
|
let encryptedDataKey = libsodiumEncryptForPublicKey(encryptionKey, this.credential.encryption.publicKey);
|
|
3352
3454
|
dataEncryptionKey = new Uint8Array(encryptedDataKey.length + 1);
|
|
@@ -3372,10 +3474,14 @@ class ApiClient {
|
|
|
3372
3474
|
});
|
|
3373
3475
|
logger.debug(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
|
|
3374
3476
|
let raw = response.data.session;
|
|
3477
|
+
const metadata = decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.metadata));
|
|
3478
|
+
if (metadata === null) {
|
|
3479
|
+
throw new Error(`Failed to decrypt session metadata for tag ${opts.tag}`);
|
|
3480
|
+
}
|
|
3375
3481
|
let session = {
|
|
3376
3482
|
id: raw.id,
|
|
3377
3483
|
seq: raw.seq,
|
|
3378
|
-
metadata
|
|
3484
|
+
metadata,
|
|
3379
3485
|
metadataVersion: raw.metadataVersion,
|
|
3380
3486
|
agentState: raw.agentState ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.agentState)) : null,
|
|
3381
3487
|
agentStateVersion: raw.agentStateVersion,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createDefaultRuntimeShell } from './index-
|
|
1
|
+
import { c as createDefaultRuntimeShell } from './index-BKjWLXkN.mjs';
|
|
2
2
|
import 'chalk';
|
|
3
|
-
import './api-
|
|
3
|
+
import './api-CfHYTLZX.mjs';
|
|
4
4
|
import 'axios';
|
|
5
5
|
import 'fs';
|
|
6
6
|
import 'node:fs';
|
|
@@ -16,7 +16,7 @@ import 'crypto';
|
|
|
16
16
|
import 'path';
|
|
17
17
|
import 'node:child_process';
|
|
18
18
|
import 'expo-server-sdk';
|
|
19
|
-
import './persistence-
|
|
19
|
+
import './persistence-Nun5c360.mjs';
|
|
20
20
|
import 'node:fs/promises';
|
|
21
21
|
import 'os';
|
|
22
22
|
import 'tmp';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-C3bSe5_d.cjs');
|
|
4
4
|
require('chalk');
|
|
5
|
-
require('./api-
|
|
5
|
+
require('./api-CfmTDha2.cjs');
|
|
6
6
|
require('axios');
|
|
7
7
|
require('fs');
|
|
8
8
|
require('node:fs');
|
|
@@ -18,7 +18,7 @@ require('crypto');
|
|
|
18
18
|
require('path');
|
|
19
19
|
require('node:child_process');
|
|
20
20
|
require('expo-server-sdk');
|
|
21
|
-
require('./persistence-
|
|
21
|
+
require('./persistence-Db4p7gsX.cjs');
|
|
22
22
|
require('node:fs/promises');
|
|
23
23
|
require('os');
|
|
24
24
|
require('tmp');
|
|
@@ -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, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders, p as decodeBase64, q as HAPPY_CLOUD_DAEMON_PORT, r as packageJson, A as ApiClient, t as HeadTailPreviewBuffer, u as getLatestDaemonLog } from './api-
|
|
3
|
-
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-
|
|
2
|
+
import { l as logger, e as encodeBase64, c as configuration, k as buildAuthenticatedHeaders, S as SigningBootstrapRequiredError, m as SIGNING_BOOTSTRAP_REQUIRED_MESSAGE, n as encodeBase64Url, h as delay, o as buildClientHeaders, p as decodeBase64, q as HAPPY_CLOUD_DAEMON_PORT, r as packageJson, A as ApiClient, t as HeadTailPreviewBuffer, u as getLatestDaemonLog } from './api-CfHYTLZX.mjs';
|
|
3
|
+
import { writeCredentialsLegacy, writeCredentialsDataKey, readCredentials, readSettings, updateSettings, readDaemonState, clearDaemonState, acquireDaemonLock, writeDaemonState, releaseDaemonLock, validateProfileForAgent, getProfileEnvironmentVariables, clearCredentials, clearMachineId } from './persistence-Nun5c360.mjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import fs, { writeFile as writeFile$1, rename, unlink as unlink$1 } from 'fs/promises';
|
|
6
6
|
import os$1, { homedir } from 'os';
|
|
@@ -2447,6 +2447,20 @@ function buildDaemonChildEnv(baseEnv, extraEnv) {
|
|
|
2447
2447
|
}
|
|
2448
2448
|
|
|
2449
2449
|
const DIFFERENT_DAEMON_RUNNING_MESSAGE = "A different daemon was started without killing us. We should kill ourselves.";
|
|
2450
|
+
function pruneStaleTrackedSessions({
|
|
2451
|
+
trackedSessionPids,
|
|
2452
|
+
removeTrackedSession,
|
|
2453
|
+
kill = process.kill
|
|
2454
|
+
}) {
|
|
2455
|
+
for (const pid of trackedSessionPids) {
|
|
2456
|
+
try {
|
|
2457
|
+
kill(pid, 0);
|
|
2458
|
+
} catch {
|
|
2459
|
+
logger.debug(`[DAEMON RUN] Removing stale session with PID ${pid} (process no longer exists)`);
|
|
2460
|
+
removeTrackedSession(pid);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2450
2464
|
function readProjectCliVersionFromDisk() {
|
|
2451
2465
|
const packageJsonPath = join(projectPath(), "package.json");
|
|
2452
2466
|
try {
|
|
@@ -2477,14 +2491,10 @@ async function runDaemonHealthCheck({
|
|
|
2477
2491
|
requestShutdown,
|
|
2478
2492
|
writeHeartbeat
|
|
2479
2493
|
}) {
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
logger.debug(`[DAEMON RUN] Removing stale session with PID ${pid} (process no longer exists)`);
|
|
2485
|
-
removeTrackedSession(pid);
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2494
|
+
pruneStaleTrackedSessions({
|
|
2495
|
+
trackedSessionPids,
|
|
2496
|
+
removeTrackedSession
|
|
2497
|
+
});
|
|
2488
2498
|
const projectVersion = readProjectCliVersion();
|
|
2489
2499
|
if (projectVersion && projectVersion !== currentCliVersion) {
|
|
2490
2500
|
await onDaemonOutdated();
|
|
@@ -2594,7 +2604,8 @@ async function precreateDaemonManagedSession(opts) {
|
|
|
2594
2604
|
return await opts.api.getOrCreateSession({
|
|
2595
2605
|
tag: opts.sessionTag,
|
|
2596
2606
|
metadata,
|
|
2597
|
-
state
|
|
2607
|
+
state,
|
|
2608
|
+
stableDataKeyPath: ["managed-session-tag", opts.sessionTag]
|
|
2598
2609
|
});
|
|
2599
2610
|
} catch (error) {
|
|
2600
2611
|
logger.debug(`[DAEMON RUN] Failed to precreate ${opts.flavor} session for tag ${opts.sessionTag}`, error);
|
|
@@ -2956,7 +2967,42 @@ function resolveManagedSessionTag(env = process.env) {
|
|
|
2956
2967
|
return readManagedSessionTag(env) ?? randomUUID();
|
|
2957
2968
|
}
|
|
2958
2969
|
|
|
2970
|
+
const DEFAULT_MAX_LINES = 40;
|
|
2971
|
+
const DEFAULT_MAX_LINE_LENGTH = 300;
|
|
2972
|
+
function truncateLine(line, maxLineLength) {
|
|
2973
|
+
if (line.length <= maxLineLength) {
|
|
2974
|
+
return line;
|
|
2975
|
+
}
|
|
2976
|
+
return `${line.slice(0, Math.max(0, maxLineLength - 3))}...`;
|
|
2977
|
+
}
|
|
2978
|
+
function createChildProcessOutputBuffer(options) {
|
|
2979
|
+
const maxLines = Math.max(1, DEFAULT_MAX_LINES);
|
|
2980
|
+
const maxLineLength = Math.max(4, DEFAULT_MAX_LINE_LENGTH);
|
|
2981
|
+
const lines = [];
|
|
2982
|
+
return {
|
|
2983
|
+
push(text) {
|
|
2984
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
2985
|
+
const line = rawLine.trim();
|
|
2986
|
+
if (!line) {
|
|
2987
|
+
continue;
|
|
2988
|
+
}
|
|
2989
|
+
lines.push(truncateLine(line, maxLineLength));
|
|
2990
|
+
while (lines.length > maxLines) {
|
|
2991
|
+
lines.shift();
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
},
|
|
2995
|
+
snapshot() {
|
|
2996
|
+
return lines.join("\n");
|
|
2997
|
+
},
|
|
2998
|
+
hasContent() {
|
|
2999
|
+
return lines.length > 0;
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
|
|
2959
3004
|
const DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS = 45e3;
|
|
3005
|
+
const DEFAULT_TRACKED_SESSION_SWEEP_INTERVAL_MS = 5e3;
|
|
2960
3006
|
function resolveSessionWebhookTimeoutMs() {
|
|
2961
3007
|
const parsed = Number.parseInt(process.env.HAPPY_DAEMON_SESSION_WEBHOOK_TIMEOUT || "", 10);
|
|
2962
3008
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
@@ -2964,6 +3010,13 @@ function resolveSessionWebhookTimeoutMs() {
|
|
|
2964
3010
|
}
|
|
2965
3011
|
return DEFAULT_SESSION_WEBHOOK_TIMEOUT_MS;
|
|
2966
3012
|
}
|
|
3013
|
+
function resolveTrackedSessionSweepIntervalMs() {
|
|
3014
|
+
const parsed = Number.parseInt(process.env.HAPPY_DAEMON_TRACKED_SESSION_SWEEP_INTERVAL || "", 10);
|
|
3015
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3016
|
+
return parsed;
|
|
3017
|
+
}
|
|
3018
|
+
return DEFAULT_TRACKED_SESSION_SWEEP_INTERVAL_MS;
|
|
3019
|
+
}
|
|
2967
3020
|
const initialMachineMetadata = {
|
|
2968
3021
|
host: os$1.hostname(),
|
|
2969
3022
|
platform: os$1.platform(),
|
|
@@ -2994,6 +3047,7 @@ async function getProfileEnvironmentVariablesForAgent(profileId, agentType) {
|
|
|
2994
3047
|
}
|
|
2995
3048
|
async function startDaemon() {
|
|
2996
3049
|
const sessionWebhookTimeoutMs = resolveSessionWebhookTimeoutMs();
|
|
3050
|
+
const trackedSessionSweepIntervalMs = resolveTrackedSessionSweepIntervalMs();
|
|
2997
3051
|
const shutdownCoordinator = createShutdownCoordinator(async () => {
|
|
2998
3052
|
logger.debug("[DAEMON RUN] Startup malfunctioned, forcing exit with code 1");
|
|
2999
3053
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -3328,14 +3382,39 @@ async function startDaemon() {
|
|
|
3328
3382
|
// Capture stdout/stderr for debugging
|
|
3329
3383
|
env: buildDaemonChildEnv(process.env, extraEnv)
|
|
3330
3384
|
});
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
}
|
|
3338
|
-
|
|
3385
|
+
const stdoutBuffer = createChildProcessOutputBuffer();
|
|
3386
|
+
const stderrBuffer = createChildProcessOutputBuffer();
|
|
3387
|
+
let childOutputSnapshotLogged = false;
|
|
3388
|
+
const logChildOutputSnapshot = (reason) => {
|
|
3389
|
+
if (childOutputSnapshotLogged) {
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
childOutputSnapshotLogged = true;
|
|
3393
|
+
const stdoutSnapshot = stdoutBuffer.snapshot();
|
|
3394
|
+
const stderrSnapshot = stderrBuffer.snapshot();
|
|
3395
|
+
if (stdoutSnapshot) {
|
|
3396
|
+
logger.debug(`[DAEMON RUN] Child PID ${happyProcess.pid} recent stdout before ${reason}:
|
|
3397
|
+
${stdoutSnapshot}`);
|
|
3398
|
+
}
|
|
3399
|
+
if (stderrSnapshot) {
|
|
3400
|
+
logger.debug(`[DAEMON RUN] Child PID ${happyProcess.pid} recent stderr before ${reason}:
|
|
3401
|
+
${stderrSnapshot}`);
|
|
3402
|
+
}
|
|
3403
|
+
};
|
|
3404
|
+
happyProcess.stdout?.on("data", (data) => {
|
|
3405
|
+
const text = data.toString();
|
|
3406
|
+
stdoutBuffer.push(text);
|
|
3407
|
+
if (process.env.DEBUG) {
|
|
3408
|
+
logger.debug(`[DAEMON RUN] Child stdout: ${text}`);
|
|
3409
|
+
}
|
|
3410
|
+
});
|
|
3411
|
+
happyProcess.stderr?.on("data", (data) => {
|
|
3412
|
+
const text = data.toString();
|
|
3413
|
+
stderrBuffer.push(text);
|
|
3414
|
+
if (process.env.DEBUG) {
|
|
3415
|
+
logger.debug(`[DAEMON RUN] Child stderr: ${text}`);
|
|
3416
|
+
}
|
|
3417
|
+
});
|
|
3339
3418
|
if (!happyProcess.pid) {
|
|
3340
3419
|
logger.debug("[DAEMON RUN] Failed to spawn process - no PID returned");
|
|
3341
3420
|
if (precreatedCodexSession && api) {
|
|
@@ -3366,12 +3445,14 @@ async function startDaemon() {
|
|
|
3366
3445
|
};
|
|
3367
3446
|
pidToTrackedSession.set(happyProcess.pid, trackedSession);
|
|
3368
3447
|
happyProcess.on("exit", (code, signal) => {
|
|
3448
|
+
logChildOutputSnapshot(`exit (code=${code}, signal=${signal})`);
|
|
3369
3449
|
logger.debug(`[DAEMON RUN] Child PID ${happyProcess.pid} exited with code ${code}, signal ${signal}`);
|
|
3370
3450
|
if (happyProcess.pid) {
|
|
3371
3451
|
onChildExited(happyProcess.pid);
|
|
3372
3452
|
}
|
|
3373
3453
|
});
|
|
3374
3454
|
happyProcess.on("error", (error) => {
|
|
3455
|
+
logChildOutputSnapshot("process error");
|
|
3375
3456
|
logger.debug(`[DAEMON RUN] Child process error:`, error);
|
|
3376
3457
|
if (happyProcess.pid) {
|
|
3377
3458
|
onChildExited(happyProcess.pid);
|
|
@@ -3388,6 +3469,7 @@ async function startDaemon() {
|
|
|
3388
3469
|
return new Promise((resolve) => {
|
|
3389
3470
|
const timeout = setTimeout(() => {
|
|
3390
3471
|
pidToAwaiter.delete(happyProcess.pid);
|
|
3472
|
+
logChildOutputSnapshot("session webhook timeout");
|
|
3391
3473
|
logger.debug(`[DAEMON RUN] Session webhook timeout for PID ${happyProcess.pid}`);
|
|
3392
3474
|
const spawnError = createSpawnSessionError(
|
|
3393
3475
|
SPAWN_SESSION_ERROR_CODES.SESSION_WEBHOOK_TIMEOUT,
|
|
@@ -3572,6 +3654,25 @@ async function startDaemon() {
|
|
|
3572
3654
|
logger.debug("[DAEMON RUN] Remote session index startup recovery failed", error);
|
|
3573
3655
|
});
|
|
3574
3656
|
const heartbeatIntervalMs = parseInt(process.env.HAPPY_DAEMON_HEARTBEAT_INTERVAL || "60000");
|
|
3657
|
+
let trackedSessionSweepRunning = false;
|
|
3658
|
+
const trackedSessionSweepInterval = setInterval(() => {
|
|
3659
|
+
if (trackedSessionSweepRunning) {
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
trackedSessionSweepRunning = true;
|
|
3663
|
+
try {
|
|
3664
|
+
pruneStaleTrackedSessions({
|
|
3665
|
+
trackedSessionPids: pidToTrackedSession.keys(),
|
|
3666
|
+
removeTrackedSession: (pid) => {
|
|
3667
|
+
removeTrackedSession(pid, `fast stale-session sweep removed tracked PID ${pid}`);
|
|
3668
|
+
}
|
|
3669
|
+
});
|
|
3670
|
+
} catch (error) {
|
|
3671
|
+
logger.debug("[DAEMON RUN] Fast tracked-session sweep failed", error);
|
|
3672
|
+
} finally {
|
|
3673
|
+
trackedSessionSweepRunning = false;
|
|
3674
|
+
}
|
|
3675
|
+
}, trackedSessionSweepIntervalMs);
|
|
3575
3676
|
let heartbeatRunning = false;
|
|
3576
3677
|
const restartOnStaleVersionAndHeartbeat = setInterval(async () => {
|
|
3577
3678
|
if (heartbeatRunning) {
|
|
@@ -3643,6 +3744,7 @@ async function startDaemon() {
|
|
|
3643
3744
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
3644
3745
|
logger.debug("[DAEMON RUN] Health check interval cleared");
|
|
3645
3746
|
}
|
|
3747
|
+
clearInterval(trackedSessionSweepInterval);
|
|
3646
3748
|
if (remoteSessionIndexRefreshTimer) {
|
|
3647
3749
|
clearTimeout(remoteSessionIndexRefreshTimer);
|
|
3648
3750
|
remoteSessionIndexRefreshTimer = null;
|
|
@@ -7567,7 +7669,14 @@ ${recentStderrExcerpt}`);
|
|
|
7567
7669
|
prompt: [contentBlock]
|
|
7568
7670
|
};
|
|
7569
7671
|
logger.debug(`[AcpBackend] Prompt request:`, JSON.stringify(promptRequest, null, 2));
|
|
7570
|
-
await
|
|
7672
|
+
await raceWithProcessExit(
|
|
7673
|
+
this.process,
|
|
7674
|
+
this.connection.prompt(promptRequest),
|
|
7675
|
+
{
|
|
7676
|
+
agentName: this.transport.agentName,
|
|
7677
|
+
operationName: "prompt"
|
|
7678
|
+
}
|
|
7679
|
+
);
|
|
7571
7680
|
logger.debug("[AcpBackend] Prompt request sent to ACP connection");
|
|
7572
7681
|
if (this.waitingForResponse && this.activeToolCalls.size === 0 && this.sawSessionUpdateSincePrompt === false) {
|
|
7573
7682
|
const noUpdatesTimeoutMs = this.getPostPromptNoUpdatesTimeoutMs();
|
|
@@ -9730,11 +9839,11 @@ var launch = /*#__PURE__*/Object.freeze({
|
|
|
9730
9839
|
|
|
9731
9840
|
const unifiedProviderExecutors = {
|
|
9732
9841
|
claude: async (opts) => {
|
|
9733
|
-
const { runClaude } = await import('./runClaude-
|
|
9842
|
+
const { runClaude } = await import('./runClaude-ClXU2LZA.mjs');
|
|
9734
9843
|
await runClaude(opts.credentials, opts.claudeOptions ?? {});
|
|
9735
9844
|
},
|
|
9736
9845
|
codex: async (opts) => {
|
|
9737
|
-
const { runCodex } = await import('./runCodex-
|
|
9846
|
+
const { runCodex } = await import('./runCodex-CXu7_Wgk.mjs');
|
|
9738
9847
|
await runCodex({
|
|
9739
9848
|
credentials: opts.credentials,
|
|
9740
9849
|
startedBy: opts.startedBy,
|
|
@@ -9743,7 +9852,7 @@ const unifiedProviderExecutors = {
|
|
|
9743
9852
|
});
|
|
9744
9853
|
},
|
|
9745
9854
|
gemini: async (opts) => {
|
|
9746
|
-
const { runGemini } = await import('./runGemini-
|
|
9855
|
+
const { runGemini } = await import('./runGemini-BlJjRWN1.mjs');
|
|
9747
9856
|
await runGemini({
|
|
9748
9857
|
credentials: opts.credentials,
|
|
9749
9858
|
startedBy: opts.startedBy
|
|
@@ -9756,13 +9865,17 @@ const unifiedProviderExecutors = {
|
|
|
9756
9865
|
});
|
|
9757
9866
|
}
|
|
9758
9867
|
};
|
|
9759
|
-
async function ensureUnifiedRuntimePrerequisites(credentials) {
|
|
9868
|
+
async function ensureUnifiedRuntimePrerequisites(credentials, opts) {
|
|
9760
9869
|
if (credentials) {
|
|
9761
|
-
|
|
9870
|
+
if (!opts?.skipDaemonBootstrap) {
|
|
9871
|
+
await ensureUnifiedDaemonStarted();
|
|
9872
|
+
}
|
|
9762
9873
|
return credentials;
|
|
9763
9874
|
}
|
|
9764
9875
|
const auth = await authAndSetupMachineIfNeeded();
|
|
9765
|
-
|
|
9876
|
+
if (!opts?.skipDaemonBootstrap) {
|
|
9877
|
+
await ensureUnifiedDaemonStarted();
|
|
9878
|
+
}
|
|
9766
9879
|
return auth.credentials;
|
|
9767
9880
|
}
|
|
9768
9881
|
async function ensureUnifiedDaemonStarted() {
|
|
@@ -9783,7 +9896,10 @@ async function ensureUnifiedDaemonStarted() {
|
|
|
9783
9896
|
throw new Error("Failed to start Happy background service.");
|
|
9784
9897
|
}
|
|
9785
9898
|
async function executeUnifiedProvider(opts) {
|
|
9786
|
-
const
|
|
9899
|
+
const skipDaemonBootstrap = "startedBy" in opts && opts.startedBy === "daemon" || opts.provider === "claude" && opts.claudeOptions?.startedBy === "daemon";
|
|
9900
|
+
const credentials = await ensureUnifiedRuntimePrerequisites(opts.credentials, {
|
|
9901
|
+
skipDaemonBootstrap
|
|
9902
|
+
});
|
|
9787
9903
|
await unifiedProviderExecutors[opts.provider]({
|
|
9788
9904
|
...opts,
|
|
9789
9905
|
credentials
|
|
@@ -9819,7 +9935,7 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
9819
9935
|
return;
|
|
9820
9936
|
} else if (subcommand === "runtime") {
|
|
9821
9937
|
if (args[1] === "providers") {
|
|
9822
|
-
const { renderRuntimeProviders } = await import('./command-
|
|
9938
|
+
const { renderRuntimeProviders } = await import('./command-B0HfYWYW.mjs');
|
|
9823
9939
|
console.log(renderRuntimeProviders());
|
|
9824
9940
|
return;
|
|
9825
9941
|
}
|
|
@@ -9997,8 +10113,8 @@ function shouldRunMainClaudeFlow(opts) {
|
|
|
9997
10113
|
const projectId = args[3];
|
|
9998
10114
|
try {
|
|
9999
10115
|
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return config; });
|
|
10000
|
-
const { readCredentials: readCredentials2 } = await import('./persistence-
|
|
10001
|
-
const { ApiClient: ApiClient2 } = await import('./api-
|
|
10116
|
+
const { readCredentials: readCredentials2 } = await import('./persistence-Nun5c360.mjs');
|
|
10117
|
+
const { ApiClient: ApiClient2 } = await import('./api-CfHYTLZX.mjs').then(function (n) { return n.v; });
|
|
10002
10118
|
let userEmail = void 0;
|
|
10003
10119
|
try {
|
|
10004
10120
|
const credentials = await readCredentials2();
|