happy-imou-cloud 1.1.7 → 2.0.0
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/{setupOfflineReconnection-ndObLZk0.mjs → BaseReasoningProcessor-BKLRCKTU.mjs} +133 -90
- package/dist/{setupOfflineReconnection-obypStdD.cjs → BaseReasoningProcessor-BRCQXCZY.cjs} +134 -90
- package/dist/{types-BXyraW9R.mjs → api-BGXYX0yH.mjs} +198 -170
- package/dist/{types-BSTmyv9d.cjs → api-D7OK-mML.cjs} +219 -192
- package/dist/command-CnLtKtP-.mjs +51 -0
- package/dist/command-G85giEAF.cjs +54 -0
- package/dist/future-Dq4Ha1Dn.cjs +24 -0
- package/dist/future-xRdLl3vf.mjs +22 -0
- package/dist/{index-DVI4b0mv.cjs → index-B_wlQBy2.cjs} +5493 -7142
- package/dist/{index-CUmYqKWt.mjs → index-C7Y0R-MI.mjs} +5482 -7143
- package/dist/index.cjs +19 -21
- package/dist/index.mjs +19 -21
- package/dist/lib.cjs +3 -2
- package/dist/lib.d.cts +17 -0
- package/dist/lib.d.mts +17 -0
- package/dist/lib.mjs +2 -1
- package/dist/{persistence-BGsuPqaO.mjs → persistence-BA_unuca.mjs} +8 -4
- package/dist/{persistence-BRH9F6RS.cjs → persistence-DHgf1CTG.cjs} +10 -6
- package/dist/registerKillSessionHandler-C2-yHm1V.mjs +428 -0
- package/dist/registerKillSessionHandler-CLREXN11.cjs +433 -0
- package/dist/runClaude-CwAitpX-.cjs +3274 -0
- package/dist/runClaude-uNC5Eym4.mjs +3271 -0
- package/dist/runCodex-B-05E-YZ.mjs +1846 -0
- package/dist/runCodex-Cm0VTqw_.cjs +1848 -0
- package/dist/{runGemini-C3dDtGOV.cjs → runGemini-CLWjwDYS.cjs} +25 -1366
- package/dist/{runGemini-B-EK_BJQ.mjs → runGemini-_biXvQAH.mjs} +12 -1353
- package/dist/types-CiliQpqS.mjs +52 -0
- package/dist/types-DVk3crez.cjs +54 -0
- package/package.json +13 -12
- package/scripts/devtools/README.md +9 -0
- package/scripts/devtools/generate-mock-credentials.ts +94 -0
- package/scripts/release-smoke.mjs +62 -0
- package/dist/config-BQNrtwRY.cjs +0 -183
- package/dist/config-Dn99YH37.mjs +0 -173
- package/dist/runCodex-Cez8cuIh.cjs +0 -1143
- package/dist/runCodex-X0BfjcZH.mjs +0 -1140
|
@@ -5,7 +5,7 @@ var chalk = require('chalk');
|
|
|
5
5
|
var fs$1 = require('fs');
|
|
6
6
|
var fs = require('node:fs');
|
|
7
7
|
var os = require('node:os');
|
|
8
|
-
var
|
|
8
|
+
var path = require('node:path');
|
|
9
9
|
var node_events = require('node:events');
|
|
10
10
|
var socket_ioClient = require('socket.io-client');
|
|
11
11
|
var z = require('zod');
|
|
@@ -15,11 +15,11 @@ var child_process = require('child_process');
|
|
|
15
15
|
var util = require('util');
|
|
16
16
|
var fs$2 = require('fs/promises');
|
|
17
17
|
var crypto = require('crypto');
|
|
18
|
-
var path = require('path');
|
|
18
|
+
var path$1 = require('path');
|
|
19
19
|
var expoServerSdk = require('expo-server-sdk');
|
|
20
20
|
|
|
21
21
|
var name = "happy-imou-cloud";
|
|
22
|
-
var version = "
|
|
22
|
+
var version = "2.0.0";
|
|
23
23
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
24
24
|
var author = "long.zhu";
|
|
25
25
|
var license = "MIT";
|
|
@@ -64,22 +64,23 @@ var scripts = {
|
|
|
64
64
|
typecheck: "tsc --noEmit",
|
|
65
65
|
build: "shx rm -rf dist && npx tsc --noEmit && pkgroll",
|
|
66
66
|
"// ==== Testing ====": "",
|
|
67
|
-
test: "
|
|
68
|
-
"test:integration": "
|
|
67
|
+
test: "yarn build && vitest run",
|
|
68
|
+
"test:integration": "yarn build && vitest run --config vitest.integration.config.ts",
|
|
69
69
|
"test:integration:watch": "vitest run --config vitest.integration.config.ts --watch",
|
|
70
|
-
"test:coverage": "
|
|
70
|
+
"test:coverage": "yarn build && vitest run --coverage && node scripts/generate-coverage-report.js",
|
|
71
71
|
"// ==== Benchmarks ====": "",
|
|
72
|
-
bench: "
|
|
73
|
-
"bench:auth": "
|
|
72
|
+
bench: "yarn build && vitest bench src/daemon/integration/benchmarks/",
|
|
73
|
+
"bench:auth": "yarn build && vitest bench src/daemon/integration/benchmarks/auth.bench.ts",
|
|
74
74
|
"bench:report": "node scripts/generate-benchmark-report.js",
|
|
75
75
|
"// ==== Start & Dev ====": "",
|
|
76
|
-
start: "
|
|
76
|
+
start: "yarn build && node ./bin/happy-cloud.mjs",
|
|
77
77
|
dev: "tsx src/index.ts",
|
|
78
|
-
"dev:local-server": "
|
|
79
|
-
"dev:integration-test-env": "
|
|
78
|
+
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
79
|
+
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
80
80
|
"// ==== Release ====": "",
|
|
81
81
|
prepublishOnly: "npm run build",
|
|
82
|
-
release: "
|
|
82
|
+
release: "yarn install && release-it",
|
|
83
|
+
"release:smoke": "node ./scripts/release-smoke.mjs",
|
|
83
84
|
"// ==== Dev/Stable Variant Management ====": "",
|
|
84
85
|
stable: "node scripts/env-wrapper.cjs stable",
|
|
85
86
|
"dev:variant": "node scripts/env-wrapper.cjs dev",
|
|
@@ -217,7 +218,7 @@ class Configuration {
|
|
|
217
218
|
const expandedPath = process.env.HAPPY_CLOUD_HOME_DIR.replace(/^~/, os.homedir());
|
|
218
219
|
this.happyCloudHomeDir = expandedPath;
|
|
219
220
|
} else {
|
|
220
|
-
this.happyCloudHomeDir =
|
|
221
|
+
this.happyCloudHomeDir = path.join(os.homedir(), ".happy-cloud");
|
|
221
222
|
}
|
|
222
223
|
this.happyHomeDir = this.happyCloudHomeDir;
|
|
223
224
|
} else {
|
|
@@ -225,15 +226,15 @@ class Configuration {
|
|
|
225
226
|
const expandedPath = process.env.HAPPY_HOME_DIR.replace(/^~/, os.homedir());
|
|
226
227
|
this.happyHomeDir = expandedPath;
|
|
227
228
|
} else {
|
|
228
|
-
this.happyHomeDir =
|
|
229
|
+
this.happyHomeDir = path.join(os.homedir(), ".happy");
|
|
229
230
|
}
|
|
230
231
|
this.happyCloudHomeDir = this.happyHomeDir;
|
|
231
232
|
}
|
|
232
|
-
this.logsDir =
|
|
233
|
-
this.settingsFile =
|
|
234
|
-
this.privateKeyFile =
|
|
235
|
-
this.daemonStateFile =
|
|
236
|
-
this.daemonLockFile =
|
|
233
|
+
this.logsDir = path.join(this.isHappyCloudMode ? this.happyCloudHomeDir : this.happyHomeDir, "logs");
|
|
234
|
+
this.settingsFile = path.join(this.isHappyCloudMode ? this.happyCloudHomeDir : this.happyHomeDir, "settings.json");
|
|
235
|
+
this.privateKeyFile = path.join(this.isHappyCloudMode ? this.happyCloudHomeDir : this.happyHomeDir, "access.key");
|
|
236
|
+
this.daemonStateFile = path.join(this.isHappyCloudMode ? this.happyCloudHomeDir : this.happyHomeDir, "daemon.state.json");
|
|
237
|
+
this.daemonLockFile = path.join(this.isHappyCloudMode ? this.happyCloudHomeDir : this.happyHomeDir, "daemon.state.json.lock");
|
|
237
238
|
this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
238
239
|
this.disableCaffeinate = ["true", "1", "yes"].includes(process.env.HAPPY_DISABLE_CAFFEINATE?.toLowerCase() || "");
|
|
239
240
|
this.currentCliVersion = packageJson.version;
|
|
@@ -285,7 +286,7 @@ function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
|
|
|
285
286
|
function getSessionLogPath() {
|
|
286
287
|
const timestamp = createTimestampForFilename();
|
|
287
288
|
const filename = configuration.isDaemonProcess ? `${timestamp}-daemon.log` : `${timestamp}.log`;
|
|
288
|
-
return
|
|
289
|
+
return path.join(configuration.logsDir, filename);
|
|
289
290
|
}
|
|
290
291
|
class Logger {
|
|
291
292
|
constructor(logFilePath = getSessionLogPath()) {
|
|
@@ -427,12 +428,12 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
427
428
|
return [];
|
|
428
429
|
}
|
|
429
430
|
const logs = fs.readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
|
|
430
|
-
const fullPath =
|
|
431
|
+
const fullPath = path.join(logsDir, file);
|
|
431
432
|
const stats = fs.statSync(fullPath);
|
|
432
433
|
return { file, path: fullPath, modified: stats.mtime };
|
|
433
434
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
434
435
|
try {
|
|
435
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-
|
|
436
|
+
const { readDaemonState } = await Promise.resolve().then(function () { return require('./persistence-DHgf1CTG.cjs'); });
|
|
436
437
|
const state = await readDaemonState();
|
|
437
438
|
if (!state) {
|
|
438
439
|
return logs;
|
|
@@ -440,7 +441,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
440
441
|
if (state.daemonLogPath && fs.existsSync(state.daemonLogPath)) {
|
|
441
442
|
const stats = fs.statSync(state.daemonLogPath);
|
|
442
443
|
const persisted = {
|
|
443
|
-
file:
|
|
444
|
+
file: path.basename(state.daemonLogPath),
|
|
444
445
|
path: state.daemonLogPath,
|
|
445
446
|
modified: stats.mtime
|
|
446
447
|
};
|
|
@@ -464,6 +465,108 @@ async function getLatestDaemonLog() {
|
|
|
464
465
|
return latest || null;
|
|
465
466
|
}
|
|
466
467
|
|
|
468
|
+
function encodeBase64(buffer, variant = "base64") {
|
|
469
|
+
if (variant === "base64url") {
|
|
470
|
+
return encodeBase64Url(buffer);
|
|
471
|
+
}
|
|
472
|
+
return Buffer.from(buffer).toString("base64");
|
|
473
|
+
}
|
|
474
|
+
function encodeBase64Url(buffer) {
|
|
475
|
+
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
476
|
+
}
|
|
477
|
+
function decodeBase64(base64, variant = "base64") {
|
|
478
|
+
if (variant === "base64url") {
|
|
479
|
+
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
480
|
+
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
481
|
+
}
|
|
482
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
483
|
+
}
|
|
484
|
+
function getRandomBytes(size) {
|
|
485
|
+
return new Uint8Array(node_crypto.randomBytes(size));
|
|
486
|
+
}
|
|
487
|
+
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
488
|
+
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
489
|
+
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
490
|
+
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
491
|
+
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
492
|
+
result.set(ephemeralKeyPair.publicKey, 0);
|
|
493
|
+
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
494
|
+
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
function encryptLegacy(data, secret) {
|
|
498
|
+
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
499
|
+
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
500
|
+
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
501
|
+
result.set(nonce);
|
|
502
|
+
result.set(encrypted, nonce.length);
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
505
|
+
function decryptLegacy(data, secret) {
|
|
506
|
+
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
507
|
+
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
508
|
+
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
509
|
+
if (!decrypted) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
513
|
+
}
|
|
514
|
+
function encryptWithDataKey(data, dataKey) {
|
|
515
|
+
const nonce = getRandomBytes(12);
|
|
516
|
+
const cipher = node_crypto.createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
517
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
518
|
+
const encrypted = Buffer.concat([
|
|
519
|
+
cipher.update(plaintext),
|
|
520
|
+
cipher.final()
|
|
521
|
+
]);
|
|
522
|
+
const authTag = cipher.getAuthTag();
|
|
523
|
+
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
524
|
+
bundle.set([0], 0);
|
|
525
|
+
bundle.set(nonce, 1);
|
|
526
|
+
bundle.set(new Uint8Array(encrypted), 13);
|
|
527
|
+
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
528
|
+
return bundle;
|
|
529
|
+
}
|
|
530
|
+
function decryptWithDataKey(bundle, dataKey) {
|
|
531
|
+
if (bundle.length < 1) {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
if (bundle[0] !== 0) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
if (bundle.length < 12 + 16 + 1) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
const nonce = bundle.slice(1, 13);
|
|
541
|
+
const authTag = bundle.slice(bundle.length - 16);
|
|
542
|
+
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
543
|
+
try {
|
|
544
|
+
const decipher = node_crypto.createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
545
|
+
decipher.setAuthTag(authTag);
|
|
546
|
+
const decrypted = Buffer.concat([
|
|
547
|
+
decipher.update(ciphertext),
|
|
548
|
+
decipher.final()
|
|
549
|
+
]);
|
|
550
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
551
|
+
} catch (error) {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function encrypt(key, variant, data) {
|
|
556
|
+
if (variant === "legacy") {
|
|
557
|
+
return encryptLegacy(data, key);
|
|
558
|
+
} else {
|
|
559
|
+
return encryptWithDataKey(data, key);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function decrypt(key, variant, data) {
|
|
563
|
+
if (variant === "legacy") {
|
|
564
|
+
return decryptLegacy(data, key);
|
|
565
|
+
} else {
|
|
566
|
+
return decryptWithDataKey(data, key);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
467
570
|
const SessionMessageContentSchema = z.z.object({
|
|
468
571
|
c: z.z.string(),
|
|
469
572
|
// Base64 encoded encrypted content
|
|
@@ -595,108 +698,6 @@ const AgentMessageSchema = z.z.object({
|
|
|
595
698
|
});
|
|
596
699
|
z.z.union([UserMessageSchema, AgentMessageSchema]);
|
|
597
700
|
|
|
598
|
-
function encodeBase64(buffer, variant = "base64") {
|
|
599
|
-
if (variant === "base64url") {
|
|
600
|
-
return encodeBase64Url(buffer);
|
|
601
|
-
}
|
|
602
|
-
return Buffer.from(buffer).toString("base64");
|
|
603
|
-
}
|
|
604
|
-
function encodeBase64Url(buffer) {
|
|
605
|
-
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
606
|
-
}
|
|
607
|
-
function decodeBase64(base64, variant = "base64") {
|
|
608
|
-
if (variant === "base64url") {
|
|
609
|
-
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
610
|
-
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
611
|
-
}
|
|
612
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
613
|
-
}
|
|
614
|
-
function getRandomBytes(size) {
|
|
615
|
-
return new Uint8Array(node_crypto.randomBytes(size));
|
|
616
|
-
}
|
|
617
|
-
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
618
|
-
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
619
|
-
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
620
|
-
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
621
|
-
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
622
|
-
result.set(ephemeralKeyPair.publicKey, 0);
|
|
623
|
-
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
624
|
-
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
625
|
-
return result;
|
|
626
|
-
}
|
|
627
|
-
function encryptLegacy(data, secret) {
|
|
628
|
-
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
629
|
-
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
630
|
-
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
631
|
-
result.set(nonce);
|
|
632
|
-
result.set(encrypted, nonce.length);
|
|
633
|
-
return result;
|
|
634
|
-
}
|
|
635
|
-
function decryptLegacy(data, secret) {
|
|
636
|
-
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
637
|
-
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
638
|
-
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
639
|
-
if (!decrypted) {
|
|
640
|
-
return null;
|
|
641
|
-
}
|
|
642
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
643
|
-
}
|
|
644
|
-
function encryptWithDataKey(data, dataKey) {
|
|
645
|
-
const nonce = getRandomBytes(12);
|
|
646
|
-
const cipher = node_crypto.createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
647
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
648
|
-
const encrypted = Buffer.concat([
|
|
649
|
-
cipher.update(plaintext),
|
|
650
|
-
cipher.final()
|
|
651
|
-
]);
|
|
652
|
-
const authTag = cipher.getAuthTag();
|
|
653
|
-
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
654
|
-
bundle.set([0], 0);
|
|
655
|
-
bundle.set(nonce, 1);
|
|
656
|
-
bundle.set(new Uint8Array(encrypted), 13);
|
|
657
|
-
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
658
|
-
return bundle;
|
|
659
|
-
}
|
|
660
|
-
function decryptWithDataKey(bundle, dataKey) {
|
|
661
|
-
if (bundle.length < 1) {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
if (bundle[0] !== 0) {
|
|
665
|
-
return null;
|
|
666
|
-
}
|
|
667
|
-
if (bundle.length < 12 + 16 + 1) {
|
|
668
|
-
return null;
|
|
669
|
-
}
|
|
670
|
-
const nonce = bundle.slice(1, 13);
|
|
671
|
-
const authTag = bundle.slice(bundle.length - 16);
|
|
672
|
-
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
673
|
-
try {
|
|
674
|
-
const decipher = node_crypto.createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
675
|
-
decipher.setAuthTag(authTag);
|
|
676
|
-
const decrypted = Buffer.concat([
|
|
677
|
-
decipher.update(ciphertext),
|
|
678
|
-
decipher.final()
|
|
679
|
-
]);
|
|
680
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
681
|
-
} catch (error) {
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
function encrypt(key, variant, data) {
|
|
686
|
-
if (variant === "legacy") {
|
|
687
|
-
return encryptLegacy(data, key);
|
|
688
|
-
} else {
|
|
689
|
-
return encryptWithDataKey(data, key);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
function decrypt(key, variant, data) {
|
|
693
|
-
if (variant === "legacy") {
|
|
694
|
-
return decryptLegacy(data, key);
|
|
695
|
-
} else {
|
|
696
|
-
return decryptWithDataKey(data, key);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
701
|
async function delay(ms) {
|
|
701
702
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
702
703
|
}
|
|
@@ -853,8 +854,8 @@ class RpcHandlerManager {
|
|
|
853
854
|
}
|
|
854
855
|
|
|
855
856
|
function validatePath(targetPath, workingDirectory) {
|
|
856
|
-
const resolvedTarget = path.resolve(workingDirectory, targetPath);
|
|
857
|
-
const resolvedWorkingDir = path.resolve(workingDirectory);
|
|
857
|
+
const resolvedTarget = path$1.resolve(workingDirectory, targetPath);
|
|
858
|
+
const resolvedWorkingDir = path$1.resolve(workingDirectory);
|
|
858
859
|
const targetNormalized = resolvedTarget.replace(/\\/g, "/");
|
|
859
860
|
const workingDirNormalized = resolvedWorkingDir.replace(/\\/g, "/");
|
|
860
861
|
process.platform === "win32" ? "\\" : "/";
|
|
@@ -1008,7 +1009,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
1008
1009
|
const entries = await fs$2.readdir(data.path, { withFileTypes: true });
|
|
1009
1010
|
const directoryEntries = await Promise.all(
|
|
1010
1011
|
entries.map(async (entry) => {
|
|
1011
|
-
const fullPath = path.join(data.path, entry.name);
|
|
1012
|
+
const fullPath = path$1.join(data.path, entry.name);
|
|
1012
1013
|
let type = "other";
|
|
1013
1014
|
let size;
|
|
1014
1015
|
let modified;
|
|
@@ -1049,26 +1050,26 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
1049
1050
|
if (!validation.valid) {
|
|
1050
1051
|
return { success: false, error: validation.error };
|
|
1051
1052
|
}
|
|
1052
|
-
async function buildTree(path
|
|
1053
|
+
async function buildTree(path, name, currentDepth) {
|
|
1053
1054
|
try {
|
|
1054
|
-
const stats = await fs$2.stat(path
|
|
1055
|
+
const stats = await fs$2.stat(path);
|
|
1055
1056
|
const node = {
|
|
1056
1057
|
name,
|
|
1057
|
-
path
|
|
1058
|
+
path,
|
|
1058
1059
|
type: stats.isDirectory() ? "directory" : "file",
|
|
1059
1060
|
size: stats.size,
|
|
1060
1061
|
modified: stats.mtime.getTime()
|
|
1061
1062
|
};
|
|
1062
1063
|
if (stats.isDirectory() && currentDepth < data.maxDepth) {
|
|
1063
|
-
const entries = await fs$2.readdir(path
|
|
1064
|
+
const entries = await fs$2.readdir(path, { withFileTypes: true });
|
|
1064
1065
|
const children = [];
|
|
1065
1066
|
await Promise.all(
|
|
1066
1067
|
entries.map(async (entry) => {
|
|
1067
1068
|
if (entry.isSymbolicLink()) {
|
|
1068
|
-
logger.debug(`Skipping symlink: ${path.join(path
|
|
1069
|
+
logger.debug(`Skipping symlink: ${path$1.join(path, entry.name)}`);
|
|
1069
1070
|
return;
|
|
1070
1071
|
}
|
|
1071
|
-
const childPath = path.join(path
|
|
1072
|
+
const childPath = path$1.join(path, entry.name);
|
|
1072
1073
|
const childNode = await buildTree(childPath, entry.name, currentDepth + 1);
|
|
1073
1074
|
if (childNode) {
|
|
1074
1075
|
children.push(childNode);
|
|
@@ -1084,7 +1085,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
1084
1085
|
}
|
|
1085
1086
|
return node;
|
|
1086
1087
|
} catch (error) {
|
|
1087
|
-
logger.debug(`Failed to process ${path
|
|
1088
|
+
logger.debug(`Failed to process ${path}:`, error instanceof Error ? error.message : String(error));
|
|
1088
1089
|
return null;
|
|
1089
1090
|
}
|
|
1090
1091
|
}
|
|
@@ -1360,6 +1361,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1360
1361
|
metadataLock = new AsyncLock();
|
|
1361
1362
|
encryptionKey;
|
|
1362
1363
|
encryptionVariant;
|
|
1364
|
+
pendingReliableCodexMessages = [];
|
|
1363
1365
|
constructor(credentials, session) {
|
|
1364
1366
|
super();
|
|
1365
1367
|
this.credentials = credentials;
|
|
@@ -1395,6 +1397,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1395
1397
|
this.socket.on("connect", () => {
|
|
1396
1398
|
logger.debug("Socket connected successfully");
|
|
1397
1399
|
this.rpcHandlerManager.onSocketConnect(this.socket);
|
|
1400
|
+
this.flushReliableCodexMessages();
|
|
1398
1401
|
});
|
|
1399
1402
|
this.socket.on("rpc-request", async (data, callback) => {
|
|
1400
1403
|
callback(await this.rpcHandlerManager.handleRequest(data));
|
|
@@ -1431,10 +1434,12 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1431
1434
|
if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
|
|
1432
1435
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.metadata.value));
|
|
1433
1436
|
this.metadataVersion = data.body.metadata.version;
|
|
1437
|
+
this.emit("metadata-updated", this.metadata);
|
|
1434
1438
|
}
|
|
1435
1439
|
if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
|
|
1436
1440
|
this.agentState = data.body.agentState.value ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.agentState.value)) : null;
|
|
1437
1441
|
this.agentStateVersion = data.body.agentState.version;
|
|
1442
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1438
1443
|
}
|
|
1439
1444
|
} else if (data.body.t === "update-machine") {
|
|
1440
1445
|
logger.debug(`[SOCKET] WARNING: Session client received unexpected machine update - ignoring`);
|
|
@@ -1456,6 +1461,33 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1456
1461
|
callback(this.pendingMessages.shift());
|
|
1457
1462
|
}
|
|
1458
1463
|
}
|
|
1464
|
+
getMetadataSnapshot() {
|
|
1465
|
+
return this.metadata;
|
|
1466
|
+
}
|
|
1467
|
+
getAgentStateSnapshot() {
|
|
1468
|
+
return this.agentState;
|
|
1469
|
+
}
|
|
1470
|
+
async waitForMetadataUpdate(signal) {
|
|
1471
|
+
if (signal?.aborted) {
|
|
1472
|
+
throw createAbortError();
|
|
1473
|
+
}
|
|
1474
|
+
return await new Promise((resolve, reject) => {
|
|
1475
|
+
const onAbort = () => {
|
|
1476
|
+
cleanup();
|
|
1477
|
+
reject(createAbortError());
|
|
1478
|
+
};
|
|
1479
|
+
const onMetadataUpdate = (metadata) => {
|
|
1480
|
+
cleanup();
|
|
1481
|
+
resolve(metadata);
|
|
1482
|
+
};
|
|
1483
|
+
const cleanup = () => {
|
|
1484
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1485
|
+
this.off("metadata-updated", onMetadataUpdate);
|
|
1486
|
+
};
|
|
1487
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1488
|
+
this.on("metadata-updated", onMetadataUpdate);
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1459
1491
|
/**
|
|
1460
1492
|
* Send message to session
|
|
1461
1493
|
* @param body - Message body (can be MessageContent or raw content for agent messages)
|
|
@@ -1514,25 +1546,28 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1514
1546
|
}
|
|
1515
1547
|
}
|
|
1516
1548
|
sendCodexMessage(body) {
|
|
1517
|
-
|
|
1549
|
+
const content = {
|
|
1518
1550
|
role: "agent",
|
|
1519
1551
|
content: {
|
|
1520
1552
|
type: "codex",
|
|
1521
1553
|
data: body
|
|
1522
|
-
// This wraps the entire Claude message
|
|
1523
1554
|
},
|
|
1524
1555
|
meta: {
|
|
1525
1556
|
sentFrom: "cli"
|
|
1526
1557
|
}
|
|
1527
1558
|
};
|
|
1528
1559
|
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
1560
|
+
const eventType = typeof body?.type === "string" ? body.type : "unknown";
|
|
1529
1561
|
if (!this.socket.connected) {
|
|
1530
|
-
|
|
1562
|
+
if (this.shouldBufferReliableCodexMessage(body)) {
|
|
1563
|
+
logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
|
|
1564
|
+
this.pendingReliableCodexMessages.push({ encrypted, type: eventType });
|
|
1565
|
+
} else {
|
|
1566
|
+
logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
|
|
1567
|
+
}
|
|
1568
|
+
return;
|
|
1531
1569
|
}
|
|
1532
|
-
this.
|
|
1533
|
-
sid: this.sessionId,
|
|
1534
|
-
message: encrypted
|
|
1535
|
-
});
|
|
1570
|
+
this.emitEncryptedSessionMessage(encrypted);
|
|
1536
1571
|
}
|
|
1537
1572
|
/**
|
|
1538
1573
|
* Send a generic agent message to the session using ACP (Agent Communication Protocol) format.
|
|
@@ -1632,10 +1667,12 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1632
1667
|
if (answer.result === "success") {
|
|
1633
1668
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
|
|
1634
1669
|
this.metadataVersion = answer.version;
|
|
1670
|
+
this.emit("metadata-updated", this.metadata);
|
|
1635
1671
|
} else if (answer.result === "version-mismatch") {
|
|
1636
1672
|
if (answer.version > this.metadataVersion) {
|
|
1637
1673
|
this.metadataVersion = answer.version;
|
|
1638
1674
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
|
|
1675
|
+
this.emit("metadata-updated", this.metadata);
|
|
1639
1676
|
}
|
|
1640
1677
|
throw new Error("Metadata version mismatch");
|
|
1641
1678
|
} else if (answer.result === "error") ;
|
|
@@ -1655,11 +1692,13 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1655
1692
|
if (answer.result === "success") {
|
|
1656
1693
|
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
1657
1694
|
this.agentStateVersion = answer.version;
|
|
1695
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1658
1696
|
logger.debug("Agent state updated", this.agentState);
|
|
1659
1697
|
} else if (answer.result === "version-mismatch") {
|
|
1660
1698
|
if (answer.version > this.agentStateVersion) {
|
|
1661
1699
|
this.agentStateVersion = answer.version;
|
|
1662
1700
|
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
1701
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1663
1702
|
}
|
|
1664
1703
|
throw new Error("Agent state version mismatch");
|
|
1665
1704
|
} else if (answer.result === "error") ;
|
|
@@ -1686,6 +1725,44 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
1686
1725
|
logger.debug("[API] socket.close() called");
|
|
1687
1726
|
this.socket.close();
|
|
1688
1727
|
}
|
|
1728
|
+
emitEncryptedSessionMessage(encrypted) {
|
|
1729
|
+
this.socket.emit("message", {
|
|
1730
|
+
sid: this.sessionId,
|
|
1731
|
+
message: encrypted
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
flushReliableCodexMessages() {
|
|
1735
|
+
if (!this.socket.connected || this.pendingReliableCodexMessages.length === 0) {
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
const buffered = this.pendingReliableCodexMessages.splice(0, this.pendingReliableCodexMessages.length);
|
|
1739
|
+
logger.debug("[API] Flushing buffered Codex messages after reconnect", {
|
|
1740
|
+
count: buffered.length,
|
|
1741
|
+
types: buffered.map((message) => message.type)
|
|
1742
|
+
});
|
|
1743
|
+
for (const message of buffered) {
|
|
1744
|
+
this.emitEncryptedSessionMessage(message.encrypted);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
shouldBufferReliableCodexMessage(body) {
|
|
1748
|
+
switch (body?.type) {
|
|
1749
|
+
case "message":
|
|
1750
|
+
case "tool-call":
|
|
1751
|
+
case "tool-call-result":
|
|
1752
|
+
case "permission-request":
|
|
1753
|
+
case "task_started":
|
|
1754
|
+
case "task_complete":
|
|
1755
|
+
case "turn_aborted":
|
|
1756
|
+
return true;
|
|
1757
|
+
default:
|
|
1758
|
+
return false;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
function createAbortError() {
|
|
1763
|
+
const error = new Error("The operation was aborted");
|
|
1764
|
+
error.name = "AbortError";
|
|
1765
|
+
return error;
|
|
1689
1766
|
}
|
|
1690
1767
|
|
|
1691
1768
|
class ApiMachineClient {
|
|
@@ -2462,60 +2539,10 @@ var api = /*#__PURE__*/Object.freeze({
|
|
|
2462
2539
|
ApiClient: ApiClient
|
|
2463
2540
|
});
|
|
2464
2541
|
|
|
2465
|
-
const UsageSchema = z.z.object({
|
|
2466
|
-
input_tokens: z.z.number().int().nonnegative(),
|
|
2467
|
-
cache_creation_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
2468
|
-
cache_read_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
2469
|
-
output_tokens: z.z.number().int().nonnegative(),
|
|
2470
|
-
service_tier: z.z.string().optional()
|
|
2471
|
-
}).passthrough();
|
|
2472
|
-
const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
2473
|
-
// User message - validates uuid and message.content
|
|
2474
|
-
z.z.object({
|
|
2475
|
-
type: z.z.literal("user"),
|
|
2476
|
-
isSidechain: z.z.boolean().optional(),
|
|
2477
|
-
isMeta: z.z.boolean().optional(),
|
|
2478
|
-
uuid: z.z.string(),
|
|
2479
|
-
// Used in getMessageKey()
|
|
2480
|
-
message: z.z.object({
|
|
2481
|
-
content: z.z.union([z.z.string(), z.z.any()])
|
|
2482
|
-
// Used in sessionScanner.ts
|
|
2483
|
-
}).passthrough()
|
|
2484
|
-
}).passthrough(),
|
|
2485
|
-
// Assistant message - only validates uuid and type
|
|
2486
|
-
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
|
2487
|
-
// which may have different structure than normal assistant messages
|
|
2488
|
-
z.z.object({
|
|
2489
|
-
uuid: z.z.string(),
|
|
2490
|
-
type: z.z.literal("assistant"),
|
|
2491
|
-
message: z.z.object({
|
|
2492
|
-
usage: UsageSchema.optional(),
|
|
2493
|
-
// Used in apiSession.ts
|
|
2494
|
-
model: z.z.string().optional()
|
|
2495
|
-
// Used for cost calculation
|
|
2496
|
-
}).passthrough().optional()
|
|
2497
|
-
}).passthrough(),
|
|
2498
|
-
// Summary message - validates summary and leafUuid
|
|
2499
|
-
z.z.object({
|
|
2500
|
-
type: z.z.literal("summary"),
|
|
2501
|
-
summary: z.z.string(),
|
|
2502
|
-
// Used in apiSession.ts
|
|
2503
|
-
leafUuid: z.z.string()
|
|
2504
|
-
// Used in getMessageKey()
|
|
2505
|
-
}).passthrough(),
|
|
2506
|
-
// System message - validates uuid
|
|
2507
|
-
z.z.object({
|
|
2508
|
-
type: z.z.literal("system"),
|
|
2509
|
-
uuid: z.z.string()
|
|
2510
|
-
// Used in getMessageKey()
|
|
2511
|
-
}).passthrough()
|
|
2512
|
-
]);
|
|
2513
|
-
|
|
2514
2542
|
exports.ApiClient = ApiClient;
|
|
2515
2543
|
exports.ApiSessionClient = ApiSessionClient;
|
|
2516
2544
|
exports.AsyncLock = AsyncLock;
|
|
2517
2545
|
exports.HAPPY_CLOUD_DAEMON_PORT = HAPPY_CLOUD_DAEMON_PORT;
|
|
2518
|
-
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
2519
2546
|
exports.api = api;
|
|
2520
2547
|
exports.backoff = backoff;
|
|
2521
2548
|
exports.buildAuthenticatedHeaders = buildAuthenticatedHeaders;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { c as createDefaultRuntimeShell } from './index-C7Y0R-MI.mjs';
|
|
2
|
+
import 'chalk';
|
|
3
|
+
import './api-BGXYX0yH.mjs';
|
|
4
|
+
import 'axios';
|
|
5
|
+
import 'fs';
|
|
6
|
+
import 'node:fs';
|
|
7
|
+
import 'node:os';
|
|
8
|
+
import 'node:path';
|
|
9
|
+
import 'node:events';
|
|
10
|
+
import 'socket.io-client';
|
|
11
|
+
import 'zod';
|
|
12
|
+
import 'node:crypto';
|
|
13
|
+
import 'tweetnacl';
|
|
14
|
+
import 'child_process';
|
|
15
|
+
import 'util';
|
|
16
|
+
import 'fs/promises';
|
|
17
|
+
import 'crypto';
|
|
18
|
+
import 'path';
|
|
19
|
+
import 'expo-server-sdk';
|
|
20
|
+
import './persistence-BA_unuca.mjs';
|
|
21
|
+
import 'node:fs/promises';
|
|
22
|
+
import 'os';
|
|
23
|
+
import 'tmp';
|
|
24
|
+
import 'qrcode-terminal';
|
|
25
|
+
import 'node:module';
|
|
26
|
+
import 'open';
|
|
27
|
+
import 'react';
|
|
28
|
+
import 'ink';
|
|
29
|
+
import 'url';
|
|
30
|
+
import 'node:child_process';
|
|
31
|
+
import 'ps-list';
|
|
32
|
+
import 'cross-spawn';
|
|
33
|
+
import 'fastify';
|
|
34
|
+
import 'fastify-type-provider-zod';
|
|
35
|
+
import 'node:readline';
|
|
36
|
+
import 'http';
|
|
37
|
+
import '@agentclientprotocol/sdk';
|
|
38
|
+
|
|
39
|
+
function listRuntimeProviders() {
|
|
40
|
+
const shell = createDefaultRuntimeShell();
|
|
41
|
+
const providers = ["claude", "codex", "gemini", "cursor"];
|
|
42
|
+
return providers.map((provider) => ({
|
|
43
|
+
provider,
|
|
44
|
+
supported: shell.supports(provider)
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
function renderRuntimeProviders() {
|
|
48
|
+
return listRuntimeProviders().map(({ provider, supported }) => `${provider}: ${supported ? "registered" : "missing"}`).join("\n");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { listRuntimeProviders, renderRuntimeProviders };
|