@ccpocket/bridge 1.42.1 → 1.43.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/websocket.js CHANGED
@@ -8,7 +8,7 @@ import { SessionManager, } from "./session.js";
8
8
  import { SdkProcess } from "./sdk-process.js";
9
9
  import { CodexProcess } from "./codex-process.js";
10
10
  import { parseClientMessage, } from "./parser.js";
11
- import { getAllRecentSessions, getCodexSessionHistory, getSessionHistory, findSessionsByClaudeIds, extractMessageImages, getClaudeSessionName, loadCodexSessionNames, renameClaudeSession, renameCodexSession, } from "./sessions-index.js";
11
+ import { getAllRecentSessions, getCodexSessionHistory, getSessionHistory, findSessionsByClaudeIds, extractMessageImages, getClaudeSessionName, loadCodexSessionNames, renameClaudeSession, renameCodexSession, saveCodexSessionProfile, } from "./sessions-index.js";
12
12
  import { ArchiveStore } from "./archive-store.js";
13
13
  import { WorktreeStore } from "./worktree-store.js";
14
14
  import { listWorktrees, removeWorktree, worktreeExists, getMainBranch, } from "./worktree.js";
@@ -20,7 +20,7 @@ import { PushRelayClient } from "./push-relay.js";
20
20
  import { normalizePushLocale, t } from "./push-i18n.js";
21
21
  import { fetchAllUsage } from "./usage.js";
22
22
  import { getPackageVersion } from "./version.js";
23
- import { isPathWithinAllowedDirectory, resolvePlatformPath, } from "./path-utils.js";
23
+ import { isPathWithinAllowedDirectory, resolvePlatformPath, resolvePlatformPathFrom, } from "./path-utils.js";
24
24
  // ---- Available model lists (delivered to clients via session_list) ----
25
25
  const CLAUDE_MODELS = [
26
26
  "claude-opus-4-7",
@@ -256,6 +256,25 @@ export class BridgeWebSocketServer {
256
256
  errorCode: "path_not_allowed",
257
257
  };
258
258
  }
259
+ normalizeAdditionalWritableRoots(roots, projectPath) {
260
+ if (!roots || roots.length === 0)
261
+ return {};
262
+ const normalized = new Map();
263
+ for (const root of roots) {
264
+ const trimmed = root.trim();
265
+ if (!trimmed)
266
+ continue;
267
+ const resolved = resolvePlatformPathFrom(projectPath, trimmed, this.platform);
268
+ if (!this.isPathAllowed(resolved)) {
269
+ return { deniedRoot: root };
270
+ }
271
+ const key = this.platform === "win32" ? resolved.toLowerCase() : resolved;
272
+ if (!normalized.has(key)) {
273
+ normalized.set(key, resolved);
274
+ }
275
+ }
276
+ return { roots: [...normalized.values()] };
277
+ }
259
278
  buildSessionCreatedMessage(params) {
260
279
  const { sessionId, provider, projectPath, session, permissionMode, executionMode, planMode, approvalsReviewer, sandboxMode, slashCommands, skills, skillMetadata, apps, appMetadata, sourceSessionId, } = params;
261
280
  const msg = {
@@ -354,6 +373,10 @@ export class BridgeWebSocketServer {
354
373
  if (session.codexSettings.webSearchMode !== undefined) {
355
374
  msg.webSearchMode = session.codexSettings.webSearchMode;
356
375
  }
376
+ if (session.codexSettings.additionalWritableRoots !== undefined) {
377
+ msg.additionalWritableRoots =
378
+ session.codexSettings.additionalWritableRoots;
379
+ }
357
380
  }
358
381
  return msg;
359
382
  }
@@ -508,6 +531,13 @@ export class BridgeWebSocketServer {
508
531
  break;
509
532
  }
510
533
  }
534
+ const additionalWritableRoots = provider === "codex"
535
+ ? this.normalizeAdditionalWritableRoots(msg.additionalWritableRoots, projectPath)
536
+ : {};
537
+ if (additionalWritableRoots.deniedRoot) {
538
+ this.send(ws, this.buildPathNotAllowedError(additionalWritableRoots.deniedRoot));
539
+ break;
540
+ }
511
541
  const cached = provider === "claude"
512
542
  ? this.sessionManager.getCachedCommands(projectPath)
513
543
  : undefined;
