@ccpocket/bridge 1.75.0 → 1.77.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/README.md +3 -0
- package/dist/index.js +36 -6
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +104 -0
- package/dist/parser.js +50 -2
- package/dist/parser.js.map +1 -1
- package/dist/upload-store.d.ts +84 -0
- package/dist/upload-store.js +559 -0
- package/dist/upload-store.js.map +1 -0
- package/dist/websocket.d.ts +9 -0
- package/dist/websocket.js +445 -61
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.js
CHANGED
|
@@ -11,6 +11,7 @@ import { codexErrorMessage, CodexRpcError, CodexProcess, } from "./codex-process
|
|
|
11
11
|
import { stopManagedCodexAppServers } from "./codex-transport.js";
|
|
12
12
|
import { parseClientMessage, } from "./parser.js";
|
|
13
13
|
import { getAllRecentSessions, getCodexSessionHistory, getSessionHistory, codexUserTurnUuid, codexThreadToSessionHistory, findSessionsByClaudeIds, extractMessageImages, getClaudeSessionName, getCodexSessionIndexMetadata, loadCodexSessionNames, renameClaudeSession, renameCodexSession, saveCodexSessionProfile, } from "./sessions-index.js";
|
|
14
|
+
import { isSafeUploadFileName, UploadStoreError, } from "./upload-store.js";
|
|
14
15
|
import { formatResumePerformanceLog, summarizeResumeHistory, } from "./resume-metrics.js";
|
|
15
16
|
import { ArchiveStore } from "./archive-store.js";
|
|
16
17
|
import { WorktreeStore } from "./worktree-store.js";
|
|
@@ -26,6 +27,12 @@ import { getPackageVersion } from "./version.js";
|
|
|
26
27
|
import { isPathWithinAllowedDirectory, resolvePlatformPath, resolvePlatformPathFrom, } from "./path-utils.js";
|
|
27
28
|
import { DirectoryListingError, listAllowedDirectories, } from "./directory-listing.js";
|
|
28
29
|
import { deriveCodexPermissionsMode, normalizeCodexPermissionsMode, withDerivedCodexPermissionsMode, } from "./codex-permissions.js";
|
|
30
|
+
function projectRequestMetadata(request) {
|
|
31
|
+
return {
|
|
32
|
+
projectPath: request.projectPath,
|
|
33
|
+
...(request.requestId ? { requestId: request.requestId } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
29
36
|
const RESUME_OPERATION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
30
37
|
const RESUME_COMPLETED_TTL_MS = 30 * 1000;
|
|
31
38
|
// ---- Available model lists (delivered to clients via session_list) ----
|
|
@@ -483,6 +490,7 @@ export class BridgeWebSocketServer {
|
|
|
483
490
|
static DEFAULT_FILE_LIST_MAX_ENTRIES = 5000;
|
|
484
491
|
static DEFAULT_FILE_LIST_MAX_BYTES = 512 * 1024;
|
|
485
492
|
static DEFAULT_FILE_DOWNLOAD_MAX_BYTES = 512 * 1024 * 1024;
|
|
493
|
+
static DEFAULT_FILE_UPLOAD_MAX_BYTES = 512 * 1024 * 1024;
|
|
486
494
|
static DEFAULT_DELTA_BATCH_MS = 100;
|
|
487
495
|
static DEFAULT_DELTA_BATCH_MAX_CHARS = 4096;
|
|
488
496
|
wss;
|
|
@@ -491,6 +499,7 @@ export class BridgeWebSocketServer {
|
|
|
491
499
|
allowedDirs;
|
|
492
500
|
imageStore;
|
|
493
501
|
mediaStore;
|
|
502
|
+
uploadStore;
|
|
494
503
|
galleryStore;
|
|
495
504
|
projectHistory;
|
|
496
505
|
debugTraceStore;
|
|
@@ -535,6 +544,7 @@ export class BridgeWebSocketServer {
|
|
|
535
544
|
fileListMaxEntries;
|
|
536
545
|
fileListMaxBytes;
|
|
537
546
|
fileDownloadMaxBytes;
|
|
547
|
+
fileUploadMaxBytes;
|
|
538
548
|
deltaBatchMs;
|
|
539
549
|
deltaBatchMaxChars;
|
|
540
550
|
deltaBatches = new Map();
|
|
@@ -543,11 +553,12 @@ export class BridgeWebSocketServer {
|
|
|
543
553
|
pendingClaudeResumeInputs = new WeakMap();
|
|
544
554
|
resumeOperations = new Map();
|
|
545
555
|
constructor(options) {
|
|
546
|
-
const { server, apiKey, allowedDirs, imageStore, mediaStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup, promptHistoryStore, platform, fileListMaxEntries, fileListMaxBytes, fileDownloadMaxBytes, deltaBatchMs, deltaBatchMaxChars, } = options;
|
|
556
|
+
const { server, apiKey, allowedDirs, imageStore, mediaStore, uploadStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup, promptHistoryStore, platform, fileListMaxEntries, fileListMaxBytes, fileDownloadMaxBytes, fileUploadMaxBytes, deltaBatchMs, deltaBatchMaxChars, } = options;
|
|
547
557
|
this.apiKey = apiKey ?? null;
|
|
548
558
|
this.allowedDirs = allowedDirs ?? [];
|
|
549
559
|
this.imageStore = imageStore ?? null;
|
|
550
560
|
this.mediaStore = mediaStore ?? null;
|
|
561
|
+
this.uploadStore = uploadStore ?? null;
|
|
551
562
|
this.galleryStore = galleryStore ?? null;
|
|
552
563
|
this.projectHistory = projectHistory ?? null;
|
|
553
564
|
this.debugTraceStore = debugTraceStore ?? new DebugTraceStore();
|
|
@@ -560,6 +571,7 @@ export class BridgeWebSocketServer {
|
|
|
560
571
|
this.fileListMaxEntries = normalizePositiveLimit(fileListMaxEntries, positiveEnvInt("BRIDGE_FILE_LIST_MAX_ENTRIES", BridgeWebSocketServer.DEFAULT_FILE_LIST_MAX_ENTRIES));
|
|
561
572
|
this.fileListMaxBytes = normalizePositiveLimit(fileListMaxBytes, positiveEnvInt("BRIDGE_FILE_LIST_MAX_BYTES", BridgeWebSocketServer.DEFAULT_FILE_LIST_MAX_BYTES));
|
|
562
573
|
this.fileDownloadMaxBytes = normalizePositiveLimit(fileDownloadMaxBytes, positiveEnvInt("BRIDGE_FILE_DOWNLOAD_MAX_SIZE_MB", BridgeWebSocketServer.DEFAULT_FILE_DOWNLOAD_MAX_BYTES / 1024 / 1024) * 1024 * 1024);
|
|
574
|
+
this.fileUploadMaxBytes = normalizePositiveLimit(fileUploadMaxBytes, positiveEnvInt("BRIDGE_FILE_UPLOAD_MAX_SIZE_MB", BridgeWebSocketServer.DEFAULT_FILE_UPLOAD_MAX_BYTES / 1024 / 1024) * 1024 * 1024);
|
|
563
575
|
this.deltaBatchMs = normalizeNonNegativeLimit(deltaBatchMs, nonNegativeEnvInt("BRIDGE_DELTA_BATCH_MS", BridgeWebSocketServer.DEFAULT_DELTA_BATCH_MS));
|
|
564
576
|
this.deltaBatchMaxChars = normalizePositiveLimit(deltaBatchMaxChars, positiveEnvInt("BRIDGE_DELTA_BATCH_MAX_CHARS", BridgeWebSocketServer.DEFAULT_DELTA_BATCH_MAX_CHARS));
|
|
565
577
|
this.archiveStore = new ArchiveStore();
|
|
@@ -714,12 +726,85 @@ export class BridgeWebSocketServer {
|
|
|
714
726
|
this.sendFileDownloadError(ws, request, "file_download_failed", "Unable to prepare the file download.");
|
|
715
727
|
}
|
|
716
728
|
}
|
|
729
|
+
sendFileUploadError(ws, requestId, errorCode, message, path) {
|
|
730
|
+
this.send(ws, { type: "error", errorCode, message, requestId, path });
|
|
731
|
+
}
|
|
732
|
+
async prepareFileUpload(ws, request) {
|
|
733
|
+
const pathApi = this.platform === "win32" ? win32 : posix;
|
|
734
|
+
const projectPath = pathApi.resolve(request.projectPath);
|
|
735
|
+
if (!this.uploadStore ||
|
|
736
|
+
pathApi.isAbsolute(request.directoryPath) ||
|
|
737
|
+
!isSafeUploadFileName(request.fileName)) {
|
|
738
|
+
this.sendFileUploadError(ws, request.requestId, !this.uploadStore ? "file_upload_unavailable" : "file_upload_not_allowed", !this.uploadStore
|
|
739
|
+
? "File uploads are unavailable on this Bridge."
|
|
740
|
+
: "The upload destination or file name is not allowed.", request.directoryPath);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (request.sizeBytes > this.fileUploadMaxBytes) {
|
|
744
|
+
this.sendFileUploadError(ws, request.requestId, "file_upload_too_large", `File is too large to upload. Maximum size is ${Math.ceil(this.fileUploadMaxBytes / 1024 / 1024)} MB.`, request.fileName);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const requestedDirectory = pathApi.resolve(projectPath, request.directoryPath || ".");
|
|
748
|
+
if (!this.isPathAllowed(projectPath) ||
|
|
749
|
+
!isPathWithinAllowedDirectory(requestedDirectory, projectPath, this.platform)) {
|
|
750
|
+
this.sendFileUploadError(ws, request.requestId, "file_upload_not_allowed", "The upload destination is outside the current project.", request.directoryPath);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
const canonicalProject = await realpath(projectPath);
|
|
755
|
+
const canonicalDirectory = await realpath(requestedDirectory);
|
|
756
|
+
const directoryStat = await stat(canonicalDirectory);
|
|
757
|
+
if (!directoryStat.isDirectory() ||
|
|
758
|
+
!isPathWithinAllowedDirectory(canonicalDirectory, canonicalProject, this.platform) ||
|
|
759
|
+
!(await this.isCanonicalPathAllowed(canonicalDirectory))) {
|
|
760
|
+
throw new UploadStoreError("file_upload_directory_changed", "The upload destination is not allowed.");
|
|
761
|
+
}
|
|
762
|
+
const ref = await this.uploadStore.register({
|
|
763
|
+
directoryPath: canonicalDirectory,
|
|
764
|
+
relativeDirectoryPath: pathApi
|
|
765
|
+
.relative(projectPath, requestedDirectory)
|
|
766
|
+
.split(pathApi.sep)
|
|
767
|
+
.join("/"),
|
|
768
|
+
fileName: request.fileName,
|
|
769
|
+
sizeBytes: request.sizeBytes,
|
|
770
|
+
conflictPolicy: request.conflictPolicy,
|
|
771
|
+
});
|
|
772
|
+
this.send(ws, {
|
|
773
|
+
type: "file_upload_ready",
|
|
774
|
+
requestId: request.requestId,
|
|
775
|
+
fileName: request.fileName,
|
|
776
|
+
sizeBytes: request.sizeBytes,
|
|
777
|
+
uploadUrl: ref.url,
|
|
778
|
+
uploadToken: ref.token,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
const known = error instanceof UploadStoreError ? error : null;
|
|
783
|
+
this.sendFileUploadError(ws, request.requestId, known?.code ?? "file_upload_directory_not_found", known?.message ?? "The upload destination was not found or is not allowed.", request.directoryPath);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
async finalizeFileUpload(ws, request) {
|
|
787
|
+
if (!this.uploadStore) {
|
|
788
|
+
this.sendFileUploadError(ws, request.requestId, "file_upload_unavailable", "File uploads are unavailable on this Bridge.");
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
const result = await this.uploadStore.finalize(request.uploadToken, request.sha256);
|
|
793
|
+
this.send(ws, { type: "file_upload_complete", requestId: request.requestId, ...result });
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
const known = error instanceof UploadStoreError ? error : null;
|
|
797
|
+
this.sendFileUploadError(ws, request.requestId, known?.code ?? "file_upload_failed", known?.message ?? "Unable to finish the file upload.");
|
|
798
|
+
}
|
|
799
|
+
}
|
|
717
800
|
/** Build a user-friendly error for disallowed project paths. */
|
|
718
|
-
buildPathNotAllowedError(projectPath) {
|
|
801
|
+
buildPathNotAllowedError(projectPath, requestId) {
|
|
719
802
|
return {
|
|
720
803
|
type: "error",
|
|
721
804
|
message: `⚠ Project path not allowed\n\n"${projectPath}" is not in the allowed directories.\n\nFix: Update BRIDGE_ALLOWED_DIRS on the Bridge server to include this path.`,
|
|
722
805
|
errorCode: "path_not_allowed",
|
|
806
|
+
path: projectPath,
|
|
807
|
+
requestId,
|
|
723
808
|
};
|
|
724
809
|
}
|
|
725
810
|
sendToolActionError(ws, message, error) {
|
|
@@ -750,7 +835,7 @@ export class BridgeWebSocketServer {
|
|
|
750
835
|
return { roots: [...normalized.values()] };
|
|
751
836
|
}
|
|
752
837
|
buildSessionCreatedMessage(params) {
|
|
753
|
-
const { sessionId, provider, projectPath, session, permissionMode, executionMode, planMode, approvalsReviewer, codexPermissionsMode, sandboxMode, slashCommands, skills, skillMetadata, apps, appMetadata, plugins, pluginMetadata, sourceSessionId, resumeRequestId, } = params;
|
|
838
|
+
const { sessionId, provider, projectPath, session, permissionMode, executionMode, planMode, approvalsReviewer, codexPermissionsMode, sandboxMode, slashCommands, skills, skillMetadata, apps, appMetadata, plugins, pluginMetadata, sourceSessionId, resumeRequestId, requestId, } = params;
|
|
754
839
|
const derivedCodexSettings = provider === "codex"
|
|
755
840
|
? withDerivedCodexPermissionsMode(session?.codexSettings)
|
|
756
841
|
: session?.codexSettings;
|
|
@@ -846,6 +931,7 @@ export class BridgeWebSocketServer {
|
|
|
846
931
|
: {}),
|
|
847
932
|
...(sourceSessionId ? { sourceSessionId } : {}),
|
|
848
933
|
...(resumeRequestId ? { resumeRequestId } : {}),
|
|
934
|
+
...(requestId ? { requestId } : {}),
|
|
849
935
|
};
|
|
850
936
|
if (provider === "codex" && derivedCodexSettings) {
|
|
851
937
|
if (derivedCodexSettings.model !== undefined) {
|
|
@@ -880,6 +966,7 @@ export class BridgeWebSocketServer {
|
|
|
880
966
|
if (mode !== "conversation") {
|
|
881
967
|
this.send(ws, {
|
|
882
968
|
type: "rewind_result",
|
|
969
|
+
sessionId,
|
|
883
970
|
success: false,
|
|
884
971
|
mode,
|
|
885
972
|
error: "Codex only supports conversation rewind",
|
|
@@ -890,6 +977,7 @@ export class BridgeWebSocketServer {
|
|
|
890
977
|
if (!session) {
|
|
891
978
|
this.send(ws, {
|
|
892
979
|
type: "rewind_result",
|
|
980
|
+
sessionId,
|
|
893
981
|
success: false,
|
|
894
982
|
mode,
|
|
895
983
|
error: `Session ${sessionId} not found`,
|
|
@@ -901,6 +989,7 @@ export class BridgeWebSocketServer {
|
|
|
901
989
|
typeof codexProcess.rollbackThread !== "function") {
|
|
902
990
|
this.send(ws, {
|
|
903
991
|
type: "rewind_result",
|
|
992
|
+
sessionId,
|
|
904
993
|
success: false,
|
|
905
994
|
mode,
|
|
906
995
|
error: "Session is not a Codex session",
|
|
@@ -910,6 +999,7 @@ export class BridgeWebSocketServer {
|
|
|
910
999
|
if (session.status !== "idle" || (codexProcess.status ?? session.status) !== "idle") {
|
|
911
1000
|
this.send(ws, {
|
|
912
1001
|
type: "rewind_result",
|
|
1002
|
+
sessionId,
|
|
913
1003
|
success: false,
|
|
914
1004
|
mode,
|
|
915
1005
|
error: "Cannot rewind while Codex is running",
|
|
@@ -919,6 +1009,7 @@ export class BridgeWebSocketServer {
|
|
|
919
1009
|
if (session.codexQueuedInput) {
|
|
920
1010
|
this.send(ws, {
|
|
921
1011
|
type: "rewind_result",
|
|
1012
|
+
sessionId,
|
|
922
1013
|
success: false,
|
|
923
1014
|
mode,
|
|
924
1015
|
error: "Cannot rewind while Codex has queued input",
|
|
@@ -930,6 +1021,7 @@ export class BridgeWebSocketServer {
|
|
|
930
1021
|
if (targetOrdinal === null || targetOrdinal > totalUserTurns) {
|
|
931
1022
|
this.send(ws, {
|
|
932
1023
|
type: "rewind_result",
|
|
1024
|
+
sessionId,
|
|
933
1025
|
success: false,
|
|
934
1026
|
mode,
|
|
935
1027
|
error: "Invalid Codex rewind target",
|
|
@@ -940,6 +1032,7 @@ export class BridgeWebSocketServer {
|
|
|
940
1032
|
if (numTurns <= 0) {
|
|
941
1033
|
this.send(ws, {
|
|
942
1034
|
type: "rewind_result",
|
|
1035
|
+
sessionId,
|
|
943
1036
|
success: false,
|
|
944
1037
|
mode,
|
|
945
1038
|
error: "Invalid Codex rewind target",
|
|
@@ -950,6 +1043,7 @@ export class BridgeWebSocketServer {
|
|
|
950
1043
|
if (!threadId) {
|
|
951
1044
|
this.send(ws, {
|
|
952
1045
|
type: "rewind_result",
|
|
1046
|
+
sessionId,
|
|
953
1047
|
success: false,
|
|
954
1048
|
mode,
|
|
955
1049
|
error: "No Codex thread ID available for rewind",
|
|
@@ -978,6 +1072,7 @@ export class BridgeWebSocketServer {
|
|
|
978
1072
|
const newSession = this.sessionManager.get(newSessionId);
|
|
979
1073
|
this.send(ws, {
|
|
980
1074
|
type: "rewind_result",
|
|
1075
|
+
sessionId,
|
|
981
1076
|
success: true,
|
|
982
1077
|
mode,
|
|
983
1078
|
});
|
|
@@ -1811,7 +1906,11 @@ export class BridgeWebSocketServer {
|
|
|
1811
1906
|
case "start": {
|
|
1812
1907
|
const projectPath = resolvePlatformPath(msg.projectPath, this.platform);
|
|
1813
1908
|
if (!this.isPathAllowed(projectPath)) {
|
|
1814
|
-
this.send(ws,
|
|
1909
|
+
this.send(ws, {
|
|
1910
|
+
...this.buildPathNotAllowedError(msg.projectPath),
|
|
1911
|
+
requestId: msg.requestId,
|
|
1912
|
+
path: msg.projectPath,
|
|
1913
|
+
});
|
|
1815
1914
|
break;
|
|
1816
1915
|
}
|
|
1817
1916
|
try {
|
|
@@ -1856,6 +1955,8 @@ export class BridgeWebSocketServer {
|
|
|
1856
1955
|
!(await this.validateCodexProfile(msg.profile, projectPath))) {
|
|
1857
1956
|
this.send(ws, {
|
|
1858
1957
|
type: "error",
|
|
1958
|
+
requestId: msg.requestId,
|
|
1959
|
+
path: projectPath,
|
|
1859
1960
|
message: `Codex profile not found: ${msg.profile}`,
|
|
1860
1961
|
});
|
|
1861
1962
|
break;
|
|
@@ -1865,7 +1966,11 @@ export class BridgeWebSocketServer {
|
|
|
1865
1966
|
? this.normalizeAdditionalWritableRoots(msg.additionalWritableRoots, projectPath)
|
|
1866
1967
|
: {};
|
|
1867
1968
|
if (additionalWritableRoots.deniedRoot) {
|
|
1868
|
-
this.send(ws,
|
|
1969
|
+
this.send(ws, {
|
|
1970
|
+
...this.buildPathNotAllowedError(additionalWritableRoots.deniedRoot),
|
|
1971
|
+
requestId: msg.requestId,
|
|
1972
|
+
path: projectPath,
|
|
1973
|
+
});
|
|
1869
1974
|
break;
|
|
1870
1975
|
}
|
|
1871
1976
|
const { sessionId, permissionMode: effectivePermissionMode, executionMode: effectiveExecutionMode, planMode: effectivePlanMode, usedFallback: autoFallbackUsed, } = provider === "claude"
|
|
@@ -1951,6 +2056,7 @@ export class BridgeWebSocketServer {
|
|
|
1951
2056
|
: msg.sandboxMode,
|
|
1952
2057
|
codexPermissionsMode: createdSession?.codexSettings?.codexPermissionsMode,
|
|
1953
2058
|
approvalsReviewer: createdSession?.codexSettings?.approvalsReviewer,
|
|
2059
|
+
requestId: msg.requestId,
|
|
1954
2060
|
...(cached
|
|
1955
2061
|
? {
|
|
1956
2062
|
slashCommands: cached.slashCommands,
|
|
@@ -2002,6 +2108,8 @@ export class BridgeWebSocketServer {
|
|
|
2002
2108
|
console.error(`[ws] Failed to start session:`, err);
|
|
2003
2109
|
this.send(ws, {
|
|
2004
2110
|
type: "error",
|
|
2111
|
+
requestId: msg.requestId,
|
|
2112
|
+
path: msg.projectPath,
|
|
2005
2113
|
message: `Failed to start session: ${err.message}`,
|
|
2006
2114
|
});
|
|
2007
2115
|
}
|
|
@@ -2019,6 +2127,7 @@ export class BridgeWebSocketServer {
|
|
|
2019
2127
|
}
|
|
2020
2128
|
this.send(ws, {
|
|
2021
2129
|
type: "error",
|
|
2130
|
+
sessionId: msg.sessionId,
|
|
2022
2131
|
message: "No active session. Send 'start' first.",
|
|
2023
2132
|
});
|
|
2024
2133
|
return;
|
|
@@ -2332,12 +2441,17 @@ export class BridgeWebSocketServer {
|
|
|
2332
2441
|
case "update_queued_input": {
|
|
2333
2442
|
const session = this.resolveSession(msg.sessionId);
|
|
2334
2443
|
if (!session || session.provider !== "codex") {
|
|
2335
|
-
this.send(ws, {
|
|
2444
|
+
this.send(ws, {
|
|
2445
|
+
type: "error",
|
|
2446
|
+
sessionId: msg.sessionId,
|
|
2447
|
+
message: "No active Codex session.",
|
|
2448
|
+
});
|
|
2336
2449
|
return;
|
|
2337
2450
|
}
|
|
2338
2451
|
if (!msg.text.trim()) {
|
|
2339
2452
|
this.send(ws, {
|
|
2340
2453
|
type: "error",
|
|
2454
|
+
sessionId: msg.sessionId,
|
|
2341
2455
|
message: "Queued message cannot be empty.",
|
|
2342
2456
|
});
|
|
2343
2457
|
return;
|
|
@@ -2346,6 +2460,7 @@ export class BridgeWebSocketServer {
|
|
|
2346
2460
|
if (!success) {
|
|
2347
2461
|
this.send(ws, {
|
|
2348
2462
|
type: "error",
|
|
2463
|
+
sessionId: msg.sessionId,
|
|
2349
2464
|
message: "Queued message not found.",
|
|
2350
2465
|
errorCode: "queued_input_not_found",
|
|
2351
2466
|
});
|
|
@@ -2357,13 +2472,18 @@ export class BridgeWebSocketServer {
|
|
|
2357
2472
|
case "cancel_queued_input": {
|
|
2358
2473
|
const session = this.resolveSession(msg.sessionId);
|
|
2359
2474
|
if (!session || session.provider !== "codex") {
|
|
2360
|
-
this.send(ws, {
|
|
2475
|
+
this.send(ws, {
|
|
2476
|
+
type: "error",
|
|
2477
|
+
sessionId: msg.sessionId,
|
|
2478
|
+
message: "No active Codex session.",
|
|
2479
|
+
});
|
|
2361
2480
|
return;
|
|
2362
2481
|
}
|
|
2363
2482
|
const success = this.sessionManager.cancelCodexQueuedInput(session.id, msg.itemId);
|
|
2364
2483
|
if (!success) {
|
|
2365
2484
|
this.send(ws, {
|
|
2366
2485
|
type: "error",
|
|
2486
|
+
sessionId: msg.sessionId,
|
|
2367
2487
|
message: "Queued message not found.",
|
|
2368
2488
|
errorCode: "queued_input_not_found",
|
|
2369
2489
|
});
|
|
@@ -2375,13 +2495,18 @@ export class BridgeWebSocketServer {
|
|
|
2375
2495
|
case "steer_queued_input": {
|
|
2376
2496
|
const session = this.resolveSession(msg.sessionId);
|
|
2377
2497
|
if (!session || session.provider !== "codex") {
|
|
2378
|
-
this.send(ws, {
|
|
2498
|
+
this.send(ws, {
|
|
2499
|
+
type: "error",
|
|
2500
|
+
sessionId: msg.sessionId,
|
|
2501
|
+
message: "No active Codex session.",
|
|
2502
|
+
});
|
|
2379
2503
|
return;
|
|
2380
2504
|
}
|
|
2381
2505
|
const result = await this.sessionManager.steerCodexQueuedInput(session.id, msg.itemId);
|
|
2382
2506
|
if (!result.ok) {
|
|
2383
2507
|
this.send(ws, {
|
|
2384
2508
|
type: "error",
|
|
2509
|
+
sessionId: msg.sessionId,
|
|
2385
2510
|
message: result.error,
|
|
2386
2511
|
errorCode: result.error === "Queued message not found."
|
|
2387
2512
|
? "queued_input_not_found"
|
|
@@ -2482,6 +2607,7 @@ export class BridgeWebSocketServer {
|
|
|
2482
2607
|
if (this.failSetPermissionMode) {
|
|
2483
2608
|
this.send(ws, {
|
|
2484
2609
|
type: "error",
|
|
2610
|
+
sessionId: msg.sessionId,
|
|
2485
2611
|
message: "Failed to set permission mode: forced test failure",
|
|
2486
2612
|
errorCode: "set_permission_mode_rejected",
|
|
2487
2613
|
});
|
|
@@ -2489,7 +2615,11 @@ export class BridgeWebSocketServer {
|
|
|
2489
2615
|
}
|
|
2490
2616
|
const session = this.resolveSession(msg.sessionId);
|
|
2491
2617
|
if (!session) {
|
|
2492
|
-
this.send(ws, {
|
|
2618
|
+
this.send(ws, {
|
|
2619
|
+
type: "error",
|
|
2620
|
+
sessionId: msg.sessionId,
|
|
2621
|
+
message: "No active session.",
|
|
2622
|
+
});
|
|
2493
2623
|
return;
|
|
2494
2624
|
}
|
|
2495
2625
|
if (session.provider === "codex") {
|
|
@@ -2739,6 +2869,7 @@ export class BridgeWebSocketServer {
|
|
|
2739
2869
|
.catch((err) => {
|
|
2740
2870
|
this.send(ws, {
|
|
2741
2871
|
type: "error",
|
|
2872
|
+
sessionId: msg.sessionId,
|
|
2742
2873
|
message: `Failed to restart session for permission mode change: ${err}`,
|
|
2743
2874
|
});
|
|
2744
2875
|
});
|
|
@@ -2751,6 +2882,7 @@ export class BridgeWebSocketServer {
|
|
|
2751
2882
|
isClaudeAutoModeUnavailableError(err)) {
|
|
2752
2883
|
this.send(ws, {
|
|
2753
2884
|
type: "error",
|
|
2885
|
+
sessionId: msg.sessionId,
|
|
2754
2886
|
message: "Auto mode is unavailable in this environment. Keeping the current permission mode.",
|
|
2755
2887
|
errorCode: "auto_mode_unavailable",
|
|
2756
2888
|
});
|
|
@@ -2758,6 +2890,7 @@ export class BridgeWebSocketServer {
|
|
|
2758
2890
|
}
|
|
2759
2891
|
this.send(ws, {
|
|
2760
2892
|
type: "error",
|
|
2893
|
+
sessionId: msg.sessionId,
|
|
2761
2894
|
message: `Failed to set permission mode: ${errorMessageOf(err)}`,
|
|
2762
2895
|
});
|
|
2763
2896
|
});
|
|
@@ -2766,12 +2899,17 @@ export class BridgeWebSocketServer {
|
|
|
2766
2899
|
case "set_codex_model": {
|
|
2767
2900
|
const session = this.resolveSession(msg.sessionId);
|
|
2768
2901
|
if (!session) {
|
|
2769
|
-
this.send(ws, {
|
|
2902
|
+
this.send(ws, {
|
|
2903
|
+
type: "error",
|
|
2904
|
+
sessionId: msg.sessionId,
|
|
2905
|
+
message: "No active session.",
|
|
2906
|
+
});
|
|
2770
2907
|
return;
|
|
2771
2908
|
}
|
|
2772
2909
|
if (session.provider !== "codex") {
|
|
2773
2910
|
this.send(ws, {
|
|
2774
2911
|
type: "error",
|
|
2912
|
+
sessionId: msg.sessionId,
|
|
2775
2913
|
message: "Model switching is only supported for Codex sessions.",
|
|
2776
2914
|
errorCode: "set_codex_model_unsupported",
|
|
2777
2915
|
});
|
|
@@ -2781,6 +2919,7 @@ export class BridgeWebSocketServer {
|
|
|
2781
2919
|
if (!model) {
|
|
2782
2920
|
this.send(ws, {
|
|
2783
2921
|
type: "error",
|
|
2922
|
+
sessionId: msg.sessionId,
|
|
2784
2923
|
message: `Invalid Codex model: ${msg.model}`,
|
|
2785
2924
|
errorCode: "set_codex_model_rejected",
|
|
2786
2925
|
});
|
|
@@ -2827,6 +2966,7 @@ export class BridgeWebSocketServer {
|
|
|
2827
2966
|
if (!session || session.provider !== "codex") {
|
|
2828
2967
|
this.send(ws, {
|
|
2829
2968
|
type: "error",
|
|
2969
|
+
sessionId: msg.sessionId,
|
|
2830
2970
|
message: "No active Codex session.",
|
|
2831
2971
|
errorCode: "set_codex_speed_unsupported",
|
|
2832
2972
|
});
|
|
@@ -2858,6 +2998,7 @@ export class BridgeWebSocketServer {
|
|
|
2858
2998
|
if (!session || session.provider !== "codex") {
|
|
2859
2999
|
this.send(ws, {
|
|
2860
3000
|
type: "error",
|
|
3001
|
+
sessionId: msg.sessionId,
|
|
2861
3002
|
message: "Goal lookup is only supported for active Codex sessions.",
|
|
2862
3003
|
errorCode: "goal_get_unsupported",
|
|
2863
3004
|
});
|
|
@@ -2871,6 +3012,7 @@ export class BridgeWebSocketServer {
|
|
|
2871
3012
|
catch (err) {
|
|
2872
3013
|
this.send(ws, {
|
|
2873
3014
|
type: "error",
|
|
3015
|
+
sessionId: msg.sessionId,
|
|
2874
3016
|
message: `Failed to get goal: ${errorMessageOf(err)}`,
|
|
2875
3017
|
errorCode: "goal_get_failed",
|
|
2876
3018
|
});
|
|
@@ -2882,6 +3024,7 @@ export class BridgeWebSocketServer {
|
|
|
2882
3024
|
if (!session || session.provider !== "codex") {
|
|
2883
3025
|
this.send(ws, {
|
|
2884
3026
|
type: "error",
|
|
3027
|
+
sessionId: msg.sessionId,
|
|
2885
3028
|
message: "Goal updates are only supported for active Codex sessions.",
|
|
2886
3029
|
errorCode: "goal_set_unsupported",
|
|
2887
3030
|
});
|
|
@@ -2903,6 +3046,7 @@ export class BridgeWebSocketServer {
|
|
|
2903
3046
|
catch (err) {
|
|
2904
3047
|
this.send(ws, {
|
|
2905
3048
|
type: "error",
|
|
3049
|
+
sessionId: msg.sessionId,
|
|
2906
3050
|
message: `Failed to set goal: ${errorMessageOf(err)}`,
|
|
2907
3051
|
errorCode: "goal_set_failed",
|
|
2908
3052
|
});
|
|
@@ -2914,6 +3058,7 @@ export class BridgeWebSocketServer {
|
|
|
2914
3058
|
if (!session || session.provider !== "codex") {
|
|
2915
3059
|
this.send(ws, {
|
|
2916
3060
|
type: "error",
|
|
3061
|
+
sessionId: msg.sessionId,
|
|
2917
3062
|
message: "Goal clearing is only supported for active Codex sessions.",
|
|
2918
3063
|
errorCode: "goal_clear_unsupported",
|
|
2919
3064
|
});
|
|
@@ -2930,6 +3075,7 @@ export class BridgeWebSocketServer {
|
|
|
2930
3075
|
catch (err) {
|
|
2931
3076
|
this.send(ws, {
|
|
2932
3077
|
type: "error",
|
|
3078
|
+
sessionId: msg.sessionId,
|
|
2933
3079
|
message: `Failed to clear goal: ${errorMessageOf(err)}`,
|
|
2934
3080
|
errorCode: "goal_clear_failed",
|
|
2935
3081
|
});
|
|
@@ -2940,6 +3086,7 @@ export class BridgeWebSocketServer {
|
|
|
2940
3086
|
if (this.failSetSandboxMode) {
|
|
2941
3087
|
this.send(ws, {
|
|
2942
3088
|
type: "error",
|
|
3089
|
+
sessionId: msg.sessionId,
|
|
2943
3090
|
message: "Failed to set sandbox mode: forced test failure",
|
|
2944
3091
|
errorCode: "set_sandbox_mode_rejected",
|
|
2945
3092
|
});
|
|
@@ -2947,12 +3094,17 @@ export class BridgeWebSocketServer {
|
|
|
2947
3094
|
}
|
|
2948
3095
|
const session = this.resolveSession(msg.sessionId);
|
|
2949
3096
|
if (!session) {
|
|
2950
|
-
this.send(ws, {
|
|
3097
|
+
this.send(ws, {
|
|
3098
|
+
type: "error",
|
|
3099
|
+
sessionId: msg.sessionId,
|
|
3100
|
+
message: "No active session.",
|
|
3101
|
+
});
|
|
2951
3102
|
return;
|
|
2952
3103
|
}
|
|
2953
3104
|
if (msg.sandboxMode !== "on" && msg.sandboxMode !== "off") {
|
|
2954
3105
|
this.send(ws, {
|
|
2955
3106
|
type: "error",
|
|
3107
|
+
sessionId: msg.sessionId,
|
|
2956
3108
|
message: `Invalid sandbox mode: ${msg.sandboxMode}`,
|
|
2957
3109
|
});
|
|
2958
3110
|
return;
|
|
@@ -3140,6 +3292,7 @@ export class BridgeWebSocketServer {
|
|
|
3140
3292
|
.catch((err) => {
|
|
3141
3293
|
this.send(ws, {
|
|
3142
3294
|
type: "error",
|
|
3295
|
+
sessionId: msg.sessionId,
|
|
3143
3296
|
message: `Failed to restart session for sandbox mode change: ${err}`,
|
|
3144
3297
|
});
|
|
3145
3298
|
});
|
|
@@ -3311,6 +3464,7 @@ export class BridgeWebSocketServer {
|
|
|
3311
3464
|
else {
|
|
3312
3465
|
this.send(ws, {
|
|
3313
3466
|
type: "error",
|
|
3467
|
+
sessionId: msg.sessionId,
|
|
3314
3468
|
message: `Session ${msg.sessionId} not found`,
|
|
3315
3469
|
});
|
|
3316
3470
|
}
|
|
@@ -3420,6 +3574,7 @@ export class BridgeWebSocketServer {
|
|
|
3420
3574
|
if (!result) {
|
|
3421
3575
|
this.send(ws, {
|
|
3422
3576
|
type: "error",
|
|
3577
|
+
sessionId: msg.sessionId,
|
|
3423
3578
|
message: `Session ${msg.sessionId} not found`,
|
|
3424
3579
|
});
|
|
3425
3580
|
break;
|
|
@@ -3447,6 +3602,7 @@ export class BridgeWebSocketServer {
|
|
|
3447
3602
|
if (!result) {
|
|
3448
3603
|
this.send(ws, {
|
|
3449
3604
|
type: "error",
|
|
3605
|
+
sessionId: msg.sessionId,
|
|
3450
3606
|
message: `Session ${msg.sessionId} not found`,
|
|
3451
3607
|
});
|
|
3452
3608
|
break;
|
|
@@ -3477,6 +3633,7 @@ export class BridgeWebSocketServer {
|
|
|
3477
3633
|
else {
|
|
3478
3634
|
this.send(ws, {
|
|
3479
3635
|
type: "error",
|
|
3636
|
+
sessionId: msg.sessionId,
|
|
3480
3637
|
message: `Session ${msg.sessionId} not found`,
|
|
3481
3638
|
});
|
|
3482
3639
|
}
|
|
@@ -3507,6 +3664,7 @@ export class BridgeWebSocketServer {
|
|
|
3507
3664
|
else {
|
|
3508
3665
|
this.send(ws, {
|
|
3509
3666
|
type: "error",
|
|
3667
|
+
sessionId: msg.sessionId,
|
|
3510
3668
|
message: `Session ${msg.sessionId} not found`,
|
|
3511
3669
|
});
|
|
3512
3670
|
}
|
|
@@ -3517,6 +3675,7 @@ export class BridgeWebSocketServer {
|
|
|
3517
3675
|
if (!session) {
|
|
3518
3676
|
this.send(ws, {
|
|
3519
3677
|
type: "error",
|
|
3678
|
+
sessionId: msg.sessionId,
|
|
3520
3679
|
message: `Session ${msg.sessionId} not found`,
|
|
3521
3680
|
});
|
|
3522
3681
|
return;
|
|
@@ -4091,7 +4250,11 @@ export class BridgeWebSocketServer {
|
|
|
4091
4250
|
case "interrupt": {
|
|
4092
4251
|
const session = this.resolveSession(msg.sessionId);
|
|
4093
4252
|
if (!session) {
|
|
4094
|
-
this.send(ws, {
|
|
4253
|
+
this.send(ws, {
|
|
4254
|
+
type: "error",
|
|
4255
|
+
sessionId: msg.sessionId,
|
|
4256
|
+
message: "No active session.",
|
|
4257
|
+
});
|
|
4095
4258
|
return;
|
|
4096
4259
|
}
|
|
4097
4260
|
session.process.interrupt();
|
|
@@ -4136,13 +4299,30 @@ export class BridgeWebSocketServer {
|
|
|
4136
4299
|
void this.prepareFileDownload(ws, msg);
|
|
4137
4300
|
break;
|
|
4138
4301
|
}
|
|
4302
|
+
case "prepare_file_upload": {
|
|
4303
|
+
void this.prepareFileUpload(ws, msg);
|
|
4304
|
+
break;
|
|
4305
|
+
}
|
|
4306
|
+
case "finalize_file_upload": {
|
|
4307
|
+
void this.finalizeFileUpload(ws, msg);
|
|
4308
|
+
break;
|
|
4309
|
+
}
|
|
4310
|
+
case "cancel_file_upload": {
|
|
4311
|
+
if (this.uploadStore)
|
|
4312
|
+
void this.uploadStore.cancel(msg.uploadToken);
|
|
4313
|
+
break;
|
|
4314
|
+
}
|
|
4139
4315
|
case "read_file":
|
|
4140
4316
|
case "read_media_file": {
|
|
4317
|
+
const responseMetadata = {
|
|
4318
|
+
...projectRequestMetadata(msg),
|
|
4319
|
+
filePath: msg.filePath,
|
|
4320
|
+
};
|
|
4141
4321
|
const absPath = resolve(msg.projectPath, msg.filePath);
|
|
4142
4322
|
if (!this.isPathAllowed(absPath)) {
|
|
4143
4323
|
this.send(ws, {
|
|
4144
4324
|
type: "file_content",
|
|
4145
|
-
|
|
4325
|
+
...responseMetadata,
|
|
4146
4326
|
content: "",
|
|
4147
4327
|
error: "Path not allowed",
|
|
4148
4328
|
});
|
|
@@ -4153,7 +4333,7 @@ export class BridgeWebSocketServer {
|
|
|
4153
4333
|
if (!existsSync(absPath)) {
|
|
4154
4334
|
this.send(ws, {
|
|
4155
4335
|
type: "file_content",
|
|
4156
|
-
|
|
4336
|
+
...responseMetadata,
|
|
4157
4337
|
content: "",
|
|
4158
4338
|
error: "File not found",
|
|
4159
4339
|
});
|
|
@@ -4175,7 +4355,7 @@ export class BridgeWebSocketServer {
|
|
|
4175
4355
|
catch {
|
|
4176
4356
|
this.send(ws, {
|
|
4177
4357
|
type: "file_content",
|
|
4178
|
-
|
|
4358
|
+
...responseMetadata,
|
|
4179
4359
|
content: "",
|
|
4180
4360
|
error: targetPath.length > 0
|
|
4181
4361
|
? `This symbolic link points to a missing target: ${targetPath}`
|
|
@@ -4186,7 +4366,7 @@ export class BridgeWebSocketServer {
|
|
|
4186
4366
|
if (resolvedTargetStat.isDirectory()) {
|
|
4187
4367
|
this.send(ws, {
|
|
4188
4368
|
type: "file_content",
|
|
4189
|
-
|
|
4369
|
+
...responseMetadata,
|
|
4190
4370
|
content: "",
|
|
4191
4371
|
error: targetPath.length > 0
|
|
4192
4372
|
? `This symbolic link points to a directory (${targetPath}). Open the target directory instead.`
|
|
@@ -4198,7 +4378,7 @@ export class BridgeWebSocketServer {
|
|
|
4198
4378
|
else if (fileStat.isDirectory()) {
|
|
4199
4379
|
this.send(ws, {
|
|
4200
4380
|
type: "file_content",
|
|
4201
|
-
|
|
4381
|
+
...responseMetadata,
|
|
4202
4382
|
content: "",
|
|
4203
4383
|
error: "This path is a directory. Open a file instead.",
|
|
4204
4384
|
});
|
|
@@ -4211,7 +4391,7 @@ export class BridgeWebSocketServer {
|
|
|
4211
4391
|
if (!(await this.isCanonicalPathAllowed(canonicalPath))) {
|
|
4212
4392
|
this.send(ws, {
|
|
4213
4393
|
type: "file_content",
|
|
4214
|
-
|
|
4394
|
+
...responseMetadata,
|
|
4215
4395
|
content: "",
|
|
4216
4396
|
error: "Path not allowed",
|
|
4217
4397
|
});
|
|
@@ -4223,7 +4403,7 @@ export class BridgeWebSocketServer {
|
|
|
4223
4403
|
if (!this.mediaStore) {
|
|
4224
4404
|
this.send(ws, {
|
|
4225
4405
|
type: "file_content",
|
|
4226
|
-
|
|
4406
|
+
...responseMetadata,
|
|
4227
4407
|
kind: mediaType.kind,
|
|
4228
4408
|
content: "",
|
|
4229
4409
|
mimeType: mediaType.mimeType,
|
|
@@ -4235,7 +4415,7 @@ export class BridgeWebSocketServer {
|
|
|
4235
4415
|
const ref = await this.mediaStore.register(canonicalPath, mediaType.mimeType, resolvedFileStat.size);
|
|
4236
4416
|
this.send(ws, {
|
|
4237
4417
|
type: "file_content",
|
|
4238
|
-
|
|
4418
|
+
...responseMetadata,
|
|
4239
4419
|
kind: mediaType.kind,
|
|
4240
4420
|
content: "",
|
|
4241
4421
|
mimeType: ref.mimeType,
|
|
@@ -4247,7 +4427,7 @@ export class BridgeWebSocketServer {
|
|
|
4247
4427
|
if (msg.type === "read_media_file") {
|
|
4248
4428
|
this.send(ws, {
|
|
4249
4429
|
type: "file_content",
|
|
4250
|
-
|
|
4430
|
+
...responseMetadata,
|
|
4251
4431
|
content: "",
|
|
4252
4432
|
error: "Unsupported media file type.",
|
|
4253
4433
|
});
|
|
@@ -4258,7 +4438,7 @@ export class BridgeWebSocketServer {
|
|
|
4258
4438
|
if (resolvedFileStat.size > BridgeWebSocketServer.MAX_IMAGE_SIZE) {
|
|
4259
4439
|
this.send(ws, {
|
|
4260
4440
|
type: "file_content",
|
|
4261
|
-
|
|
4441
|
+
...responseMetadata,
|
|
4262
4442
|
kind: "image",
|
|
4263
4443
|
content: "",
|
|
4264
4444
|
mimeType,
|
|
@@ -4270,7 +4450,7 @@ export class BridgeWebSocketServer {
|
|
|
4270
4450
|
const buf = await readFile(absPath);
|
|
4271
4451
|
this.send(ws, {
|
|
4272
4452
|
type: "file_content",
|
|
4273
|
-
|
|
4453
|
+
...responseMetadata,
|
|
4274
4454
|
kind: "image",
|
|
4275
4455
|
content: "",
|
|
4276
4456
|
base64: buf.toString("base64"),
|
|
@@ -4326,7 +4506,7 @@ export class BridgeWebSocketServer {
|
|
|
4326
4506
|
: raw;
|
|
4327
4507
|
this.send(ws, {
|
|
4328
4508
|
type: "file_content",
|
|
4329
|
-
|
|
4509
|
+
...responseMetadata,
|
|
4330
4510
|
kind: "text",
|
|
4331
4511
|
content,
|
|
4332
4512
|
language,
|
|
@@ -4337,7 +4517,7 @@ export class BridgeWebSocketServer {
|
|
|
4337
4517
|
catch (err) {
|
|
4338
4518
|
this.send(ws, {
|
|
4339
4519
|
type: "file_content",
|
|
4340
|
-
|
|
4520
|
+
...responseMetadata,
|
|
4341
4521
|
content: "",
|
|
4342
4522
|
error: `Failed to read file: ${err}`,
|
|
4343
4523
|
});
|
|
@@ -4347,7 +4527,12 @@ export class BridgeWebSocketServer {
|
|
|
4347
4527
|
}
|
|
4348
4528
|
case "list_files": {
|
|
4349
4529
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4350
|
-
this.send(ws,
|
|
4530
|
+
this.send(ws, {
|
|
4531
|
+
type: "file_list",
|
|
4532
|
+
...projectRequestMetadata(msg),
|
|
4533
|
+
files: [],
|
|
4534
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4535
|
+
});
|
|
4351
4536
|
break;
|
|
4352
4537
|
}
|
|
4353
4538
|
void (async () => {
|
|
@@ -4358,6 +4543,7 @@ export class BridgeWebSocketServer {
|
|
|
4358
4543
|
});
|
|
4359
4544
|
this.send(ws, {
|
|
4360
4545
|
type: "file_list",
|
|
4546
|
+
...projectRequestMetadata(msg),
|
|
4361
4547
|
files: result.files,
|
|
4362
4548
|
totalFiles: result.totalFiles,
|
|
4363
4549
|
truncated: result.truncated,
|
|
@@ -4366,8 +4552,10 @@ export class BridgeWebSocketServer {
|
|
|
4366
4552
|
catch (err) {
|
|
4367
4553
|
const message = err instanceof Error ? err.message : String(err);
|
|
4368
4554
|
this.send(ws, {
|
|
4369
|
-
type: "
|
|
4370
|
-
|
|
4555
|
+
type: "file_list",
|
|
4556
|
+
...projectRequestMetadata(msg),
|
|
4557
|
+
files: [],
|
|
4558
|
+
error: `Failed to list files: ${message}`,
|
|
4371
4559
|
});
|
|
4372
4560
|
}
|
|
4373
4561
|
})();
|
|
@@ -4457,7 +4645,15 @@ export class BridgeWebSocketServer {
|
|
|
4457
4645
|
}
|
|
4458
4646
|
case "get_diff": {
|
|
4459
4647
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4460
|
-
this.send(ws,
|
|
4648
|
+
this.send(ws, {
|
|
4649
|
+
type: "diff_result",
|
|
4650
|
+
...projectRequestMetadata(msg),
|
|
4651
|
+
staged: msg.staged === true,
|
|
4652
|
+
diff: "",
|
|
4653
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId)
|
|
4654
|
+
.message,
|
|
4655
|
+
errorCode: "path_not_allowed",
|
|
4656
|
+
});
|
|
4461
4657
|
break;
|
|
4462
4658
|
}
|
|
4463
4659
|
this.collectGitDiff(msg.projectPath, ({ diff, error }) => {
|
|
@@ -4465,6 +4661,8 @@ export class BridgeWebSocketServer {
|
|
|
4465
4661
|
if (/not a git repository/i.test(error)) {
|
|
4466
4662
|
this.send(ws, {
|
|
4467
4663
|
type: "diff_result",
|
|
4664
|
+
...projectRequestMetadata(msg),
|
|
4665
|
+
staged: msg.staged === true,
|
|
4468
4666
|
diff: "",
|
|
4469
4667
|
error: "This project is not a git repository",
|
|
4470
4668
|
errorCode: "git_not_available",
|
|
@@ -4473,6 +4671,8 @@ export class BridgeWebSocketServer {
|
|
|
4473
4671
|
else {
|
|
4474
4672
|
this.send(ws, {
|
|
4475
4673
|
type: "diff_result",
|
|
4674
|
+
...projectRequestMetadata(msg),
|
|
4675
|
+
staged: msg.staged === true,
|
|
4476
4676
|
diff: "",
|
|
4477
4677
|
error: `Failed to get diff: ${error}`,
|
|
4478
4678
|
});
|
|
@@ -4481,10 +4681,21 @@ export class BridgeWebSocketServer {
|
|
|
4481
4681
|
}
|
|
4482
4682
|
void this.collectImageChanges(msg.projectPath, diff).then((imageChanges) => {
|
|
4483
4683
|
if (imageChanges.length > 0) {
|
|
4484
|
-
this.send(ws, {
|
|
4684
|
+
this.send(ws, {
|
|
4685
|
+
type: "diff_result",
|
|
4686
|
+
...projectRequestMetadata(msg),
|
|
4687
|
+
staged: msg.staged === true,
|
|
4688
|
+
diff,
|
|
4689
|
+
imageChanges,
|
|
4690
|
+
});
|
|
4485
4691
|
}
|
|
4486
4692
|
else {
|
|
4487
|
-
this.send(ws, {
|
|
4693
|
+
this.send(ws, {
|
|
4694
|
+
type: "diff_result",
|
|
4695
|
+
...projectRequestMetadata(msg),
|
|
4696
|
+
staged: msg.staged === true,
|
|
4697
|
+
diff,
|
|
4698
|
+
});
|
|
4488
4699
|
}
|
|
4489
4700
|
});
|
|
4490
4701
|
}, msg.staged === true
|
|
@@ -4497,7 +4708,13 @@ export class BridgeWebSocketServer {
|
|
|
4497
4708
|
case "get_diff_image": {
|
|
4498
4709
|
if (!this.isPathAllowed(msg.projectPath) ||
|
|
4499
4710
|
!this.isPathAllowed(resolve(msg.projectPath, msg.filePath))) {
|
|
4500
|
-
this.send(ws, {
|
|
4711
|
+
this.send(ws, {
|
|
4712
|
+
type: "diff_image_result",
|
|
4713
|
+
...projectRequestMetadata(msg),
|
|
4714
|
+
filePath: msg.filePath,
|
|
4715
|
+
version: msg.version,
|
|
4716
|
+
error: "Path not allowed",
|
|
4717
|
+
});
|
|
4501
4718
|
break;
|
|
4502
4719
|
}
|
|
4503
4720
|
if (msg.version === "both") {
|
|
@@ -4510,6 +4727,7 @@ export class BridgeWebSocketServer {
|
|
|
4510
4727
|
const errors = [oldResult.error, newResult.error].filter(Boolean);
|
|
4511
4728
|
this.send(ws, {
|
|
4512
4729
|
type: "diff_image_result",
|
|
4730
|
+
...projectRequestMetadata(msg),
|
|
4513
4731
|
filePath: msg.filePath,
|
|
4514
4732
|
version: "both",
|
|
4515
4733
|
oldBase64: oldResult.base64,
|
|
@@ -4530,6 +4748,7 @@ export class BridgeWebSocketServer {
|
|
|
4530
4748
|
const result = await this.loadDiffImageAsync(msg.projectPath, msg.filePath, version);
|
|
4531
4749
|
this.send(ws, {
|
|
4532
4750
|
type: "diff_image_result",
|
|
4751
|
+
...projectRequestMetadata(msg),
|
|
4533
4752
|
filePath: msg.filePath,
|
|
4534
4753
|
version,
|
|
4535
4754
|
...result,
|
|
@@ -4544,25 +4763,42 @@ export class BridgeWebSocketServer {
|
|
|
4544
4763
|
}
|
|
4545
4764
|
case "list_worktrees": {
|
|
4546
4765
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4547
|
-
this.send(ws,
|
|
4766
|
+
this.send(ws, {
|
|
4767
|
+
type: "worktree_list",
|
|
4768
|
+
...projectRequestMetadata(msg),
|
|
4769
|
+
worktrees: [],
|
|
4770
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4771
|
+
});
|
|
4548
4772
|
break;
|
|
4549
4773
|
}
|
|
4550
4774
|
try {
|
|
4551
4775
|
const worktrees = listWorktrees(msg.projectPath);
|
|
4552
4776
|
const mainBranch = getMainBranch(msg.projectPath);
|
|
4553
|
-
this.send(ws, {
|
|
4777
|
+
this.send(ws, {
|
|
4778
|
+
type: "worktree_list",
|
|
4779
|
+
...projectRequestMetadata(msg),
|
|
4780
|
+
worktrees,
|
|
4781
|
+
mainBranch,
|
|
4782
|
+
});
|
|
4554
4783
|
}
|
|
4555
4784
|
catch (err) {
|
|
4556
4785
|
this.send(ws, {
|
|
4557
|
-
type: "
|
|
4558
|
-
|
|
4786
|
+
type: "worktree_list",
|
|
4787
|
+
...projectRequestMetadata(msg),
|
|
4788
|
+
worktrees: [],
|
|
4789
|
+
error: `Failed to list worktrees: ${err}`,
|
|
4559
4790
|
});
|
|
4560
4791
|
}
|
|
4561
4792
|
break;
|
|
4562
4793
|
}
|
|
4563
4794
|
case "remove_worktree": {
|
|
4564
4795
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4565
|
-
this.send(ws,
|
|
4796
|
+
this.send(ws, {
|
|
4797
|
+
type: "worktree_removed",
|
|
4798
|
+
...projectRequestMetadata(msg),
|
|
4799
|
+
worktreePath: msg.worktreePath,
|
|
4800
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4801
|
+
});
|
|
4566
4802
|
break;
|
|
4567
4803
|
}
|
|
4568
4804
|
try {
|
|
@@ -4570,13 +4806,16 @@ export class BridgeWebSocketServer {
|
|
|
4570
4806
|
this.worktreeStore.deleteByWorktreePath(msg.worktreePath);
|
|
4571
4807
|
this.send(ws, {
|
|
4572
4808
|
type: "worktree_removed",
|
|
4809
|
+
...projectRequestMetadata(msg),
|
|
4573
4810
|
worktreePath: msg.worktreePath,
|
|
4574
4811
|
});
|
|
4575
4812
|
}
|
|
4576
4813
|
catch (err) {
|
|
4577
4814
|
this.send(ws, {
|
|
4578
|
-
type: "
|
|
4579
|
-
|
|
4815
|
+
type: "worktree_removed",
|
|
4816
|
+
...projectRequestMetadata(msg),
|
|
4817
|
+
worktreePath: msg.worktreePath,
|
|
4818
|
+
error: `Failed to remove worktree: ${err}`,
|
|
4580
4819
|
});
|
|
4581
4820
|
}
|
|
4582
4821
|
break;
|
|
@@ -4584,7 +4823,12 @@ export class BridgeWebSocketServer {
|
|
|
4584
4823
|
// ---- Git Operations (Phase 1-3) ----
|
|
4585
4824
|
case "git_stage": {
|
|
4586
4825
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4587
|
-
this.send(ws,
|
|
4826
|
+
this.send(ws, {
|
|
4827
|
+
type: "git_stage_result",
|
|
4828
|
+
...projectRequestMetadata(msg),
|
|
4829
|
+
success: false,
|
|
4830
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4831
|
+
});
|
|
4588
4832
|
break;
|
|
4589
4833
|
}
|
|
4590
4834
|
try {
|
|
@@ -4592,11 +4836,16 @@ export class BridgeWebSocketServer {
|
|
|
4592
4836
|
stageFiles(msg.projectPath, msg.files);
|
|
4593
4837
|
if (msg.hunks?.length)
|
|
4594
4838
|
stageHunks(msg.projectPath, msg.hunks);
|
|
4595
|
-
this.send(ws, {
|
|
4839
|
+
this.send(ws, {
|
|
4840
|
+
type: "git_stage_result",
|
|
4841
|
+
...projectRequestMetadata(msg),
|
|
4842
|
+
success: true,
|
|
4843
|
+
});
|
|
4596
4844
|
}
|
|
4597
4845
|
catch (err) {
|
|
4598
4846
|
this.send(ws, {
|
|
4599
4847
|
type: "git_stage_result",
|
|
4848
|
+
...projectRequestMetadata(msg),
|
|
4600
4849
|
success: false,
|
|
4601
4850
|
error: String(err),
|
|
4602
4851
|
});
|
|
@@ -4605,16 +4854,26 @@ export class BridgeWebSocketServer {
|
|
|
4605
4854
|
}
|
|
4606
4855
|
case "git_unstage": {
|
|
4607
4856
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4608
|
-
this.send(ws,
|
|
4857
|
+
this.send(ws, {
|
|
4858
|
+
type: "git_unstage_result",
|
|
4859
|
+
...projectRequestMetadata(msg),
|
|
4860
|
+
success: false,
|
|
4861
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4862
|
+
});
|
|
4609
4863
|
break;
|
|
4610
4864
|
}
|
|
4611
4865
|
try {
|
|
4612
4866
|
unstageFiles(msg.projectPath, msg.files ?? []);
|
|
4613
|
-
this.send(ws, {
|
|
4867
|
+
this.send(ws, {
|
|
4868
|
+
type: "git_unstage_result",
|
|
4869
|
+
...projectRequestMetadata(msg),
|
|
4870
|
+
success: true,
|
|
4871
|
+
});
|
|
4614
4872
|
}
|
|
4615
4873
|
catch (err) {
|
|
4616
4874
|
this.send(ws, {
|
|
4617
4875
|
type: "git_unstage_result",
|
|
4876
|
+
...projectRequestMetadata(msg),
|
|
4618
4877
|
success: false,
|
|
4619
4878
|
error: String(err),
|
|
4620
4879
|
});
|
|
@@ -4623,16 +4882,26 @@ export class BridgeWebSocketServer {
|
|
|
4623
4882
|
}
|
|
4624
4883
|
case "git_unstage_hunks": {
|
|
4625
4884
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4626
|
-
this.send(ws,
|
|
4885
|
+
this.send(ws, {
|
|
4886
|
+
type: "git_unstage_hunks_result",
|
|
4887
|
+
...projectRequestMetadata(msg),
|
|
4888
|
+
success: false,
|
|
4889
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4890
|
+
});
|
|
4627
4891
|
break;
|
|
4628
4892
|
}
|
|
4629
4893
|
try {
|
|
4630
4894
|
unstageHunks(msg.projectPath, msg.hunks);
|
|
4631
|
-
this.send(ws, {
|
|
4895
|
+
this.send(ws, {
|
|
4896
|
+
type: "git_unstage_hunks_result",
|
|
4897
|
+
...projectRequestMetadata(msg),
|
|
4898
|
+
success: true,
|
|
4899
|
+
});
|
|
4632
4900
|
}
|
|
4633
4901
|
catch (err) {
|
|
4634
4902
|
this.send(ws, {
|
|
4635
4903
|
type: "git_unstage_hunks_result",
|
|
4904
|
+
...projectRequestMetadata(msg),
|
|
4636
4905
|
success: false,
|
|
4637
4906
|
error: String(err),
|
|
4638
4907
|
});
|
|
@@ -4641,7 +4910,12 @@ export class BridgeWebSocketServer {
|
|
|
4641
4910
|
}
|
|
4642
4911
|
case "git_commit": {
|
|
4643
4912
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4644
|
-
this.send(ws,
|
|
4913
|
+
this.send(ws, {
|
|
4914
|
+
type: "git_commit_result",
|
|
4915
|
+
...projectRequestMetadata(msg),
|
|
4916
|
+
success: false,
|
|
4917
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4918
|
+
});
|
|
4645
4919
|
break;
|
|
4646
4920
|
}
|
|
4647
4921
|
const session = msg.sessionId
|
|
@@ -4675,6 +4949,7 @@ export class BridgeWebSocketServer {
|
|
|
4675
4949
|
const result = gitCommit(msg.projectPath, message);
|
|
4676
4950
|
this.send(ws, {
|
|
4677
4951
|
type: "git_commit_result",
|
|
4952
|
+
...projectRequestMetadata(msg),
|
|
4678
4953
|
success: true,
|
|
4679
4954
|
commitHash: result.hash,
|
|
4680
4955
|
message: result.message,
|
|
@@ -4683,6 +4958,7 @@ export class BridgeWebSocketServer {
|
|
|
4683
4958
|
catch (err) {
|
|
4684
4959
|
this.send(ws, {
|
|
4685
4960
|
type: "git_commit_result",
|
|
4961
|
+
...projectRequestMetadata(msg),
|
|
4686
4962
|
success: false,
|
|
4687
4963
|
error: err instanceof Error ? err.message : String(err),
|
|
4688
4964
|
});
|
|
@@ -4691,19 +4967,26 @@ export class BridgeWebSocketServer {
|
|
|
4691
4967
|
}
|
|
4692
4968
|
case "git_push": {
|
|
4693
4969
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4694
|
-
this.send(ws,
|
|
4970
|
+
this.send(ws, {
|
|
4971
|
+
type: "git_push_result",
|
|
4972
|
+
...projectRequestMetadata(msg),
|
|
4973
|
+
success: false,
|
|
4974
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
4975
|
+
});
|
|
4695
4976
|
break;
|
|
4696
4977
|
}
|
|
4697
4978
|
try {
|
|
4698
4979
|
gitPush(msg.projectPath);
|
|
4699
4980
|
this.send(ws, {
|
|
4700
4981
|
type: "git_push_result",
|
|
4982
|
+
...projectRequestMetadata(msg),
|
|
4701
4983
|
success: true,
|
|
4702
4984
|
});
|
|
4703
4985
|
}
|
|
4704
4986
|
catch (err) {
|
|
4705
4987
|
this.send(ws, {
|
|
4706
4988
|
type: "git_push_result",
|
|
4989
|
+
...projectRequestMetadata(msg),
|
|
4707
4990
|
success: false,
|
|
4708
4991
|
error: String(err),
|
|
4709
4992
|
});
|
|
@@ -4712,13 +4995,22 @@ export class BridgeWebSocketServer {
|
|
|
4712
4995
|
}
|
|
4713
4996
|
case "git_branches": {
|
|
4714
4997
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4715
|
-
this.send(ws,
|
|
4998
|
+
this.send(ws, {
|
|
4999
|
+
type: "git_branches_result",
|
|
5000
|
+
...projectRequestMetadata(msg),
|
|
5001
|
+
current: "",
|
|
5002
|
+
branches: [],
|
|
5003
|
+
checkedOutBranches: [],
|
|
5004
|
+
remoteStatusByBranch: {},
|
|
5005
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5006
|
+
});
|
|
4716
5007
|
break;
|
|
4717
5008
|
}
|
|
4718
5009
|
try {
|
|
4719
5010
|
const result = listBranches(msg.projectPath);
|
|
4720
5011
|
this.send(ws, {
|
|
4721
5012
|
type: "git_branches_result",
|
|
5013
|
+
...projectRequestMetadata(msg),
|
|
4722
5014
|
current: result.current,
|
|
4723
5015
|
branches: result.branches,
|
|
4724
5016
|
checkedOutBranches: result.checkedOutBranches,
|
|
@@ -4728,6 +5020,7 @@ export class BridgeWebSocketServer {
|
|
|
4728
5020
|
catch (err) {
|
|
4729
5021
|
this.send(ws, {
|
|
4730
5022
|
type: "git_branches_result",
|
|
5023
|
+
...projectRequestMetadata(msg),
|
|
4731
5024
|
current: "",
|
|
4732
5025
|
branches: [],
|
|
4733
5026
|
remoteStatusByBranch: {},
|
|
@@ -4738,16 +5031,26 @@ export class BridgeWebSocketServer {
|
|
|
4738
5031
|
}
|
|
4739
5032
|
case "git_create_branch": {
|
|
4740
5033
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4741
|
-
this.send(ws,
|
|
5034
|
+
this.send(ws, {
|
|
5035
|
+
type: "git_create_branch_result",
|
|
5036
|
+
...projectRequestMetadata(msg),
|
|
5037
|
+
success: false,
|
|
5038
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5039
|
+
});
|
|
4742
5040
|
break;
|
|
4743
5041
|
}
|
|
4744
5042
|
try {
|
|
4745
5043
|
createBranch(msg.projectPath, msg.name, msg.checkout);
|
|
4746
|
-
this.send(ws, {
|
|
5044
|
+
this.send(ws, {
|
|
5045
|
+
type: "git_create_branch_result",
|
|
5046
|
+
...projectRequestMetadata(msg),
|
|
5047
|
+
success: true,
|
|
5048
|
+
});
|
|
4747
5049
|
}
|
|
4748
5050
|
catch (err) {
|
|
4749
5051
|
this.send(ws, {
|
|
4750
5052
|
type: "git_create_branch_result",
|
|
5053
|
+
...projectRequestMetadata(msg),
|
|
4751
5054
|
success: false,
|
|
4752
5055
|
error: String(err),
|
|
4753
5056
|
});
|
|
@@ -4756,16 +5059,26 @@ export class BridgeWebSocketServer {
|
|
|
4756
5059
|
}
|
|
4757
5060
|
case "git_checkout_branch": {
|
|
4758
5061
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4759
|
-
this.send(ws,
|
|
5062
|
+
this.send(ws, {
|
|
5063
|
+
type: "git_checkout_branch_result",
|
|
5064
|
+
...projectRequestMetadata(msg),
|
|
5065
|
+
success: false,
|
|
5066
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5067
|
+
});
|
|
4760
5068
|
break;
|
|
4761
5069
|
}
|
|
4762
5070
|
try {
|
|
4763
5071
|
checkoutBranch(msg.projectPath, msg.branch);
|
|
4764
|
-
this.send(ws, {
|
|
5072
|
+
this.send(ws, {
|
|
5073
|
+
type: "git_checkout_branch_result",
|
|
5074
|
+
...projectRequestMetadata(msg),
|
|
5075
|
+
success: true,
|
|
5076
|
+
});
|
|
4765
5077
|
}
|
|
4766
5078
|
catch (err) {
|
|
4767
5079
|
this.send(ws, {
|
|
4768
5080
|
type: "git_checkout_branch_result",
|
|
5081
|
+
...projectRequestMetadata(msg),
|
|
4769
5082
|
success: false,
|
|
4770
5083
|
error: String(err),
|
|
4771
5084
|
});
|
|
@@ -4774,16 +5087,26 @@ export class BridgeWebSocketServer {
|
|
|
4774
5087
|
}
|
|
4775
5088
|
case "git_revert_file": {
|
|
4776
5089
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4777
|
-
this.send(ws,
|
|
5090
|
+
this.send(ws, {
|
|
5091
|
+
type: "git_revert_file_result",
|
|
5092
|
+
...projectRequestMetadata(msg),
|
|
5093
|
+
success: false,
|
|
5094
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5095
|
+
});
|
|
4778
5096
|
break;
|
|
4779
5097
|
}
|
|
4780
5098
|
try {
|
|
4781
5099
|
revertFiles(msg.projectPath, msg.files);
|
|
4782
|
-
this.send(ws, {
|
|
5100
|
+
this.send(ws, {
|
|
5101
|
+
type: "git_revert_file_result",
|
|
5102
|
+
...projectRequestMetadata(msg),
|
|
5103
|
+
success: true,
|
|
5104
|
+
});
|
|
4783
5105
|
}
|
|
4784
5106
|
catch (err) {
|
|
4785
5107
|
this.send(ws, {
|
|
4786
5108
|
type: "git_revert_file_result",
|
|
5109
|
+
...projectRequestMetadata(msg),
|
|
4787
5110
|
success: false,
|
|
4788
5111
|
error: String(err),
|
|
4789
5112
|
});
|
|
@@ -4792,16 +5115,26 @@ export class BridgeWebSocketServer {
|
|
|
4792
5115
|
}
|
|
4793
5116
|
case "git_revert_hunks": {
|
|
4794
5117
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4795
|
-
this.send(ws,
|
|
5118
|
+
this.send(ws, {
|
|
5119
|
+
type: "git_revert_hunks_result",
|
|
5120
|
+
...projectRequestMetadata(msg),
|
|
5121
|
+
success: false,
|
|
5122
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5123
|
+
});
|
|
4796
5124
|
break;
|
|
4797
5125
|
}
|
|
4798
5126
|
try {
|
|
4799
5127
|
revertHunks(msg.projectPath, msg.hunks);
|
|
4800
|
-
this.send(ws, {
|
|
5128
|
+
this.send(ws, {
|
|
5129
|
+
type: "git_revert_hunks_result",
|
|
5130
|
+
...projectRequestMetadata(msg),
|
|
5131
|
+
success: true,
|
|
5132
|
+
});
|
|
4801
5133
|
}
|
|
4802
5134
|
catch (err) {
|
|
4803
5135
|
this.send(ws, {
|
|
4804
5136
|
type: "git_revert_hunks_result",
|
|
5137
|
+
...projectRequestMetadata(msg),
|
|
4805
5138
|
success: false,
|
|
4806
5139
|
error: String(err),
|
|
4807
5140
|
});
|
|
@@ -4810,16 +5143,26 @@ export class BridgeWebSocketServer {
|
|
|
4810
5143
|
}
|
|
4811
5144
|
case "git_fetch": {
|
|
4812
5145
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4813
|
-
this.send(ws,
|
|
5146
|
+
this.send(ws, {
|
|
5147
|
+
type: "git_fetch_result",
|
|
5148
|
+
...projectRequestMetadata(msg),
|
|
5149
|
+
success: false,
|
|
5150
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5151
|
+
});
|
|
4814
5152
|
break;
|
|
4815
5153
|
}
|
|
4816
5154
|
try {
|
|
4817
5155
|
gitFetch(msg.projectPath);
|
|
4818
|
-
this.send(ws, {
|
|
5156
|
+
this.send(ws, {
|
|
5157
|
+
type: "git_fetch_result",
|
|
5158
|
+
...projectRequestMetadata(msg),
|
|
5159
|
+
success: true,
|
|
5160
|
+
});
|
|
4819
5161
|
}
|
|
4820
5162
|
catch (err) {
|
|
4821
5163
|
this.send(ws, {
|
|
4822
5164
|
type: "git_fetch_result",
|
|
5165
|
+
...projectRequestMetadata(msg),
|
|
4823
5166
|
success: false,
|
|
4824
5167
|
error: String(err),
|
|
4825
5168
|
});
|
|
@@ -4828,7 +5171,12 @@ export class BridgeWebSocketServer {
|
|
|
4828
5171
|
}
|
|
4829
5172
|
case "git_pull": {
|
|
4830
5173
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4831
|
-
this.send(ws,
|
|
5174
|
+
this.send(ws, {
|
|
5175
|
+
type: "git_pull_result",
|
|
5176
|
+
...projectRequestMetadata(msg),
|
|
5177
|
+
success: false,
|
|
5178
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5179
|
+
});
|
|
4832
5180
|
break;
|
|
4833
5181
|
}
|
|
4834
5182
|
try {
|
|
@@ -4836,6 +5184,7 @@ export class BridgeWebSocketServer {
|
|
|
4836
5184
|
if (result.success) {
|
|
4837
5185
|
this.send(ws, {
|
|
4838
5186
|
type: "git_pull_result",
|
|
5187
|
+
...projectRequestMetadata(msg),
|
|
4839
5188
|
success: true,
|
|
4840
5189
|
message: result.message,
|
|
4841
5190
|
});
|
|
@@ -4843,6 +5192,7 @@ export class BridgeWebSocketServer {
|
|
|
4843
5192
|
else {
|
|
4844
5193
|
this.send(ws, {
|
|
4845
5194
|
type: "git_pull_result",
|
|
5195
|
+
...projectRequestMetadata(msg),
|
|
4846
5196
|
success: false,
|
|
4847
5197
|
error: result.message,
|
|
4848
5198
|
});
|
|
@@ -4851,6 +5201,7 @@ export class BridgeWebSocketServer {
|
|
|
4851
5201
|
catch (err) {
|
|
4852
5202
|
this.send(ws, {
|
|
4853
5203
|
type: "git_pull_result",
|
|
5204
|
+
...projectRequestMetadata(msg),
|
|
4854
5205
|
success: false,
|
|
4855
5206
|
error: String(err),
|
|
4856
5207
|
});
|
|
@@ -4861,6 +5212,7 @@ export class BridgeWebSocketServer {
|
|
|
4861
5212
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4862
5213
|
this.send(ws, {
|
|
4863
5214
|
type: "git_status_result",
|
|
5215
|
+
requestId: msg.requestId,
|
|
4864
5216
|
sessionId: msg.sessionId,
|
|
4865
5217
|
projectPath: msg.projectPath,
|
|
4866
5218
|
hasUncommittedChanges: false,
|
|
@@ -4882,6 +5234,7 @@ export class BridgeWebSocketServer {
|
|
|
4882
5234
|
});
|
|
4883
5235
|
this.send(ws, {
|
|
4884
5236
|
type: "git_status_result",
|
|
5237
|
+
requestId: msg.requestId,
|
|
4885
5238
|
sessionId: msg.sessionId,
|
|
4886
5239
|
projectPath: msg.projectPath,
|
|
4887
5240
|
hasUncommittedChanges: result.hasUncommittedChanges,
|
|
@@ -4900,6 +5253,7 @@ export class BridgeWebSocketServer {
|
|
|
4900
5253
|
catch (err) {
|
|
4901
5254
|
this.send(ws, {
|
|
4902
5255
|
type: "git_status_result",
|
|
5256
|
+
requestId: msg.requestId,
|
|
4903
5257
|
sessionId: msg.sessionId,
|
|
4904
5258
|
projectPath: msg.projectPath,
|
|
4905
5259
|
hasUncommittedChanges: false,
|
|
@@ -4918,13 +5272,22 @@ export class BridgeWebSocketServer {
|
|
|
4918
5272
|
}
|
|
4919
5273
|
case "git_remote_status": {
|
|
4920
5274
|
if (!this.isPathAllowed(msg.projectPath)) {
|
|
4921
|
-
this.send(ws,
|
|
5275
|
+
this.send(ws, {
|
|
5276
|
+
type: "git_remote_status_result",
|
|
5277
|
+
...projectRequestMetadata(msg),
|
|
5278
|
+
ahead: 0,
|
|
5279
|
+
behind: 0,
|
|
5280
|
+
branch: "",
|
|
5281
|
+
hasUpstream: false,
|
|
5282
|
+
error: this.buildPathNotAllowedError(msg.projectPath, msg.requestId).message,
|
|
5283
|
+
});
|
|
4922
5284
|
break;
|
|
4923
5285
|
}
|
|
4924
5286
|
try {
|
|
4925
5287
|
const result = gitRemoteStatus(msg.projectPath);
|
|
4926
5288
|
this.send(ws, {
|
|
4927
5289
|
type: "git_remote_status_result",
|
|
5290
|
+
...projectRequestMetadata(msg),
|
|
4928
5291
|
ahead: result.ahead,
|
|
4929
5292
|
behind: result.behind,
|
|
4930
5293
|
branch: result.branch,
|
|
@@ -4934,6 +5297,7 @@ export class BridgeWebSocketServer {
|
|
|
4934
5297
|
catch (err) {
|
|
4935
5298
|
this.send(ws, {
|
|
4936
5299
|
type: "git_remote_status_result",
|
|
5300
|
+
...projectRequestMetadata(msg),
|
|
4937
5301
|
ahead: 0,
|
|
4938
5302
|
behind: 0,
|
|
4939
5303
|
branch: "",
|
|
@@ -4947,6 +5311,7 @@ export class BridgeWebSocketServer {
|
|
|
4947
5311
|
if (!session) {
|
|
4948
5312
|
this.send(ws, {
|
|
4949
5313
|
type: "rewind_preview",
|
|
5314
|
+
sessionId: msg.sessionId,
|
|
4950
5315
|
canRewind: false,
|
|
4951
5316
|
error: `Session ${msg.sessionId} not found`,
|
|
4952
5317
|
});
|
|
@@ -4955,6 +5320,7 @@ export class BridgeWebSocketServer {
|
|
|
4955
5320
|
if (session.provider === "codex") {
|
|
4956
5321
|
this.send(ws, {
|
|
4957
5322
|
type: "rewind_preview",
|
|
5323
|
+
sessionId: msg.sessionId,
|
|
4958
5324
|
canRewind: false,
|
|
4959
5325
|
error: "Codex rewind does not restore files",
|
|
4960
5326
|
});
|
|
@@ -4965,6 +5331,7 @@ export class BridgeWebSocketServer {
|
|
|
4965
5331
|
.then((result) => {
|
|
4966
5332
|
this.send(ws, {
|
|
4967
5333
|
type: "rewind_preview",
|
|
5334
|
+
sessionId: msg.sessionId,
|
|
4968
5335
|
canRewind: result.canRewind,
|
|
4969
5336
|
filesChanged: result.filesChanged,
|
|
4970
5337
|
insertions: result.insertions,
|
|
@@ -4975,6 +5342,7 @@ export class BridgeWebSocketServer {
|
|
|
4975
5342
|
.catch((err) => {
|
|
4976
5343
|
this.send(ws, {
|
|
4977
5344
|
type: "rewind_preview",
|
|
5345
|
+
sessionId: msg.sessionId,
|
|
4978
5346
|
canRewind: false,
|
|
4979
5347
|
error: `Dry run failed: ${err}`,
|
|
4980
5348
|
});
|
|
@@ -4986,6 +5354,7 @@ export class BridgeWebSocketServer {
|
|
|
4986
5354
|
if (!session) {
|
|
4987
5355
|
this.send(ws, {
|
|
4988
5356
|
type: "rewind_result",
|
|
5357
|
+
sessionId: msg.sessionId,
|
|
4989
5358
|
success: false,
|
|
4990
5359
|
mode: msg.mode,
|
|
4991
5360
|
error: `Session ${msg.sessionId} not found`,
|
|
@@ -4996,6 +5365,7 @@ export class BridgeWebSocketServer {
|
|
|
4996
5365
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4997
5366
|
this.send(ws, {
|
|
4998
5367
|
type: "rewind_result",
|
|
5368
|
+
sessionId: msg.sessionId,
|
|
4999
5369
|
success: false,
|
|
5000
5370
|
mode: msg.mode,
|
|
5001
5371
|
error: errMsg,
|
|
@@ -5014,6 +5384,7 @@ export class BridgeWebSocketServer {
|
|
|
5014
5384
|
if (result.canRewind) {
|
|
5015
5385
|
this.send(ws, {
|
|
5016
5386
|
type: "rewind_result",
|
|
5387
|
+
sessionId: msg.sessionId,
|
|
5017
5388
|
success: true,
|
|
5018
5389
|
mode: "code",
|
|
5019
5390
|
});
|
|
@@ -5021,6 +5392,7 @@ export class BridgeWebSocketServer {
|
|
|
5021
5392
|
else {
|
|
5022
5393
|
this.send(ws, {
|
|
5023
5394
|
type: "rewind_result",
|
|
5395
|
+
sessionId: msg.sessionId,
|
|
5024
5396
|
success: false,
|
|
5025
5397
|
mode: "code",
|
|
5026
5398
|
error: result.error ?? "Cannot rewind files",
|
|
@@ -5036,6 +5408,7 @@ export class BridgeWebSocketServer {
|
|
|
5036
5408
|
this.sessionManager.rewindConversation(msg.sessionId, msg.targetUuid, (newSessionId) => {
|
|
5037
5409
|
this.send(ws, {
|
|
5038
5410
|
type: "rewind_result",
|
|
5411
|
+
sessionId: msg.sessionId,
|
|
5039
5412
|
success: true,
|
|
5040
5413
|
mode: "conversation",
|
|
5041
5414
|
});
|
|
@@ -5067,6 +5440,7 @@ export class BridgeWebSocketServer {
|
|
|
5067
5440
|
if (!result.canRewind) {
|
|
5068
5441
|
this.send(ws, {
|
|
5069
5442
|
type: "rewind_result",
|
|
5443
|
+
sessionId: msg.sessionId,
|
|
5070
5444
|
success: false,
|
|
5071
5445
|
mode: "both",
|
|
5072
5446
|
error: result.error ?? "Cannot rewind files",
|
|
@@ -5078,6 +5452,7 @@ export class BridgeWebSocketServer {
|
|
|
5078
5452
|
this.sessionManager.rewindConversation(msg.sessionId, msg.targetUuid, (newSessionId) => {
|
|
5079
5453
|
this.send(ws, {
|
|
5080
5454
|
type: "rewind_result",
|
|
5455
|
+
sessionId: msg.sessionId,
|
|
5081
5456
|
success: true,
|
|
5082
5457
|
mode: "both",
|
|
5083
5458
|
});
|
|
@@ -5109,6 +5484,7 @@ export class BridgeWebSocketServer {
|
|
|
5109
5484
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5110
5485
|
this.send(ws, {
|
|
5111
5486
|
type: "error",
|
|
5487
|
+
sessionId: msg.sessionId,
|
|
5112
5488
|
message: errMsg,
|
|
5113
5489
|
errorCode: "fork_failed",
|
|
5114
5490
|
});
|
|
@@ -5154,6 +5530,8 @@ export class BridgeWebSocketServer {
|
|
|
5154
5530
|
const info = this.galleryStore.metaToInfo(meta);
|
|
5155
5531
|
this.send(ws, {
|
|
5156
5532
|
type: "screenshot_result",
|
|
5533
|
+
...projectRequestMetadata(msg),
|
|
5534
|
+
sessionId: msg.sessionId,
|
|
5157
5535
|
success: true,
|
|
5158
5536
|
image: info,
|
|
5159
5537
|
});
|
|
@@ -5163,6 +5541,8 @@ export class BridgeWebSocketServer {
|
|
|
5163
5541
|
}
|
|
5164
5542
|
this.send(ws, {
|
|
5165
5543
|
type: "screenshot_result",
|
|
5544
|
+
...projectRequestMetadata(msg),
|
|
5545
|
+
sessionId: msg.sessionId,
|
|
5166
5546
|
success: false,
|
|
5167
5547
|
error: "Failed to save screenshot to gallery",
|
|
5168
5548
|
});
|
|
@@ -5175,6 +5555,8 @@ export class BridgeWebSocketServer {
|
|
|
5175
5555
|
.catch((err) => {
|
|
5176
5556
|
this.send(ws, {
|
|
5177
5557
|
type: "screenshot_result",
|
|
5558
|
+
...projectRequestMetadata(msg),
|
|
5559
|
+
sessionId: msg.sessionId,
|
|
5178
5560
|
success: false,
|
|
5179
5561
|
error: err instanceof Error ? err.message : String(err),
|
|
5180
5562
|
});
|
|
@@ -5727,6 +6109,7 @@ export class BridgeWebSocketServer {
|
|
|
5727
6109
|
defaultCodexProfile: this.defaultCodexProfile,
|
|
5728
6110
|
codexAutoReviewDisabled: this.codexAutoReviewDisabled,
|
|
5729
6111
|
bridgeVersion: getPackageVersion(),
|
|
6112
|
+
protocolCapabilities: ["project_request_correlation_v1"],
|
|
5730
6113
|
});
|
|
5731
6114
|
}
|
|
5732
6115
|
sendPromptHistoryStatus(ws) {
|
|
@@ -5759,6 +6142,7 @@ export class BridgeWebSocketServer {
|
|
|
5759
6142
|
defaultCodexProfile: this.defaultCodexProfile,
|
|
5760
6143
|
codexAutoReviewDisabled: this.codexAutoReviewDisabled,
|
|
5761
6144
|
bridgeVersion: getPackageVersion(),
|
|
6145
|
+
protocolCapabilities: ["project_request_correlation_v1"],
|
|
5762
6146
|
});
|
|
5763
6147
|
}
|
|
5764
6148
|
broadcastPromptHistoryStatus() {
|
|
@@ -6650,7 +7034,7 @@ export class BridgeWebSocketServer {
|
|
|
6650
7034
|
if (!compatibleMsg)
|
|
6651
7035
|
return;
|
|
6652
7036
|
const sessionId = this.extractSessionIdFromServerMessage(compatibleMsg);
|
|
6653
|
-
if (sessionId) {
|
|
7037
|
+
if (sessionId && this.debugEvents.has(sessionId)) {
|
|
6654
7038
|
this.recordDebugEvent(sessionId, {
|
|
6655
7039
|
direction: "outgoing",
|
|
6656
7040
|
channel: "ws",
|