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
|
@@ -17,7 +17,7 @@ import { resolve, join as join$1 } from 'path';
|
|
|
17
17
|
import { Expo } from 'expo-server-sdk';
|
|
18
18
|
|
|
19
19
|
var name = "happy-imou-cloud";
|
|
20
|
-
var version = "
|
|
20
|
+
var version = "2.0.0";
|
|
21
21
|
var description = "hicloud - Imou 企业定制版。关键是 happy!移动端远程 AI 编程工具,支持 Claude Code、Codex 和 Gemini CLI";
|
|
22
22
|
var author = "long.zhu";
|
|
23
23
|
var license = "MIT";
|
|
@@ -62,22 +62,23 @@ var scripts = {
|
|
|
62
62
|
typecheck: "tsc --noEmit",
|
|
63
63
|
build: "shx rm -rf dist && npx tsc --noEmit && pkgroll",
|
|
64
64
|
"// ==== Testing ====": "",
|
|
65
|
-
test: "
|
|
66
|
-
"test:integration": "
|
|
65
|
+
test: "yarn build && vitest run",
|
|
66
|
+
"test:integration": "yarn build && vitest run --config vitest.integration.config.ts",
|
|
67
67
|
"test:integration:watch": "vitest run --config vitest.integration.config.ts --watch",
|
|
68
|
-
"test:coverage": "
|
|
68
|
+
"test:coverage": "yarn build && vitest run --coverage && node scripts/generate-coverage-report.js",
|
|
69
69
|
"// ==== Benchmarks ====": "",
|
|
70
|
-
bench: "
|
|
71
|
-
"bench:auth": "
|
|
70
|
+
bench: "yarn build && vitest bench src/daemon/integration/benchmarks/",
|
|
71
|
+
"bench:auth": "yarn build && vitest bench src/daemon/integration/benchmarks/auth.bench.ts",
|
|
72
72
|
"bench:report": "node scripts/generate-benchmark-report.js",
|
|
73
73
|
"// ==== Start & Dev ====": "",
|
|
74
|
-
start: "
|
|
74
|
+
start: "yarn build && node ./bin/happy-cloud.mjs",
|
|
75
75
|
dev: "tsx src/index.ts",
|
|
76
|
-
"dev:local-server": "
|
|
77
|
-
"dev:integration-test-env": "
|
|
76
|
+
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
77
|
+
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
78
78
|
"// ==== Release ====": "",
|
|
79
79
|
prepublishOnly: "npm run build",
|
|
80
|
-
release: "
|
|
80
|
+
release: "yarn install && release-it",
|
|
81
|
+
"release:smoke": "node ./scripts/release-smoke.mjs",
|
|
81
82
|
"// ==== Dev/Stable Variant Management ====": "",
|
|
82
83
|
stable: "node scripts/env-wrapper.cjs stable",
|
|
83
84
|
"dev:variant": "node scripts/env-wrapper.cjs dev",
|
|
@@ -430,7 +431,7 @@ async function listDaemonLogFiles(limit = 50) {
|
|
|
430
431
|
return { file, path: fullPath, modified: stats.mtime };
|
|
431
432
|
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
432
433
|
try {
|
|
433
|
-
const { readDaemonState } = await import('./persistence-
|
|
434
|
+
const { readDaemonState } = await import('./persistence-BA_unuca.mjs');
|
|
434
435
|
const state = await readDaemonState();
|
|
435
436
|
if (!state) {
|
|
436
437
|
return logs;
|
|
@@ -462,6 +463,108 @@ async function getLatestDaemonLog() {
|
|
|
462
463
|
return latest || null;
|
|
463
464
|
}
|
|
464
465
|
|
|
466
|
+
function encodeBase64(buffer, variant = "base64") {
|
|
467
|
+
if (variant === "base64url") {
|
|
468
|
+
return encodeBase64Url(buffer);
|
|
469
|
+
}
|
|
470
|
+
return Buffer.from(buffer).toString("base64");
|
|
471
|
+
}
|
|
472
|
+
function encodeBase64Url(buffer) {
|
|
473
|
+
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
474
|
+
}
|
|
475
|
+
function decodeBase64(base64, variant = "base64") {
|
|
476
|
+
if (variant === "base64url") {
|
|
477
|
+
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
478
|
+
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
479
|
+
}
|
|
480
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
481
|
+
}
|
|
482
|
+
function getRandomBytes(size) {
|
|
483
|
+
return new Uint8Array(randomBytes(size));
|
|
484
|
+
}
|
|
485
|
+
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
486
|
+
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
487
|
+
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
488
|
+
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
489
|
+
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
490
|
+
result.set(ephemeralKeyPair.publicKey, 0);
|
|
491
|
+
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
492
|
+
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
function encryptLegacy(data, secret) {
|
|
496
|
+
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
497
|
+
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
498
|
+
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
499
|
+
result.set(nonce);
|
|
500
|
+
result.set(encrypted, nonce.length);
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
function decryptLegacy(data, secret) {
|
|
504
|
+
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
505
|
+
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
506
|
+
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
507
|
+
if (!decrypted) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
511
|
+
}
|
|
512
|
+
function encryptWithDataKey(data, dataKey) {
|
|
513
|
+
const nonce = getRandomBytes(12);
|
|
514
|
+
const cipher = createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
515
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
516
|
+
const encrypted = Buffer.concat([
|
|
517
|
+
cipher.update(plaintext),
|
|
518
|
+
cipher.final()
|
|
519
|
+
]);
|
|
520
|
+
const authTag = cipher.getAuthTag();
|
|
521
|
+
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
522
|
+
bundle.set([0], 0);
|
|
523
|
+
bundle.set(nonce, 1);
|
|
524
|
+
bundle.set(new Uint8Array(encrypted), 13);
|
|
525
|
+
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
526
|
+
return bundle;
|
|
527
|
+
}
|
|
528
|
+
function decryptWithDataKey(bundle, dataKey) {
|
|
529
|
+
if (bundle.length < 1) {
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
if (bundle[0] !== 0) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
if (bundle.length < 12 + 16 + 1) {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
const nonce = bundle.slice(1, 13);
|
|
539
|
+
const authTag = bundle.slice(bundle.length - 16);
|
|
540
|
+
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
541
|
+
try {
|
|
542
|
+
const decipher = createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
543
|
+
decipher.setAuthTag(authTag);
|
|
544
|
+
const decrypted = Buffer.concat([
|
|
545
|
+
decipher.update(ciphertext),
|
|
546
|
+
decipher.final()
|
|
547
|
+
]);
|
|
548
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
549
|
+
} catch (error) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function encrypt(key, variant, data) {
|
|
554
|
+
if (variant === "legacy") {
|
|
555
|
+
return encryptLegacy(data, key);
|
|
556
|
+
} else {
|
|
557
|
+
return encryptWithDataKey(data, key);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function decrypt(key, variant, data) {
|
|
561
|
+
if (variant === "legacy") {
|
|
562
|
+
return decryptLegacy(data, key);
|
|
563
|
+
} else {
|
|
564
|
+
return decryptWithDataKey(data, key);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
465
568
|
const SessionMessageContentSchema = z.object({
|
|
466
569
|
c: z.string(),
|
|
467
570
|
// Base64 encoded encrypted content
|
|
@@ -593,108 +696,6 @@ const AgentMessageSchema = z.object({
|
|
|
593
696
|
});
|
|
594
697
|
z.union([UserMessageSchema, AgentMessageSchema]);
|
|
595
698
|
|
|
596
|
-
function encodeBase64(buffer, variant = "base64") {
|
|
597
|
-
if (variant === "base64url") {
|
|
598
|
-
return encodeBase64Url(buffer);
|
|
599
|
-
}
|
|
600
|
-
return Buffer.from(buffer).toString("base64");
|
|
601
|
-
}
|
|
602
|
-
function encodeBase64Url(buffer) {
|
|
603
|
-
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
604
|
-
}
|
|
605
|
-
function decodeBase64(base64, variant = "base64") {
|
|
606
|
-
if (variant === "base64url") {
|
|
607
|
-
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
608
|
-
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
609
|
-
}
|
|
610
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
611
|
-
}
|
|
612
|
-
function getRandomBytes(size) {
|
|
613
|
-
return new Uint8Array(randomBytes(size));
|
|
614
|
-
}
|
|
615
|
-
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
616
|
-
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
617
|
-
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
618
|
-
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
619
|
-
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
620
|
-
result.set(ephemeralKeyPair.publicKey, 0);
|
|
621
|
-
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
622
|
-
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
623
|
-
return result;
|
|
624
|
-
}
|
|
625
|
-
function encryptLegacy(data, secret) {
|
|
626
|
-
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
627
|
-
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
628
|
-
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
629
|
-
result.set(nonce);
|
|
630
|
-
result.set(encrypted, nonce.length);
|
|
631
|
-
return result;
|
|
632
|
-
}
|
|
633
|
-
function decryptLegacy(data, secret) {
|
|
634
|
-
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
635
|
-
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
636
|
-
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
637
|
-
if (!decrypted) {
|
|
638
|
-
return null;
|
|
639
|
-
}
|
|
640
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
641
|
-
}
|
|
642
|
-
function encryptWithDataKey(data, dataKey) {
|
|
643
|
-
const nonce = getRandomBytes(12);
|
|
644
|
-
const cipher = createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
645
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
646
|
-
const encrypted = Buffer.concat([
|
|
647
|
-
cipher.update(plaintext),
|
|
648
|
-
cipher.final()
|
|
649
|
-
]);
|
|
650
|
-
const authTag = cipher.getAuthTag();
|
|
651
|
-
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
652
|
-
bundle.set([0], 0);
|
|
653
|
-
bundle.set(nonce, 1);
|
|
654
|
-
bundle.set(new Uint8Array(encrypted), 13);
|
|
655
|
-
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
656
|
-
return bundle;
|
|
657
|
-
}
|
|
658
|
-
function decryptWithDataKey(bundle, dataKey) {
|
|
659
|
-
if (bundle.length < 1) {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
if (bundle[0] !== 0) {
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
if (bundle.length < 12 + 16 + 1) {
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
const nonce = bundle.slice(1, 13);
|
|
669
|
-
const authTag = bundle.slice(bundle.length - 16);
|
|
670
|
-
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
671
|
-
try {
|
|
672
|
-
const decipher = createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
673
|
-
decipher.setAuthTag(authTag);
|
|
674
|
-
const decrypted = Buffer.concat([
|
|
675
|
-
decipher.update(ciphertext),
|
|
676
|
-
decipher.final()
|
|
677
|
-
]);
|
|
678
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
679
|
-
} catch (error) {
|
|
680
|
-
return null;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
function encrypt(key, variant, data) {
|
|
684
|
-
if (variant === "legacy") {
|
|
685
|
-
return encryptLegacy(data, key);
|
|
686
|
-
} else {
|
|
687
|
-
return encryptWithDataKey(data, key);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
function decrypt(key, variant, data) {
|
|
691
|
-
if (variant === "legacy") {
|
|
692
|
-
return decryptLegacy(data, key);
|
|
693
|
-
} else {
|
|
694
|
-
return decryptWithDataKey(data, key);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
699
|
async function delay(ms) {
|
|
699
700
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
700
701
|
}
|
|
@@ -1358,6 +1359,7 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1358
1359
|
metadataLock = new AsyncLock();
|
|
1359
1360
|
encryptionKey;
|
|
1360
1361
|
encryptionVariant;
|
|
1362
|
+
pendingReliableCodexMessages = [];
|
|
1361
1363
|
constructor(credentials, session) {
|
|
1362
1364
|
super();
|
|
1363
1365
|
this.credentials = credentials;
|
|
@@ -1393,6 +1395,7 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1393
1395
|
this.socket.on("connect", () => {
|
|
1394
1396
|
logger.debug("Socket connected successfully");
|
|
1395
1397
|
this.rpcHandlerManager.onSocketConnect(this.socket);
|
|
1398
|
+
this.flushReliableCodexMessages();
|
|
1396
1399
|
});
|
|
1397
1400
|
this.socket.on("rpc-request", async (data, callback) => {
|
|
1398
1401
|
callback(await this.rpcHandlerManager.handleRequest(data));
|
|
@@ -1429,10 +1432,12 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1429
1432
|
if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
|
|
1430
1433
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.metadata.value));
|
|
1431
1434
|
this.metadataVersion = data.body.metadata.version;
|
|
1435
|
+
this.emit("metadata-updated", this.metadata);
|
|
1432
1436
|
}
|
|
1433
1437
|
if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
|
|
1434
1438
|
this.agentState = data.body.agentState.value ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.agentState.value)) : null;
|
|
1435
1439
|
this.agentStateVersion = data.body.agentState.version;
|
|
1440
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1436
1441
|
}
|
|
1437
1442
|
} else if (data.body.t === "update-machine") {
|
|
1438
1443
|
logger.debug(`[SOCKET] WARNING: Session client received unexpected machine update - ignoring`);
|
|
@@ -1454,6 +1459,33 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1454
1459
|
callback(this.pendingMessages.shift());
|
|
1455
1460
|
}
|
|
1456
1461
|
}
|
|
1462
|
+
getMetadataSnapshot() {
|
|
1463
|
+
return this.metadata;
|
|
1464
|
+
}
|
|
1465
|
+
getAgentStateSnapshot() {
|
|
1466
|
+
return this.agentState;
|
|
1467
|
+
}
|
|
1468
|
+
async waitForMetadataUpdate(signal) {
|
|
1469
|
+
if (signal?.aborted) {
|
|
1470
|
+
throw createAbortError();
|
|
1471
|
+
}
|
|
1472
|
+
return await new Promise((resolve, reject) => {
|
|
1473
|
+
const onAbort = () => {
|
|
1474
|
+
cleanup();
|
|
1475
|
+
reject(createAbortError());
|
|
1476
|
+
};
|
|
1477
|
+
const onMetadataUpdate = (metadata) => {
|
|
1478
|
+
cleanup();
|
|
1479
|
+
resolve(metadata);
|
|
1480
|
+
};
|
|
1481
|
+
const cleanup = () => {
|
|
1482
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1483
|
+
this.off("metadata-updated", onMetadataUpdate);
|
|
1484
|
+
};
|
|
1485
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1486
|
+
this.on("metadata-updated", onMetadataUpdate);
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1457
1489
|
/**
|
|
1458
1490
|
* Send message to session
|
|
1459
1491
|
* @param body - Message body (can be MessageContent or raw content for agent messages)
|
|
@@ -1512,25 +1544,28 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1512
1544
|
}
|
|
1513
1545
|
}
|
|
1514
1546
|
sendCodexMessage(body) {
|
|
1515
|
-
|
|
1547
|
+
const content = {
|
|
1516
1548
|
role: "agent",
|
|
1517
1549
|
content: {
|
|
1518
1550
|
type: "codex",
|
|
1519
1551
|
data: body
|
|
1520
|
-
// This wraps the entire Claude message
|
|
1521
1552
|
},
|
|
1522
1553
|
meta: {
|
|
1523
1554
|
sentFrom: "cli"
|
|
1524
1555
|
}
|
|
1525
1556
|
};
|
|
1526
1557
|
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
1558
|
+
const eventType = typeof body?.type === "string" ? body.type : "unknown";
|
|
1527
1559
|
if (!this.socket.connected) {
|
|
1528
|
-
|
|
1560
|
+
if (this.shouldBufferReliableCodexMessage(body)) {
|
|
1561
|
+
logger.debug("[API] Socket not connected, buffering reliable Codex message:", { type: eventType });
|
|
1562
|
+
this.pendingReliableCodexMessages.push({ encrypted, type: eventType });
|
|
1563
|
+
} else {
|
|
1564
|
+
logger.debug("[API] Socket not connected, dropping non-critical Codex message:", { type: eventType });
|
|
1565
|
+
}
|
|
1566
|
+
return;
|
|
1529
1567
|
}
|
|
1530
|
-
this.
|
|
1531
|
-
sid: this.sessionId,
|
|
1532
|
-
message: encrypted
|
|
1533
|
-
});
|
|
1568
|
+
this.emitEncryptedSessionMessage(encrypted);
|
|
1534
1569
|
}
|
|
1535
1570
|
/**
|
|
1536
1571
|
* Send a generic agent message to the session using ACP (Agent Communication Protocol) format.
|
|
@@ -1630,10 +1665,12 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1630
1665
|
if (answer.result === "success") {
|
|
1631
1666
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
|
|
1632
1667
|
this.metadataVersion = answer.version;
|
|
1668
|
+
this.emit("metadata-updated", this.metadata);
|
|
1633
1669
|
} else if (answer.result === "version-mismatch") {
|
|
1634
1670
|
if (answer.version > this.metadataVersion) {
|
|
1635
1671
|
this.metadataVersion = answer.version;
|
|
1636
1672
|
this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
|
|
1673
|
+
this.emit("metadata-updated", this.metadata);
|
|
1637
1674
|
}
|
|
1638
1675
|
throw new Error("Metadata version mismatch");
|
|
1639
1676
|
} else if (answer.result === "error") ;
|
|
@@ -1653,11 +1690,13 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1653
1690
|
if (answer.result === "success") {
|
|
1654
1691
|
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
1655
1692
|
this.agentStateVersion = answer.version;
|
|
1693
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1656
1694
|
logger.debug("Agent state updated", this.agentState);
|
|
1657
1695
|
} else if (answer.result === "version-mismatch") {
|
|
1658
1696
|
if (answer.version > this.agentStateVersion) {
|
|
1659
1697
|
this.agentStateVersion = answer.version;
|
|
1660
1698
|
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
1699
|
+
this.emit("agent-state-updated", this.agentState);
|
|
1661
1700
|
}
|
|
1662
1701
|
throw new Error("Agent state version mismatch");
|
|
1663
1702
|
} else if (answer.result === "error") ;
|
|
@@ -1684,6 +1723,44 @@ class ApiSessionClient extends EventEmitter {
|
|
|
1684
1723
|
logger.debug("[API] socket.close() called");
|
|
1685
1724
|
this.socket.close();
|
|
1686
1725
|
}
|
|
1726
|
+
emitEncryptedSessionMessage(encrypted) {
|
|
1727
|
+
this.socket.emit("message", {
|
|
1728
|
+
sid: this.sessionId,
|
|
1729
|
+
message: encrypted
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
flushReliableCodexMessages() {
|
|
1733
|
+
if (!this.socket.connected || this.pendingReliableCodexMessages.length === 0) {
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
const buffered = this.pendingReliableCodexMessages.splice(0, this.pendingReliableCodexMessages.length);
|
|
1737
|
+
logger.debug("[API] Flushing buffered Codex messages after reconnect", {
|
|
1738
|
+
count: buffered.length,
|
|
1739
|
+
types: buffered.map((message) => message.type)
|
|
1740
|
+
});
|
|
1741
|
+
for (const message of buffered) {
|
|
1742
|
+
this.emitEncryptedSessionMessage(message.encrypted);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
shouldBufferReliableCodexMessage(body) {
|
|
1746
|
+
switch (body?.type) {
|
|
1747
|
+
case "message":
|
|
1748
|
+
case "tool-call":
|
|
1749
|
+
case "tool-call-result":
|
|
1750
|
+
case "permission-request":
|
|
1751
|
+
case "task_started":
|
|
1752
|
+
case "task_complete":
|
|
1753
|
+
case "turn_aborted":
|
|
1754
|
+
return true;
|
|
1755
|
+
default:
|
|
1756
|
+
return false;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
function createAbortError() {
|
|
1761
|
+
const error = new Error("The operation was aborted");
|
|
1762
|
+
error.name = "AbortError";
|
|
1763
|
+
return error;
|
|
1687
1764
|
}
|
|
1688
1765
|
|
|
1689
1766
|
class ApiMachineClient {
|
|
@@ -2460,53 +2537,4 @@ var api = /*#__PURE__*/Object.freeze({
|
|
|
2460
2537
|
ApiClient: ApiClient
|
|
2461
2538
|
});
|
|
2462
2539
|
|
|
2463
|
-
|
|
2464
|
-
input_tokens: z.number().int().nonnegative(),
|
|
2465
|
-
cache_creation_input_tokens: z.number().int().nonnegative().optional(),
|
|
2466
|
-
cache_read_input_tokens: z.number().int().nonnegative().optional(),
|
|
2467
|
-
output_tokens: z.number().int().nonnegative(),
|
|
2468
|
-
service_tier: z.string().optional()
|
|
2469
|
-
}).passthrough();
|
|
2470
|
-
const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
|
2471
|
-
// User message - validates uuid and message.content
|
|
2472
|
-
z.object({
|
|
2473
|
-
type: z.literal("user"),
|
|
2474
|
-
isSidechain: z.boolean().optional(),
|
|
2475
|
-
isMeta: z.boolean().optional(),
|
|
2476
|
-
uuid: z.string(),
|
|
2477
|
-
// Used in getMessageKey()
|
|
2478
|
-
message: z.object({
|
|
2479
|
-
content: z.union([z.string(), z.any()])
|
|
2480
|
-
// Used in sessionScanner.ts
|
|
2481
|
-
}).passthrough()
|
|
2482
|
-
}).passthrough(),
|
|
2483
|
-
// Assistant message - only validates uuid and type
|
|
2484
|
-
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
|
2485
|
-
// which may have different structure than normal assistant messages
|
|
2486
|
-
z.object({
|
|
2487
|
-
uuid: z.string(),
|
|
2488
|
-
type: z.literal("assistant"),
|
|
2489
|
-
message: z.object({
|
|
2490
|
-
usage: UsageSchema.optional(),
|
|
2491
|
-
// Used in apiSession.ts
|
|
2492
|
-
model: z.string().optional()
|
|
2493
|
-
// Used for cost calculation
|
|
2494
|
-
}).passthrough().optional()
|
|
2495
|
-
}).passthrough(),
|
|
2496
|
-
// Summary message - validates summary and leafUuid
|
|
2497
|
-
z.object({
|
|
2498
|
-
type: z.literal("summary"),
|
|
2499
|
-
summary: z.string(),
|
|
2500
|
-
// Used in apiSession.ts
|
|
2501
|
-
leafUuid: z.string()
|
|
2502
|
-
// Used in getMessageKey()
|
|
2503
|
-
}).passthrough(),
|
|
2504
|
-
// System message - validates uuid
|
|
2505
|
-
z.object({
|
|
2506
|
-
type: z.literal("system"),
|
|
2507
|
-
uuid: z.string()
|
|
2508
|
-
// Used in getMessageKey()
|
|
2509
|
-
}).passthrough()
|
|
2510
|
-
]);
|
|
2511
|
-
|
|
2512
|
-
export { ApiClient as A, HAPPY_CLOUD_DAEMON_PORT as H, RawJSONLinesSchema as R, ApiSessionClient as a, connectionState as b, configuration as c, delay as d, encodeBase64 as e, backoff as f, AsyncLock as g, buildAuthenticatedHeaders as h, encodeBase64Url as i, buildClientHeaders as j, decodeBase64 as k, logger as l, getLatestDaemonLog as m, api as n, packageJson as p, startOfflineReconnection as s };
|
|
2540
|
+
export { ApiClient as A, HAPPY_CLOUD_DAEMON_PORT as H, ApiSessionClient as a, connectionState as b, configuration as c, backoff as d, encodeBase64 as e, delay as f, AsyncLock as g, buildAuthenticatedHeaders as h, encodeBase64Url as i, buildClientHeaders as j, decodeBase64 as k, logger as l, getLatestDaemonLog as m, api as n, packageJson as p, startOfflineReconnection as s };
|