@@ -553,6 +583,7 @@ export class BridgeWebSocketServer {
553
583
  networkAccessEnabled: msg.networkAccessEnabled,
554
584
  webSearchMode: msg.webSearchMode ??
555
585
  undefined,
586
+ additionalWritableRoots: additionalWritableRoots.roots,
556
587
  threadId: msg.sessionId,
557
588
  collaborationMode: planMode
558
589
  ? "plan"
@@ -927,6 +958,11 @@ export class BridgeWebSocketServer {
927
958
  if (newCollaboration !== currentCollaboration) {
928
959
  process.setCollaborationMode(newCollaboration);
929
960
  }
961
+ session.codexSettings = {
962
+ ...(session.codexSettings ?? {}),
963
+ approvalPolicy: newApproval,
964
+ approvalsReviewer: newReviewer,
965
+ };
930
966
  session.lastActivityAt = new Date();
931
967
  this.broadcast({
932
968
  type: "system",
@@ -1662,16 +1698,16 @@ export class BridgeWebSocketServer {
1662
1698
  // Resume flow: keep past history in SessionInfo and deliver it only
1663
1699
  // via get_history(sessionId) to avoid duplicate/missed replay races.
1664
1700
  if (provider === "codex") {
1665
- if (msg.profile &&
1666
- !(await this.validateCodexProfile(msg.profile, resumeProjectPath))) {
1667
- this.send(ws, {
1668
- type: "error",
1669
- message: `Codex profile not found: ${msg.profile}`,
1670
- });
1671
- break;
1672
- }
1673
1701
  const wtMapping = this.worktreeStore.get(sessionRefId);
1674
1702
  const effectiveProjectPath = resolvePlatformPath(wtMapping?.projectPath ?? resumeProjectPath, this.platform);
1703
+ const effectiveProfile = msg.profile
1704
+ ? await this.resolveCodexResumeProfile(msg.profile, sessionRefId, effectiveProjectPath)
1705
+ : undefined;
1706
+ const additionalWritableRoots = this.normalizeAdditionalWritableRoots(msg.additionalWritableRoots, effectiveProjectPath);
1707
+ if (additionalWritableRoots.deniedRoot) {
1708
+ this.send(ws, this.buildPathNotAllowedError(additionalWritableRoots.deniedRoot));
1709
+ break;
1710
+ }
1675
1711
  let worktreeOpts;
1676
1712
  if (wtMapping) {
1677
1713
  if (worktreeExists(wtMapping.worktreePath)) {
@@ -1691,7 +1727,7 @@ export class BridgeWebSocketServer {
1691
1727
  .then((pastMessages) => {
1692
1728
  const sessionId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
1693
1729
  threadId: sessionRefId,
1694
- profile: msg.profile,
1730
+ profile: effectiveProfile,
1695
1731
  approvalPolicy: codexApprovalPolicy ??
1696
1732
  normalizeCodexApprovalPolicy(executionMode === "fullAccess" ? "never" : "on-request"),
1697
1733
  approvalsReviewer: msg.approvalsReviewer,
@@ -1701,6 +1737,7 @@ export class BridgeWebSocketServer {
1701
1737
  networkAccessEnabled: msg.networkAccessEnabled,
1702
1738
  webSearchMode: msg.webSearchMode ??
1703
1739
  undefined,
1740
+ additionalWritableRoots: additionalWritableRoots.roots,
1704
1741
  collaborationMode: planMode
1705
1742
  ? "plan"
1706
1743
  : "default",
@@ -3075,6 +3112,26 @@ export class BridgeWebSocketServer {
3075
3112
  this.defaultCodexProfile = snapshot.defaultProfile;
3076
3113
  return snapshot.profiles.includes(profile);
3077
3114
  }
3115
+ async resolveCodexResumeProfile(requestedProfile, threadId, projectPath) {
3116
+ const snapshot = await this.loadCodexProfiles(projectPath);
3117
+ this.codexProfiles = snapshot.profiles;
3118
+ this.defaultCodexProfile = snapshot.defaultProfile;
3119
+ if (snapshot.profiles.includes(requestedProfile)) {
3120
+ return requestedProfile;
3121
+ }
3122
+ const fallbackProfile = snapshot.defaultProfile &&
3123
+ snapshot.profiles.includes(snapshot.defaultProfile)
3124
+ ? snapshot.defaultProfile
3125
+ : undefined;
3126
+ console.warn(`[ws] Codex profile not found on resume: ${requestedProfile}; ` +
3127
+ (fallbackProfile
3128
+ ? `falling back to default profile: ${fallbackProfile}`
3129
+ : "falling back to Codex config default"));
3130
+ saveCodexSessionProfile(threadId, fallbackProfile ?? null).catch((err) => {
3131
+ console.warn(`[ws] Failed to update Codex session profile cache: ${err}`);
3132
+ });
3133
+ return fallbackProfile;
3134
+ }
3078
3135
  getActiveCodexProcess() {
3079
3136
  const summary = this.sessionManager
3080
3137
  .list()
@@ -3802,6 +3859,10 @@ export class BridgeWebSocketServer {
3802
3859
  if (session.codexSettings.webSearchMode !== undefined) {
3803
3860
  msg.webSearchMode = session.codexSettings.webSearchMode;
3804
3861
  }
3862
+ if (session.codexSettings.additionalWritableRoots !== undefined) {
3863
+ msg.additionalWritableRoots =
3864
+ session.codexSettings.additionalWritableRoots;
3865
+ }
3805
3866
  }
3806
3867
  return msg;
3807
3868
  }