@ccpocket/bridge 1.43.1 → 1.44.1
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/codex-process.d.ts +17 -0
- package/dist/codex-process.js +38 -0
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +46 -0
- package/dist/parser.js +48 -0
- package/dist/parser.js.map +1 -1
- package/dist/session.d.ts +33 -2
- package/dist/session.js +136 -0
- package/dist/session.js.map +1 -1
- package/dist/websocket.d.ts +3 -0
- package/dist/websocket.js +175 -20
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { execFile, execFileSync } from "node:child_process";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { lstat, readFile, readlink, stat, unlink } from "node:fs/promises";
|
|
@@ -178,6 +179,7 @@ export class BridgeWebSocketServer {
|
|
|
178
179
|
failSetPermissionMode = envFlagEnabled("BRIDGE_FAIL_SET_PERMISSION_MODE");
|
|
179
180
|
failSetSandboxMode = envFlagEnabled("BRIDGE_FAIL_SET_SANDBOX_MODE");
|
|
180
181
|
platform;
|
|
182
|
+
clientSupportedServerMessages = new WeakMap();
|
|
181
183
|
constructor(options) {
|
|
182
184
|
const { server, apiKey, allowedDirs, imageStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup, platform, } = options;
|
|
183
185
|
this.apiKey = apiKey ?? null;
|
|
@@ -477,6 +479,10 @@ export class BridgeWebSocketServer {
|
|
|
477
479
|
});
|
|
478
480
|
}
|
|
479
481
|
async handleClientMessage(msg, ws) {
|
|
482
|
+
if (msg.type === "client_capabilities") {
|
|
483
|
+
this.clientSupportedServerMessages.set(ws, new Set(msg.supportedServerMessages ?? []));
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
480
486
|
const incomingSessionId = this.extractSessionIdFromClientMessage(msg);
|
|
481
487
|
const isActiveRuntimeSession = incomingSessionId != null &&
|
|
482
488
|
this.sessionManager.get(incomingSessionId) != null;
|
|
@@ -667,16 +673,8 @@ export class BridgeWebSocketServer {
|
|
|
667
673
|
return;
|
|
668
674
|
}
|
|
669
675
|
const text = msg.text;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
!session.process.isWaitingForInput) {
|
|
673
|
-
this.send(ws, {
|
|
674
|
-
type: "input_rejected",
|
|
675
|
-
sessionId: session.id,
|
|
676
|
-
reason: "Process is busy",
|
|
677
|
-
});
|
|
678
|
-
break;
|
|
679
|
-
}
|
|
676
|
+
const codexSkills = msg.skills ?? (msg.skill ? [msg.skill] : []);
|
|
677
|
+
const codexMentions = msg.mentions ?? [];
|
|
680
678
|
// Snapshot busy state before dispatch. We prefer the actual enqueue
|
|
681
679
|
// result returned by SdkProcess sendInput* below, but keep this as a
|
|
682
680
|
// fallback for test doubles and async paths.
|
|
@@ -709,6 +707,50 @@ export class BridgeWebSocketServer {
|
|
|
709
707
|
if (imageRefs.length === 0)
|
|
710
708
|
imageRefs = undefined;
|
|
711
709
|
}
|
|
710
|
+
if (session.provider === "codex" &&
|
|
711
|
+
!session.process.isWaitingForInput) {
|
|
712
|
+
if (session.codexQueuedInput) {
|
|
713
|
+
this.send(ws, {
|
|
714
|
+
type: "input_rejected",
|
|
715
|
+
sessionId: session.id,
|
|
716
|
+
reason: "Queue is full",
|
|
717
|
+
});
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
const queued = this.sessionManager.queueCodexInput(session.id, {
|
|
721
|
+
itemId: randomUUID(),
|
|
722
|
+
text,
|
|
723
|
+
createdAt: new Date().toISOString(),
|
|
724
|
+
...(images.length > 0 ? { imageCount: images.length, images } : {}),
|
|
725
|
+
...(imageRefs ? { imageRefs } : {}),
|
|
726
|
+
...(codexSkills.length > 0 ? { skills: codexSkills } : {}),
|
|
727
|
+
...(codexMentions.length > 0 ? { mentions: codexMentions } : {}),
|
|
728
|
+
});
|
|
729
|
+
if (!queued) {
|
|
730
|
+
this.send(ws, {
|
|
731
|
+
type: "input_rejected",
|
|
732
|
+
sessionId: session.id,
|
|
733
|
+
reason: "Queue is full",
|
|
734
|
+
});
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
if (images.length > 0 && this.galleryStore && session.projectPath) {
|
|
738
|
+
for (const img of images) {
|
|
739
|
+
this.galleryStore
|
|
740
|
+
.addImageFromBase64(img.base64, img.mimeType, session.projectPath, msg.sessionId)
|
|
741
|
+
.catch((err) => {
|
|
742
|
+
console.warn(`[ws] Failed to persist queued image to gallery: ${err}`);
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
this.send(ws, {
|
|
747
|
+
type: "input_ack",
|
|
748
|
+
sessionId: session.id,
|
|
749
|
+
queued: true,
|
|
750
|
+
});
|
|
751
|
+
this.broadcastSessionList();
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
712
754
|
session.history.push({
|
|
713
755
|
type: "user_input",
|
|
714
756
|
text,
|
|
@@ -734,13 +776,11 @@ export class BridgeWebSocketServer {
|
|
|
734
776
|
queued: false,
|
|
735
777
|
});
|
|
736
778
|
const codexProc = session.process;
|
|
737
|
-
const skills = msg.skills ?? (msg.skill ? [msg.skill] : []);
|
|
738
|
-
const mentions = msg.mentions ?? [];
|
|
739
779
|
if (images.length > 0) {
|
|
740
780
|
codexProc.sendInputStructured(text, {
|
|
741
781
|
images,
|
|
742
|
-
skills,
|
|
743
|
-
mentions,
|
|
782
|
+
skills: codexSkills,
|
|
783
|
+
mentions: codexMentions,
|
|
744
784
|
});
|
|
745
785
|
}
|
|
746
786
|
else if (msg.imageId && this.galleryStore) {
|
|
@@ -750,22 +790,31 @@ export class BridgeWebSocketServer {
|
|
|
750
790
|
if (imageData) {
|
|
751
791
|
codexProc.sendInputStructured(text, {
|
|
752
792
|
images: [imageData],
|
|
753
|
-
skills,
|
|
754
|
-
mentions,
|
|
793
|
+
skills: codexSkills,
|
|
794
|
+
mentions: codexMentions,
|
|
755
795
|
});
|
|
756
796
|
}
|
|
757
797
|
else {
|
|
758
798
|
console.warn(`[ws] Image not found: ${msg.imageId}`);
|
|
759
|
-
codexProc.sendInputStructured(text, {
|
|
799
|
+
codexProc.sendInputStructured(text, {
|
|
800
|
+
skills: codexSkills,
|
|
801
|
+
mentions: codexMentions,
|
|
802
|
+
});
|
|
760
803
|
}
|
|
761
804
|
})
|
|
762
805
|
.catch((err) => {
|
|
763
806
|
console.error(`[ws] Failed to load image: ${err}`);
|
|
764
|
-
codexProc.sendInputStructured(text, {
|
|
807
|
+
codexProc.sendInputStructured(text, {
|
|
808
|
+
skills: codexSkills,
|
|
809
|
+
mentions: codexMentions,
|
|
810
|
+
});
|
|
765
811
|
});
|
|
766
812
|
}
|
|
767
|
-
else if (
|
|
768
|
-
codexProc.sendInputStructured(text, {
|
|
813
|
+
else if (codexSkills.length > 0 || codexMentions.length > 0) {
|
|
814
|
+
codexProc.sendInputStructured(text, {
|
|
815
|
+
skills: codexSkills,
|
|
816
|
+
mentions: codexMentions,
|
|
817
|
+
});
|
|
769
818
|
}
|
|
770
819
|
else {
|
|
771
820
|
codexProc.sendInput(text);
|
|
@@ -841,6 +890,69 @@ export class BridgeWebSocketServer {
|
|
|
841
890
|
}
|
|
842
891
|
break;
|
|
843
892
|
}
|
|
893
|
+
case "update_queued_input": {
|
|
894
|
+
const session = this.resolveSession(msg.sessionId);
|
|
895
|
+
if (!session || session.provider !== "codex") {
|
|
896
|
+
this.send(ws, { type: "error", message: "No active Codex session." });
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (!msg.text.trim()) {
|
|
900
|
+
this.send(ws, {
|
|
901
|
+
type: "error",
|
|
902
|
+
message: "Queued message cannot be empty.",
|
|
903
|
+
});
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const success = this.sessionManager.updateCodexQueuedInput(session.id, msg.itemId, msg.text, { skills: msg.skills ?? [], mentions: msg.mentions ?? [] });
|
|
907
|
+
if (!success) {
|
|
908
|
+
this.send(ws, {
|
|
909
|
+
type: "error",
|
|
910
|
+
message: "Queued message not found.",
|
|
911
|
+
errorCode: "queued_input_not_found",
|
|
912
|
+
});
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
this.broadcastSessionList();
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
case "cancel_queued_input": {
|
|
919
|
+
const session = this.resolveSession(msg.sessionId);
|
|
920
|
+
if (!session || session.provider !== "codex") {
|
|
921
|
+
this.send(ws, { type: "error", message: "No active Codex session." });
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const success = this.sessionManager.cancelCodexQueuedInput(session.id, msg.itemId);
|
|
925
|
+
if (!success) {
|
|
926
|
+
this.send(ws, {
|
|
927
|
+
type: "error",
|
|
928
|
+
message: "Queued message not found.",
|
|
929
|
+
errorCode: "queued_input_not_found",
|
|
930
|
+
});
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
this.broadcastSessionList();
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
case "steer_queued_input": {
|
|
937
|
+
const session = this.resolveSession(msg.sessionId);
|
|
938
|
+
if (!session || session.provider !== "codex") {
|
|
939
|
+
this.send(ws, { type: "error", message: "No active Codex session." });
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
const result = await this.sessionManager.steerCodexQueuedInput(session.id, msg.itemId);
|
|
943
|
+
if (!result.ok) {
|
|
944
|
+
this.send(ws, {
|
|
945
|
+
type: "error",
|
|
946
|
+
message: result.error,
|
|
947
|
+
errorCode: result.error === "Queued message not found."
|
|
948
|
+
? "queued_input_not_found"
|
|
949
|
+
: "queued_input_steer_failed",
|
|
950
|
+
});
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
this.broadcastSessionList();
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
844
956
|
case "push_register": {
|
|
845
957
|
const locale = normalizePushLocale(msg.locale);
|
|
846
958
|
const privacyMode = msg.privacyMode === true;
|
|
@@ -1478,6 +1590,31 @@ export class BridgeWebSocketServer {
|
|
|
1478
1590
|
status: session.status,
|
|
1479
1591
|
sessionId: msg.sessionId,
|
|
1480
1592
|
});
|
|
1593
|
+
if (session.provider === "codex") {
|
|
1594
|
+
const item = session.codexQueuedInput;
|
|
1595
|
+
this.sendConversationQueue(ws, {
|
|
1596
|
+
type: "conversation_queue",
|
|
1597
|
+
sessionId: msg.sessionId,
|
|
1598
|
+
limit: 1,
|
|
1599
|
+
items: item
|
|
1600
|
+
? [
|
|
1601
|
+
{
|
|
1602
|
+
itemId: item.itemId,
|
|
1603
|
+
text: item.text,
|
|
1604
|
+
createdAt: item.createdAt,
|
|
1605
|
+
...(item.updatedAt ? { updatedAt: item.updatedAt } : {}),
|
|
1606
|
+
...(item.imageCount
|
|
1607
|
+
? { imageCount: item.imageCount }
|
|
1608
|
+
: {}),
|
|
1609
|
+
...(item.skills?.length ? { skills: item.skills } : {}),
|
|
1610
|
+
...(item.mentions?.length
|
|
1611
|
+
? { mentions: item.mentions }
|
|
1612
|
+
: {}),
|
|
1613
|
+
},
|
|
1614
|
+
]
|
|
1615
|
+
: [],
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1481
1618
|
// Send cached slash commands so the client can restore them even when
|
|
1482
1619
|
// the original init/supported_commands message was evicted from the
|
|
1483
1620
|
// in-memory history (MAX_HISTORY_PER_SESSION overflow).
|
|
@@ -3049,6 +3186,8 @@ export class BridgeWebSocketServer {
|
|
|
3049
3186
|
const data = JSON.stringify({ ...msg, sessionId });
|
|
3050
3187
|
for (const client of this.wss.clients) {
|
|
3051
3188
|
if (client.readyState === WebSocket.OPEN) {
|
|
3189
|
+
if (!this.shouldSendToClient(client, msg))
|
|
3190
|
+
continue;
|
|
3052
3191
|
client.send(data);
|
|
3053
3192
|
}
|
|
3054
3193
|
}
|
|
@@ -3371,11 +3510,27 @@ export class BridgeWebSocketServer {
|
|
|
3371
3510
|
const data = JSON.stringify(msg);
|
|
3372
3511
|
for (const client of this.wss.clients) {
|
|
3373
3512
|
if (client.readyState === WebSocket.OPEN) {
|
|
3513
|
+
if (!this.shouldSendToClient(client, msg))
|
|
3514
|
+
continue;
|
|
3374
3515
|
client.send(data);
|
|
3375
3516
|
}
|
|
3376
3517
|
}
|
|
3377
3518
|
}
|
|
3519
|
+
shouldSendToClient(ws, msg) {
|
|
3520
|
+
if (msg.type !== "conversation_queue")
|
|
3521
|
+
return true;
|
|
3522
|
+
return (this.clientSupportedServerMessages
|
|
3523
|
+
.get(ws)
|
|
3524
|
+
?.has("conversation_queue") ?? false);
|
|
3525
|
+
}
|
|
3526
|
+
sendConversationQueue(ws, msg) {
|
|
3527
|
+
if (!this.shouldSendToClient(ws, msg))
|
|
3528
|
+
return;
|
|
3529
|
+
this.send(ws, msg);
|
|
3530
|
+
}
|
|
3378
3531
|
send(ws, msg) {
|
|
3532
|
+
if (!this.shouldSendToClient(ws, msg))
|
|
3533
|
+
return;
|
|
3379
3534
|
const sessionId = this.extractSessionIdFromServerMessage(msg);
|
|
3380
3535
|
if (sessionId) {
|
|
3381
3536
|
this.recordDebugEvent(sessionId, {
|