@ccpocket/bridge 1.37.0 → 1.38.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/codex-process.d.ts +6 -0
- package/dist/codex-process.js +20 -0
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +4 -0
- package/dist/parser.js.map +1 -1
- package/dist/session.d.ts +2 -0
- package/dist/session.js +8 -1
- package/dist/session.js.map +1 -1
- package/dist/sessions-index.d.ts +3 -0
- package/dist/sessions-index.js +47 -0
- package/dist/sessions-index.js.map +1 -1
- package/dist/websocket.d.ts +6 -0
- package/dist/websocket.js +112 -1
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFile, execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import { readFile, unlink } from "node:fs/promises";
|
|
3
|
+
import { lstat, readFile, readlink, stat, unlink } from "node:fs/promises";
|
|
4
4
|
import { resolve, extname } from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { WebSocketServer, WebSocket } from "ws";
|
|
@@ -148,6 +148,9 @@ export class BridgeWebSocketServer {
|
|
|
148
148
|
debugEvents = new Map();
|
|
149
149
|
notifiedPermissionToolUses = new Map();
|
|
150
150
|
archiveStore;
|
|
151
|
+
codexProfiles = [];
|
|
152
|
+
defaultCodexProfile;
|
|
153
|
+
codexProfilesRequest = null;
|
|
151
154
|
/** FCM token → push notification locale */
|
|
152
155
|
tokenLocales = new Map();
|
|
153
156
|
tokenPrivacyMode = new Map();
|
|
@@ -344,6 +347,7 @@ export class BridgeWebSocketServer {
|
|
|
344
347
|
}
|
|
345
348
|
handleConnection(ws) {
|
|
346
349
|
// Send session list and project history on connect
|
|
350
|
+
void this.refreshCodexProfiles();
|
|
347
351
|
this.sendSessionList(ws);
|
|
348
352
|
const projects = this.projectHistory?.getProjects() ?? [];
|
|
349
353
|
this.send(ws, { type: "project_history", projects });
|
|
@@ -422,6 +426,14 @@ export class BridgeWebSocketServer {
|
|
|
422
426
|
const legacyPermissionMode = modesToLegacyPermissionMode(provider, executionMode, planMode);
|
|
423
427
|
if (provider === "codex") {
|
|
424
428
|
console.log(`[ws] start(codex): execution=${executionMode} plan=${planMode}`);
|
|
429
|
+
if (msg.profile &&
|
|
430
|
+
!(await this.validateCodexProfile(msg.profile, projectPath))) {
|
|
431
|
+
this.send(ws, {
|
|
432
|
+
type: "error",
|
|
433
|
+
message: `Codex profile not found: ${msg.profile}`,
|
|
434
|
+
});
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
425
437
|
}
|
|
426
438
|
const cached = provider === "claude"
|
|
427
439
|
? this.sessionManager.getCachedCommands(projectPath)
|
|
@@ -447,6 +459,7 @@ export class BridgeWebSocketServer {
|
|
|
447
459
|
existingWorktreePath: msg.existingWorktreePath,
|
|
448
460
|
}, provider, provider === "codex"
|
|
449
461
|
? {
|
|
462
|
+
profile: msg.profile,
|
|
450
463
|
approvalPolicy: codexApprovalPolicy ??
|
|
451
464
|
normalizeCodexApprovalPolicy(executionMode === "fullAccess" ? "never" : "on-request"),
|
|
452
465
|
sandboxMode: sandboxModeToInternal(msg.sandboxMode),
|
|
@@ -1535,6 +1548,14 @@ export class BridgeWebSocketServer {
|
|
|
1535
1548
|
// Resume flow: keep past history in SessionInfo and deliver it only
|
|
1536
1549
|
// via get_history(sessionId) to avoid duplicate/missed replay races.
|
|
1537
1550
|
if (provider === "codex") {
|
|
1551
|
+
if (msg.profile &&
|
|
1552
|
+
!(await this.validateCodexProfile(msg.profile, resumeProjectPath))) {
|
|
1553
|
+
this.send(ws, {
|
|
1554
|
+
type: "error",
|
|
1555
|
+
message: `Codex profile not found: ${msg.profile}`,
|
|
1556
|
+
});
|
|
1557
|
+
break;
|
|
1558
|
+
}
|
|
1538
1559
|
const wtMapping = this.worktreeStore.get(sessionRefId);
|
|
1539
1560
|
const effectiveProjectPath = resolvePlatformPath(wtMapping?.projectPath ?? resumeProjectPath, this.platform);
|
|
1540
1561
|
let worktreeOpts;
|
|
@@ -1556,6 +1577,7 @@ export class BridgeWebSocketServer {
|
|
|
1556
1577
|
.then((pastMessages) => {
|
|
1557
1578
|
const sessionId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
1558
1579
|
threadId: sessionRefId,
|
|
1580
|
+
profile: msg.profile,
|
|
1559
1581
|
approvalPolicy: codexApprovalPolicy ??
|
|
1560
1582
|
normalizeCodexApprovalPolicy(executionMode === "fullAccess" ? "never" : "on-request"),
|
|
1561
1583
|
sandboxMode: sandboxModeToInternal(msg.sandboxMode),
|
|
@@ -1767,6 +1789,51 @@ export class BridgeWebSocketServer {
|
|
|
1767
1789
|
});
|
|
1768
1790
|
return;
|
|
1769
1791
|
}
|
|
1792
|
+
const fileStat = await lstat(absPath);
|
|
1793
|
+
if (fileStat.isSymbolicLink()) {
|
|
1794
|
+
let targetPath = "";
|
|
1795
|
+
try {
|
|
1796
|
+
targetPath = await readlink(absPath);
|
|
1797
|
+
}
|
|
1798
|
+
catch {
|
|
1799
|
+
// Best effort only; the user-facing error still works without it.
|
|
1800
|
+
}
|
|
1801
|
+
let resolvedTargetStat;
|
|
1802
|
+
try {
|
|
1803
|
+
resolvedTargetStat = await stat(absPath);
|
|
1804
|
+
}
|
|
1805
|
+
catch {
|
|
1806
|
+
this.send(ws, {
|
|
1807
|
+
type: "file_content",
|
|
1808
|
+
filePath: msg.filePath,
|
|
1809
|
+
content: "",
|
|
1810
|
+
error: targetPath.length > 0
|
|
1811
|
+
? `This symbolic link points to a missing target: ${targetPath}`
|
|
1812
|
+
: "This symbolic link points to a missing target.",
|
|
1813
|
+
});
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
if (resolvedTargetStat.isDirectory()) {
|
|
1817
|
+
this.send(ws, {
|
|
1818
|
+
type: "file_content",
|
|
1819
|
+
filePath: msg.filePath,
|
|
1820
|
+
content: "",
|
|
1821
|
+
error: targetPath.length > 0
|
|
1822
|
+
? `This symbolic link points to a directory (${targetPath}). Open the target directory instead.`
|
|
1823
|
+
: "This symbolic link points to a directory. Open the target directory instead.",
|
|
1824
|
+
});
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
else if (fileStat.isDirectory()) {
|
|
1829
|
+
this.send(ws, {
|
|
1830
|
+
type: "file_content",
|
|
1831
|
+
filePath: msg.filePath,
|
|
1832
|
+
content: "",
|
|
1833
|
+
error: "This path is a directory. Open a file instead.",
|
|
1834
|
+
});
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1770
1837
|
const maxLines = typeof msg.maxLines === "number" && msg.maxLines > 0
|
|
1771
1838
|
? msg.maxLines
|
|
1772
1839
|
: 5000;
|
|
@@ -2774,6 +2841,8 @@ export class BridgeWebSocketServer {
|
|
|
2774
2841
|
allowedDirs: this.allowedDirs,
|
|
2775
2842
|
claudeModels: CLAUDE_MODELS,
|
|
2776
2843
|
codexModels: CODEX_MODELS,
|
|
2844
|
+
codexProfiles: this.codexProfiles,
|
|
2845
|
+
defaultCodexProfile: this.defaultCodexProfile,
|
|
2777
2846
|
bridgeVersion: getPackageVersion(),
|
|
2778
2847
|
});
|
|
2779
2848
|
}
|
|
@@ -2787,6 +2856,8 @@ export class BridgeWebSocketServer {
|
|
|
2787
2856
|
allowedDirs: this.allowedDirs,
|
|
2788
2857
|
claudeModels: CLAUDE_MODELS,
|
|
2789
2858
|
codexModels: CODEX_MODELS,
|
|
2859
|
+
codexProfiles: this.codexProfiles,
|
|
2860
|
+
defaultCodexProfile: this.defaultCodexProfile,
|
|
2790
2861
|
bridgeVersion: getPackageVersion(),
|
|
2791
2862
|
});
|
|
2792
2863
|
}
|
|
@@ -2840,6 +2911,46 @@ export class BridgeWebSocketServer {
|
|
|
2840
2911
|
archivedSessionIds: this.archiveStore.archivedIds(),
|
|
2841
2912
|
});
|
|
2842
2913
|
}
|
|
2914
|
+
async refreshCodexProfiles(projectPath) {
|
|
2915
|
+
if (this.codexProfilesRequest)
|
|
2916
|
+
return this.codexProfilesRequest;
|
|
2917
|
+
this.codexProfilesRequest = this.loadCodexProfiles(projectPath)
|
|
2918
|
+
.then(({ profiles, defaultProfile }) => {
|
|
2919
|
+
this.codexProfiles = profiles;
|
|
2920
|
+
this.defaultCodexProfile = defaultProfile;
|
|
2921
|
+
this.broadcastSessionList();
|
|
2922
|
+
})
|
|
2923
|
+
.catch((err) => {
|
|
2924
|
+
console.warn(`[ws] Failed to load Codex profiles: ${err}`);
|
|
2925
|
+
this.codexProfiles = [];
|
|
2926
|
+
this.defaultCodexProfile = undefined;
|
|
2927
|
+
})
|
|
2928
|
+
.finally(() => {
|
|
2929
|
+
this.codexProfilesRequest = null;
|
|
2930
|
+
});
|
|
2931
|
+
return this.codexProfilesRequest;
|
|
2932
|
+
}
|
|
2933
|
+
async loadCodexProfiles(projectPath) {
|
|
2934
|
+
const process = this.getActiveCodexProcess() ??
|
|
2935
|
+
(await this.createStandaloneCodexProcess(projectPath));
|
|
2936
|
+
const isStandalone = process !== this.getActiveCodexProcess();
|
|
2937
|
+
try {
|
|
2938
|
+
return await process.readProfileConfig(projectPath);
|
|
2939
|
+
}
|
|
2940
|
+
finally {
|
|
2941
|
+
if (isStandalone) {
|
|
2942
|
+
process.stop();
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
async validateCodexProfile(profile, projectPath) {
|
|
2947
|
+
if (!profile)
|
|
2948
|
+
return true;
|
|
2949
|
+
const snapshot = await this.loadCodexProfiles(projectPath);
|
|
2950
|
+
this.codexProfiles = snapshot.profiles;
|
|
2951
|
+
this.defaultCodexProfile = snapshot.defaultProfile;
|
|
2952
|
+
return snapshot.profiles.includes(profile);
|
|
2953
|
+
}
|
|
2843
2954
|
getActiveCodexProcess() {
|
|
2844
2955
|
const summary = this.sessionManager
|
|
2845
2956
|
.list()
|