happy-imou-cloud 2.0.10 → 2.0.11
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-5ACv9gKu.mjs +320 -0
- package/dist/BaseReasoningProcessor-COrRWyn0.cjs +323 -0
- package/dist/ProviderSelectionHandler-BljOLMQn.mjs +261 -0
- package/dist/ProviderSelectionHandler-DBGobhGZ.cjs +265 -0
- package/dist/{api-BjxmW-0W.mjs → api-BcWf5v4i.mjs} +174 -46
- package/dist/{api-DUE5TJBE.cjs → api-Ye-rPX6s.cjs} +174 -46
- package/dist/{command-Df7u5eAT.cjs → command-BK93nizl.cjs} +6 -6
- package/dist/{command-ComOeFLY.mjs → command-nOI80Mnm.mjs} +6 -6
- package/dist/{index-CzvgPwr1.mjs → index-DnsqY6I_.mjs} +189 -60
- package/dist/{index-Cuvfa15L.cjs → index-J7QKJ8lc.cjs} +194 -61
- package/dist/index.cjs +8 -8
- package/dist/index.mjs +8 -8
- package/dist/lib.cjs +2 -3
- package/dist/lib.d.cts +2 -0
- package/dist/lib.d.mts +2 -0
- package/dist/lib.mjs +2 -3
- package/dist/{persistence-BxP6Jw1f.mjs → persistence-BMa6cyw9.mjs} +2 -3
- package/dist/{persistence-D7JtnrYA.cjs → persistence-xK5CKhbn.cjs} +2 -3
- package/dist/registerKillSessionHandler-CH6yN0eG.cjs +1198 -0
- package/dist/registerKillSessionHandler-Dxwg4L4J.mjs +1179 -0
- package/dist/{runClaude-C1W_Nw0C.cjs → runClaude-BMHlBny_.cjs} +884 -398
- package/dist/{runClaude-B_fTMxc4.mjs → runClaude-cQ-UT0Ke.mjs} +882 -396
- package/dist/{runCodex-CUgOiIEz.mjs → runCodex-AnJUPIhX.mjs} +155 -578
- package/dist/{runCodex-D2VCWVEK.cjs → runCodex-roOSOWWL.cjs} +158 -583
- package/dist/{runGemini-2_FEtJYa.mjs → runGemini-BGo_0mzA.mjs} +142 -387
- package/dist/{runGemini-CiGnjflq.cjs → runGemini-Cg6Zbqlz.cjs} +145 -390
- package/package.json +9 -8
- package/scripts/e2e/fake-codex-acp-agent.mjs +139 -0
- package/scripts/e2e/local-server-session-roundtrip.mjs +1063 -0
- package/scripts/release-smoke.mjs +23 -6
- package/dist/names-CicSgRNg.mjs +0 -625
- package/dist/names-yJNZoTv_.cjs +0 -636
- package/dist/registerKillSessionHandler-ARQrPvnT.mjs +0 -454
- package/dist/registerKillSessionHandler-DCMFiXyA.cjs +0 -459
|
@@ -9,21 +9,20 @@ import { io } from 'socket.io-client';
|
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash as createHash$1, createHmac, hkdfSync } from 'node:crypto';
|
|
11
11
|
import tweetnacl from 'tweetnacl';
|
|
12
|
-
import { exec } from 'child_process';
|
|
13
|
-
import { promisify } from 'util';
|
|
14
12
|
import { readFile, stat, writeFile, readdir } from 'fs/promises';
|
|
15
13
|
import { createHash } from 'crypto';
|
|
16
14
|
import { resolve, join as join$1 } from 'path';
|
|
15
|
+
import { spawn } from 'node:child_process';
|
|
17
16
|
import { Expo } from 'expo-server-sdk';
|
|
18
17
|
|
|
19
18
|
var name = "happy-imou-cloud";
|
|
20
|
-
var version = "2.0.
|
|
19
|
+
var version = "2.0.11";
|
|
21
20
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
22
21
|
var author = "long.zhu";
|
|
23
22
|
var license = "MIT";
|
|
24
23
|
var type = "module";
|
|
25
24
|
var bin = {
|
|
26
|
-
hicloud: "
|
|
25
|
+
hicloud: "bin/happy-cloud.mjs"
|
|
27
26
|
};
|
|
28
27
|
var main = "./dist/index.cjs";
|
|
29
28
|
var module$1 = "./dist/index.mjs";
|
|
@@ -75,6 +74,7 @@ var scripts = {
|
|
|
75
74
|
dev: "tsx src/index.ts",
|
|
76
75
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
77
76
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
77
|
+
"e2e:local-server": "yarn build && node ./scripts/e2e/local-server-session-roundtrip.mjs",
|
|
78
78
|
"// ==== Release ====": "",
|
|
79
79
|
prepublishOnly: "yarn release:smoke",
|
|
80
80
|
release: "yarn install && release-it",
|
|
@@ -430,7 +430,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
430
430
|
return { file, path: fullPath, modified: stats.mtime };
|
|
431
431
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
432
432
|
try {
|
|
433
|
-
const { readDaemonState } = await import('./persistence-
|
|
433
|
+
const { readDaemonState } = await import('./persistence-BMa6cyw9.mjs');
|
|
434
434
|
const state = await readDaemonState();
|
|
435
435
|
if (!state) {
|
|
436
436
|
return logs;
|
|
@@ -865,7 +865,131 @@ function validatePath(targetPath, workingDirectory) {
|
|
|
865
865
|
return { valid: true };
|
|
866
866
|
}
|
|
867
867
|
|
|
868
|
-
const
|
|
868
|
+
const DEFAULT_SHELL_COMMAND_TIMEOUT_MS = 3e4;
|
|
869
|
+
const DEFAULT_SHELL_OUTPUT_MAX_BYTES = 256 * 1024;
|
|
870
|
+
class BoundedTailBuffer {
|
|
871
|
+
chunks = [];
|
|
872
|
+
maxBytes;
|
|
873
|
+
storedBytes = 0;
|
|
874
|
+
receivedBytes = 0;
|
|
875
|
+
constructor(maxBytes) {
|
|
876
|
+
this.maxBytes = Math.max(1, maxBytes);
|
|
877
|
+
}
|
|
878
|
+
append(chunk) {
|
|
879
|
+
const normalized = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
880
|
+
if (normalized.length === 0) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
this.receivedBytes += normalized.length;
|
|
884
|
+
this.chunks.push(normalized);
|
|
885
|
+
this.storedBytes += normalized.length;
|
|
886
|
+
while (this.storedBytes > this.maxBytes && this.chunks.length > 0) {
|
|
887
|
+
const overflow = this.storedBytes - this.maxBytes;
|
|
888
|
+
const oldest = this.chunks[0];
|
|
889
|
+
if (oldest.length <= overflow) {
|
|
890
|
+
this.chunks.shift();
|
|
891
|
+
this.storedBytes -= oldest.length;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
this.chunks[0] = oldest.subarray(overflow);
|
|
895
|
+
this.storedBytes -= overflow;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
toString(label) {
|
|
899
|
+
const content = Buffer.concat(this.chunks).toString("utf8");
|
|
900
|
+
if (this.receivedBytes <= this.maxBytes) {
|
|
901
|
+
return content;
|
|
902
|
+
}
|
|
903
|
+
return `...[${label} truncated, showing last ${this.maxBytes} bytes of ${this.receivedBytes}]
|
|
904
|
+
${content}`;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
async function runShellCommand(opts) {
|
|
908
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_COMMAND_TIMEOUT_MS;
|
|
909
|
+
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_SHELL_OUTPUT_MAX_BYTES;
|
|
910
|
+
return await new Promise((resolve) => {
|
|
911
|
+
const stdout = new BoundedTailBuffer(maxOutputBytes);
|
|
912
|
+
const stderr = new BoundedTailBuffer(maxOutputBytes);
|
|
913
|
+
const child = spawn(opts.command, [], {
|
|
914
|
+
cwd: opts.cwd,
|
|
915
|
+
shell: true,
|
|
916
|
+
windowsHide: true
|
|
917
|
+
});
|
|
918
|
+
let settled = false;
|
|
919
|
+
let timedOut = false;
|
|
920
|
+
let timeoutHandle;
|
|
921
|
+
const finish = (result) => {
|
|
922
|
+
if (settled) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
settled = true;
|
|
926
|
+
if (timeoutHandle) {
|
|
927
|
+
clearTimeout(timeoutHandle);
|
|
928
|
+
}
|
|
929
|
+
resolve(result);
|
|
930
|
+
};
|
|
931
|
+
child.stdout?.on("data", (chunk) => {
|
|
932
|
+
stdout.append(chunk);
|
|
933
|
+
});
|
|
934
|
+
child.stderr?.on("data", (chunk) => {
|
|
935
|
+
stderr.append(chunk);
|
|
936
|
+
});
|
|
937
|
+
child.on("error", (error) => {
|
|
938
|
+
finish({
|
|
939
|
+
success: false,
|
|
940
|
+
stdout: stdout.toString("stdout"),
|
|
941
|
+
stderr: stderr.toString("stderr"),
|
|
942
|
+
exitCode: 1,
|
|
943
|
+
error: error.message
|
|
944
|
+
});
|
|
945
|
+
});
|
|
946
|
+
child.on("close", (code, signal) => {
|
|
947
|
+
const stdoutText = stdout.toString("stdout");
|
|
948
|
+
const stderrText = stderr.toString("stderr");
|
|
949
|
+
if (timedOut) {
|
|
950
|
+
finish({
|
|
951
|
+
success: false,
|
|
952
|
+
stdout: stdoutText,
|
|
953
|
+
stderr: stderrText,
|
|
954
|
+
exitCode: typeof code === "number" ? code : -1,
|
|
955
|
+
error: "Command timed out"
|
|
956
|
+
});
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
if (code === 0) {
|
|
960
|
+
finish({
|
|
961
|
+
success: true,
|
|
962
|
+
stdout: stdoutText,
|
|
963
|
+
stderr: stderrText,
|
|
964
|
+
exitCode: 0
|
|
965
|
+
});
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
finish({
|
|
969
|
+
success: false,
|
|
970
|
+
stdout: stdoutText,
|
|
971
|
+
stderr: stderrText,
|
|
972
|
+
exitCode: typeof code === "number" ? code : 1,
|
|
973
|
+
error: stderrText || (signal ? `Command exited due to signal ${signal}` : `Command failed with exit code ${code ?? "unknown"}`)
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
timeoutHandle = setTimeout(() => {
|
|
977
|
+
timedOut = true;
|
|
978
|
+
try {
|
|
979
|
+
child.kill();
|
|
980
|
+
} catch {
|
|
981
|
+
finish({
|
|
982
|
+
success: false,
|
|
983
|
+
stdout: stdout.toString("stdout"),
|
|
984
|
+
stderr: stderr.toString("stderr"),
|
|
985
|
+
exitCode: -1,
|
|
986
|
+
error: "Command timed out"
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
}, timeoutMs);
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
|
|
869
993
|
function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
870
994
|
rpcHandlerManager.registerHandler("bash", async (data) => {
|
|
871
995
|
logger.debug("Shell command request:", data.command);
|
|
@@ -876,57 +1000,37 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
876
1000
|
}
|
|
877
1001
|
}
|
|
878
1002
|
try {
|
|
879
|
-
const
|
|
880
|
-
|
|
1003
|
+
const cwd = data.cwd === "/" ? void 0 : data.cwd;
|
|
1004
|
+
logger.debug("Shell command executing...", {
|
|
1005
|
+
cwd,
|
|
881
1006
|
timeout: data.timeout || 3e4
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
};
|
|
893
|
-
logger.debug("Shell command result:", {
|
|
894
|
-
success: true,
|
|
895
|
-
exitCode: 0,
|
|
1007
|
+
});
|
|
1008
|
+
const result = await runShellCommand({
|
|
1009
|
+
command: data.command,
|
|
1010
|
+
cwd,
|
|
1011
|
+
timeoutMs: data.timeout || 3e4
|
|
1012
|
+
});
|
|
1013
|
+
logger.debug("Shell command finished:", {
|
|
1014
|
+
success: result.success,
|
|
1015
|
+
exitCode: result.exitCode,
|
|
1016
|
+
error: result.error,
|
|
896
1017
|
stdoutLen: result.stdout.length,
|
|
897
1018
|
stderrLen: result.stderr.length
|
|
898
1019
|
});
|
|
899
1020
|
return result;
|
|
900
1021
|
} catch (error) {
|
|
901
|
-
const execError = error;
|
|
902
|
-
if (execError.code === "ETIMEDOUT" || execError.killed) {
|
|
903
|
-
const result2 = {
|
|
904
|
-
success: false,
|
|
905
|
-
stdout: execError.stdout || "",
|
|
906
|
-
stderr: execError.stderr || "",
|
|
907
|
-
exitCode: typeof execError.code === "number" ? execError.code : -1,
|
|
908
|
-
error: "Command timed out"
|
|
909
|
-
};
|
|
910
|
-
logger.debug("Shell command timed out:", {
|
|
911
|
-
success: false,
|
|
912
|
-
exitCode: result2.exitCode,
|
|
913
|
-
error: "Command timed out"
|
|
914
|
-
});
|
|
915
|
-
return result2;
|
|
916
|
-
}
|
|
917
1022
|
const result = {
|
|
918
1023
|
success: false,
|
|
919
|
-
stdout:
|
|
920
|
-
stderr:
|
|
921
|
-
exitCode:
|
|
922
|
-
error:
|
|
1024
|
+
stdout: "",
|
|
1025
|
+
stderr: "",
|
|
1026
|
+
exitCode: 1,
|
|
1027
|
+
error: error instanceof Error ? error.message : "Command failed"
|
|
923
1028
|
};
|
|
924
|
-
logger.debug("Shell command failed:", {
|
|
1029
|
+
logger.debug("Shell command failed before execution completed:", {
|
|
1030
|
+
cwd: data.cwd === "/" ? void 0 : data.cwd,
|
|
925
1031
|
success: false,
|
|
926
1032
|
exitCode: result.exitCode,
|
|
927
|
-
error: result.error
|
|
928
|
-
stdoutLen: result.stdout.length,
|
|
929
|
-
stderrLen: result.stderr.length
|
|
1033
|
+
error: result.error
|
|
930
1034
|
});
|
|
931
1035
|
return result;
|
|
932
1036
|
}
|
|
@@ -1343,6 +1447,8 @@ function buildSocketAuth(opts) {
|
|
|
1343
1447
|
return auth;
|
|
1344
1448
|
}
|
|
1345
1449
|
|
|
1450
|
+
const MAX_PENDING_RELIABLE_CODEX_MESSAGES = 200;
|
|
1451
|
+
const MAX_PENDING_RELIABLE_CODEX_MESSAGE_BYTES = 512 * 1024;
|
|
1346
1452
|
class ApiSessionClient extends EventEmitter {
|
|
1347
1453
|
credentials;
|
|
1348
1454
|
sessionId;
|
|
@@ -1359,6 +1465,7 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1359
1465
|
encryptionKey;
|
|
1360
1466
|
encryptionVariant;
|
|
1361
1467
|
pendingReliableCodexMessages = [];
|
|
1468
|
+
pendingReliableCodexMessageBytes = 0;
|
|
1362
1469
|
reconnectAfterServerDisconnectTimer = null;
|
|
1363
1470
|
lastSocketServerError = null;
|
|
1364
1471
|
constructor(credentials, session) {
|
|
@@ -1565,6 +1672,8 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1565
1672
|
if (this.shouldBufferReliableCodexMessage(body)) {
|
|
1566
1673
|
logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
|
|
1567
1674
|
this.pendingReliableCodexMessages.push({ encrypted, type: eventType });
|
|
1675
|
+
this.pendingReliableCodexMessageBytes += encrypted.length;
|
|
1676
|
+
this.trimPendingReliableCodexMessages();
|
|
1568
1677
|
} else {
|
|
1569
1678
|
logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
|
|
1570
1679
|
}
|
|
@@ -1740,6 +1849,7 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1740
1849
|
return;
|
|
1741
1850
|
}
|
|
1742
1851
|
const buffered = this.pendingReliableCodexMessages.splice(0, this.pendingReliableCodexMessages.length);
|
|
1852
|
+
this.pendingReliableCodexMessageBytes = 0;
|
|
1743
1853
|
logger.debug("[API] Flushing buffered Codex messages after reconnect", {
|
|
1744
1854
|
count: buffered.length,
|
|
1745
1855
|
types: buffered.map((message) => message.type)
|
|
@@ -1763,6 +1873,24 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1763
1873
|
return false;
|
|
1764
1874
|
}
|
|
1765
1875
|
}
|
|
1876
|
+
trimPendingReliableCodexMessages() {
|
|
1877
|
+
let dropped = 0;
|
|
1878
|
+
while (this.pendingReliableCodexMessages.length > MAX_PENDING_RELIABLE_CODEX_MESSAGES || this.pendingReliableCodexMessageBytes > MAX_PENDING_RELIABLE_CODEX_MESSAGE_BYTES) {
|
|
1879
|
+
const removed = this.pendingReliableCodexMessages.shift();
|
|
1880
|
+
if (!removed) {
|
|
1881
|
+
break;
|
|
1882
|
+
}
|
|
1883
|
+
this.pendingReliableCodexMessageBytes -= removed.encrypted.length;
|
|
1884
|
+
dropped += 1;
|
|
1885
|
+
}
|
|
1886
|
+
if (dropped > 0) {
|
|
1887
|
+
logger.debug("[API] Dropped oldest buffered Codex messages to cap reconnect memory usage", {
|
|
1888
|
+
dropped,
|
|
1889
|
+
remaining: this.pendingReliableCodexMessages.length,
|
|
1890
|
+
bytes: this.pendingReliableCodexMessageBytes
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1766
1894
|
retryAfterServerDisconnect(reason) {
|
|
1767
1895
|
if (reason !== "io server disconnect") {
|
|
1768
1896
|
return;
|
|
@@ -11,21 +11,20 @@ var socket_ioClient = require('socket.io-client');
|
|
|
11
11
|
var z = require('zod');
|
|
12
12
|
var node_crypto = require('node:crypto');
|
|
13
13
|
var tweetnacl = require('tweetnacl');
|
|
14
|
-
var child_process = require('child_process');
|
|
15
|
-
var util = require('util');
|
|
16
14
|
var fs$2 = require('fs/promises');
|
|
17
15
|
var crypto = require('crypto');
|
|
18
16
|
var path$1 = require('path');
|
|
17
|
+
var node_child_process = require('node:child_process');
|
|
19
18
|
var expoServerSdk = require('expo-server-sdk');
|
|
20
19
|
|
|
21
20
|
var name = "happy-imou-cloud";
|
|
22
|
-
var version = "2.0.
|
|
21
|
+
var version = "2.0.11";
|
|
23
22
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
24
23
|
var author = "long.zhu";
|
|
25
24
|
var license = "MIT";
|
|
26
25
|
var type = "module";
|
|
27
26
|
var bin = {
|
|
28
|
-
hicloud: "
|
|
27
|
+
hicloud: "bin/happy-cloud.mjs"
|
|
29
28
|
};
|
|
30
29
|
var main = "./dist/index.cjs";
|
|
31
30
|
var module$1 = "./dist/index.mjs";
|
|
@@ -77,6 +76,7 @@ var scripts = {
|
|
|
77
76
|
dev: "tsx src/index.ts",
|
|
78
77
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
79
78
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
79
|
+
"e2e:local-server": "yarn build && node ./scripts/e2e/local-server-session-roundtrip.mjs",
|
|
80
80
|
"// ==== Release ====": "",
|
|
81
81
|
prepublishOnly: "yarn release:smoke",
|
|
82
82
|
release: "yarn install && release-it",
|
|
@@ -432,7 +432,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
432
432
|
return { file, path: fullPath, modified: stats.mtime };
|
|
433
433
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
434
434
|
try {
|
|
435
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-
|
|
435
|
+
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-xK5CKhbn.cjs'); });
|
|
436
436
|
const state = await readDaemonState();
|
|
437
437
|
if (!state) {
|
|
438
438
|
return logs;
|
|
@@ -867,7 +867,131 @@ function validatePath(targetPath, workingDirectory) {
|
|
|
867
867
|
return { valid: true };
|
|
868
868
|
}
|
|
869
869
|
|
|
870
|
-
const
|
|
870
|
+
const DEFAULT_SHELL_COMMAND_TIMEOUT_MS = 3e4;
|
|
871
|
+
const DEFAULT_SHELL_OUTPUT_MAX_BYTES = 256 * 1024;
|
|
872
|
+
class BoundedTailBuffer {
|
|
873
|
+
chunks = [];
|
|
874
|
+
maxBytes;
|
|
875
|
+
storedBytes = 0;
|
|
876
|
+
receivedBytes = 0;
|
|
877
|
+
constructor(maxBytes) {
|
|
878
|
+
this.maxBytes = Math.max(1, maxBytes);
|
|
879
|
+
}
|
|
880
|
+
append(chunk) {
|
|
881
|
+
const normalized = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
882
|
+
if (normalized.length === 0) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
this.receivedBytes += normalized.length;
|
|
886
|
+
this.chunks.push(normalized);
|
|
887
|
+
this.storedBytes += normalized.length;
|
|
888
|
+
while (this.storedBytes > this.maxBytes && this.chunks.length > 0) {
|
|
889
|
+
const overflow = this.storedBytes - this.maxBytes;
|
|
890
|
+
const oldest = this.chunks[0];
|
|
891
|
+
if (oldest.length <= overflow) {
|
|
892
|
+
this.chunks.shift();
|
|
893
|
+
this.storedBytes -= oldest.length;
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
this.chunks[0] = oldest.subarray(overflow);
|
|
897
|
+
this.storedBytes -= overflow;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
toString(label) {
|
|
901
|
+
const content = Buffer.concat(this.chunks).toString("utf8");
|
|
902
|
+
if (this.receivedBytes <= this.maxBytes) {
|
|
903
|
+
return content;
|
|
904
|
+
}
|
|
905
|
+
return `...[${label} truncated, showing last ${this.maxBytes} bytes of ${this.receivedBytes}]
|
|
906
|
+
${content}`;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
async function runShellCommand(opts) {
|
|
910
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SHELL_COMMAND_TIMEOUT_MS;
|
|
911
|
+
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_SHELL_OUTPUT_MAX_BYTES;
|
|
912
|
+
return await new Promise((resolve) => {
|
|
913
|
+
const stdout = new BoundedTailBuffer(maxOutputBytes);
|
|
914
|
+
const stderr = new BoundedTailBuffer(maxOutputBytes);
|
|
915
|
+
const child = node_child_process.spawn(opts.command, [], {
|
|
916
|
+
cwd: opts.cwd,
|
|
917
|
+
shell: true,
|
|
918
|
+
windowsHide: true
|
|
919
|
+
});
|
|
920
|
+
let settled = false;
|
|
921
|
+
let timedOut = false;
|
|
922
|
+
let timeoutHandle;
|
|
923
|
+
const finish = (result) => {
|
|
924
|
+
if (settled) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
settled = true;
|
|
928
|
+
if (timeoutHandle) {
|
|
929
|
+
clearTimeout(timeoutHandle);
|
|
930
|
+
}
|
|
931
|
+
resolve(result);
|
|
932
|
+
};
|
|
933
|
+
child.stdout?.on("data", (chunk) => {
|
|
934
|
+
stdout.append(chunk);
|
|
935
|
+
});
|
|
936
|
+
child.stderr?.on("data", (chunk) => {
|
|
937
|
+
stderr.append(chunk);
|
|
938
|
+
});
|
|
939
|
+
child.on("error", (error) => {
|
|
940
|
+
finish({
|
|
941
|
+
success: false,
|
|
942
|
+
stdout: stdout.toString("stdout"),
|
|
943
|
+
stderr: stderr.toString("stderr"),
|
|
944
|
+
exitCode: 1,
|
|
945
|
+
error: error.message
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
child.on("close", (code, signal) => {
|
|
949
|
+
const stdoutText = stdout.toString("stdout");
|
|
950
|
+
const stderrText = stderr.toString("stderr");
|
|
951
|
+
if (timedOut) {
|
|
952
|
+
finish({
|
|
953
|
+
success: false,
|
|
954
|
+
stdout: stdoutText,
|
|
955
|
+
stderr: stderrText,
|
|
956
|
+
exitCode: typeof code === "number" ? code : -1,
|
|
957
|
+
error: "Command timed out"
|
|
958
|
+
});
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (code === 0) {
|
|
962
|
+
finish({
|
|
963
|
+
success: true,
|
|
964
|
+
stdout: stdoutText,
|
|
965
|
+
stderr: stderrText,
|
|
966
|
+
exitCode: 0
|
|
967
|
+
});
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
finish({
|
|
971
|
+
success: false,
|
|
972
|
+
stdout: stdoutText,
|
|
973
|
+
stderr: stderrText,
|
|
974
|
+
exitCode: typeof code === "number" ? code : 1,
|
|
975
|
+
error: stderrText || (signal ? `Command exited due to signal ${signal}` : `Command failed with exit code ${code ?? "unknown"}`)
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
timeoutHandle = setTimeout(() => {
|
|
979
|
+
timedOut = true;
|
|
980
|
+
try {
|
|
981
|
+
child.kill();
|
|
982
|
+
} catch {
|
|
983
|
+
finish({
|
|
984
|
+
success: false,
|
|
985
|
+
stdout: stdout.toString("stdout"),
|
|
986
|
+
stderr: stderr.toString("stderr"),
|
|
987
|
+
exitCode: -1,
|
|
988
|
+
error: "Command timed out"
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}, timeoutMs);
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
871
995
|
function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
872
996
|
rpcHandlerManager.registerHandler("bash", async (data) => {
|
|
873
997
|
logger.debug("Shell command request:", data.command);
|
|
@@ -878,57 +1002,37 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
878
1002
|
}
|
|
879
1003
|
}
|
|
880
1004
|
try {
|
|
881
|
-
const
|
|
882
|
-
|
|
1005
|
+
const cwd = data.cwd === "/" ? void 0 : data.cwd;
|
|
1006
|
+
logger.debug("Shell command executing...", {
|
|
1007
|
+
cwd,
|
|
883
1008
|
timeout: data.timeout || 3e4
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
};
|
|
895
|
-
logger.debug("Shell command result:", {
|
|
896
|
-
success: true,
|
|
897
|
-
exitCode: 0,
|
|
1009
|
+
});
|
|
1010
|
+
const result = await runShellCommand({
|
|
1011
|
+
command: data.command,
|
|
1012
|
+
cwd,
|
|
1013
|
+
timeoutMs: data.timeout || 3e4
|
|
1014
|
+
});
|
|
1015
|
+
logger.debug("Shell command finished:", {
|
|
1016
|
+
success: result.success,
|
|
1017
|
+
exitCode: result.exitCode,
|
|
1018
|
+
error: result.error,
|
|
898
1019
|
stdoutLen: result.stdout.length,
|
|
899
1020
|
stderrLen: result.stderr.length
|
|
900
1021
|
});
|
|
901
1022
|
return result;
|
|
902
1023
|
} catch (error) {
|
|
903
|
-
const execError = error;
|
|
904
|
-
if (execError.code === "ETIMEDOUT" || execError.killed) {
|
|
905
|
-
const result2 = {
|
|
906
|
-
success: false,
|
|
907
|
-
stdout: execError.stdout || "",
|
|
908
|
-
stderr: execError.stderr || "",
|
|
909
|
-
exitCode: typeof execError.code === "number" ? execError.code : -1,
|
|
910
|
-
error: "Command timed out"
|
|
911
|
-
};
|
|
912
|
-
logger.debug("Shell command timed out:", {
|
|
913
|
-
success: false,
|
|
914
|
-
exitCode: result2.exitCode,
|
|
915
|
-
error: "Command timed out"
|
|
916
|
-
});
|
|
917
|
-
return result2;
|
|
918
|
-
}
|
|
919
1024
|
const result = {
|
|
920
1025
|
success: false,
|
|
921
|
-
stdout:
|
|
922
|
-
stderr:
|
|
923
|
-
exitCode:
|
|
924
|
-
error:
|
|
1026
|
+
stdout: "",
|
|
1027
|
+
stderr: "",
|
|
1028
|
+
exitCode: 1,
|
|
1029
|
+
error: error instanceof Error ? error.message : "Command failed"
|
|
925
1030
|
};
|
|
926
|
-
logger.debug("Shell command failed:", {
|
|
1031
|
+
logger.debug("Shell command failed before execution completed:", {
|
|
1032
|
+
cwd: data.cwd === "/" ? void 0 : data.cwd,
|
|
927
1033
|
success: false,
|
|
928
1034
|
exitCode: result.exitCode,
|
|
929
|
-
error: result.error
|
|
930
|
-
stdoutLen: result.stdout.length,
|
|
931
|
-
stderrLen: result.stderr.length
|
|
1035
|
+
error: result.error
|
|
932
1036
|
});
|
|
933
1037
|
return result;
|
|
934
1038
|
}
|
|
@@ -1345,6 +1449,8 @@ function buildSocketAuth(opts) {
|
|
|
1345
1449
|
return auth;
|
|
1346
1450
|
}
|
|
1347
1451
|
|
|
1452
|
+
const MAX_PENDING_RELIABLE_CODEX_MESSAGES = 200;
|
|
1453
|
+
const MAX_PENDING_RELIABLE_CODEX_MESSAGE_BYTES = 512 * 1024;
|
|
1348
1454
|
class ApiSessionClient extends node_events.EventEmitter {
|
|
1349
1455
|
credentials;
|
|
1350
1456
|
sessionId;
|
|
@@ -1361,6 +1467,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1361
1467
|
encryptionKey;
|
|
1362
1468
|
encryptionVariant;
|
|
1363
1469
|
pendingReliableCodexMessages = [];
|
|
1470
|
+
pendingReliableCodexMessageBytes = 0;
|
|
1364
1471
|
reconnectAfterServerDisconnectTimer = null;
|
|
1365
1472
|
lastSocketServerError = null;
|
|
1366
1473
|
constructor(credentials, session) {
|
|
@@ -1567,6 +1674,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1567
1674
|
if (this.shouldBufferReliableCodexMessage(body)) {
|
|
1568
1675
|
logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
|
|
1569
1676
|
this.pendingReliableCodexMessages.push({ encrypted, type: eventType });
|
|
1677
|
+
this.pendingReliableCodexMessageBytes += encrypted.length;
|
|
1678
|
+
this.trimPendingReliableCodexMessages();
|
|
1570
1679
|
} else {
|
|
1571
1680
|
logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
|
|
1572
1681
|
}
|
|
@@ -1742,6 +1851,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1742
1851
|
return;
|
|
1743
1852
|
}
|
|
1744
1853
|
const buffered = this.pendingReliableCodexMessages.splice(0, this.pendingReliableCodexMessages.length);
|
|
1854
|
+
this.pendingReliableCodexMessageBytes = 0;
|
|
1745
1855
|
logger.debug("[API] Flushing buffered Codex messages after reconnect", {
|
|
1746
1856
|
count: buffered.length,
|
|
1747
1857
|
types: buffered.map((message) => message.type)
|
|
@@ -1765,6 +1875,24 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1765
1875
|
return false;
|
|
1766
1876
|
}
|
|
1767
1877
|
}
|
|
1878
|
+
trimPendingReliableCodexMessages() {
|
|
1879
|
+
let dropped = 0;
|
|
1880
|
+
while (this.pendingReliableCodexMessages.length > MAX_PENDING_RELIABLE_CODEX_MESSAGES || this.pendingReliableCodexMessageBytes > MAX_PENDING_RELIABLE_CODEX_MESSAGE_BYTES) {
|
|
1881
|
+
const removed = this.pendingReliableCodexMessages.shift();
|
|
1882
|
+
if (!removed) {
|
|
1883
|
+
break;
|
|
1884
|
+
}
|
|
1885
|
+
this.pendingReliableCodexMessageBytes -= removed.encrypted.length;
|
|
1886
|
+
dropped += 1;
|
|
1887
|
+
}
|
|
1888
|
+
if (dropped > 0) {
|
|
1889
|
+
logger.debug("[API] Dropped oldest buffered Codex messages to cap reconnect memory usage", {
|
|
1890
|
+
dropped,
|
|
1891
|
+
remaining: this.pendingReliableCodexMessages.length,
|
|
1892
|
+
bytes: this.pendingReliableCodexMessageBytes
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1768
1896
|
retryAfterServerDisconnect(reason) {
|
|
1769
1897
|
if (reason !== "io server disconnect") {
|
|
1770
1898
|
return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-J7QKJ8lc.cjs');
|
|
4
4
|
require('chalk');
|
|
5
|
-
require('./api-
|
|
5
|
+
require('./api-Ye-rPX6s.cjs');
|
|
6
6
|
require('axios');
|
|
7
7
|
require('fs');
|
|
8
8
|
require('node:fs');
|
|
@@ -13,13 +13,12 @@ require('socket.io-client');
|
|
|
13
13
|
require('zod');
|
|
14
14
|
require('node:crypto');
|
|
15
15
|
require('tweetnacl');
|
|
16
|
-
require('child_process');
|
|
17
|
-
require('util');
|
|
18
16
|
require('fs/promises');
|
|
19
17
|
require('crypto');
|
|
20
18
|
require('path');
|
|
19
|
+
require('node:child_process');
|
|
21
20
|
require('expo-server-sdk');
|
|
22
|
-
require('./persistence-
|
|
21
|
+
require('./persistence-xK5CKhbn.cjs');
|
|
23
22
|
require('node:fs/promises');
|
|
24
23
|
require('os');
|
|
25
24
|
require('tmp');
|
|
@@ -28,14 +27,15 @@ require('node:module');
|
|
|
28
27
|
require('open');
|
|
29
28
|
require('react');
|
|
30
29
|
require('ink');
|
|
30
|
+
require('child_process');
|
|
31
31
|
require('url');
|
|
32
|
-
require('node:child_process');
|
|
33
32
|
require('ps-list');
|
|
34
33
|
require('cross-spawn');
|
|
35
34
|
require('fastify');
|
|
36
35
|
require('fastify-type-provider-zod');
|
|
37
36
|
require('node:readline');
|
|
38
37
|
require('http');
|
|
38
|
+
require('util');
|
|
39
39
|
require('@agentclientprotocol/sdk');
|
|
40
40
|
|
|
41
41
|
function listRuntimeProviders() {
|