Skip to content

Migrate multi-profile support to upstream contextId routing#8

Merged
GreyC merged 1 commit into
mainfrom
migrate/multi-profile-upstream
May 10, 2026
Merged

Migrate multi-profile support to upstream contextId routing#8
GreyC merged 1 commit into
mainfrom
migrate/multi-profile-upstream

Conversation

@GreyC
Copy link
Copy Markdown
Owner

@GreyC GreyC commented May 10, 2026

Summary

Implements the multi-profile migration decision from #6 by rebasing the work onto upstream's contextId Browser Bridge model and keeping only the minimal local fix we still need.

This PR intentionally does not carry forward the old custom multi-profile routing stack:

  • no custom profileId / extensions routing
  • no custom daemon-routing.ts / config.ts
  • no popup rename / profileLabel sync-storage UX
  • no broad daemon/client/CLI routing delta

The only code delta is the stale WebSocket close guard in the extension background connection lifecycle.

What changed

  • Captures each daemon WebSocket as thisWs inside connect().
  • Binds onopen, onmessage, onclose, and onerror to that specific instance.
  • Ignores stale onopen / onclose callbacks when a newer WebSocket has already replaced the module-level ws pointer.
  • Adds a regression test where an old daemon WebSocket closes after a newer connection exists, then verifies the newer connection can still return command responses.
  • Rebuilds extension/dist/background.js.

Why

Upstream now has the correct long-term multi-profile architecture using:

  • contextId
  • extensionProfiles
  • src/browser/profile.ts
  • opencli profile list
  • opencli profile rename <contextId> <alias>
  • opencli profile use <profile>
  • --profile <alias-or-contextId> / OPENCLI_PROFILE

Keeping our old custom routing caused repeated merge conflicts in high-churn files. This PR reduces the migration to a small, low-conflict patch while preserving the real connection bug fix.

Verification

npx vitest run --project extension extension/src/background.test.ts
npm run typecheck -- --pretty false
npm --prefix extension run typecheck
npm --prefix extension run build

All passed locally.

Follow-up docs

Per #6, BillMend operation docs should be updated after this lands so account-sensitive examples use explicit upstream aliases, for example:

opencli --profile Geoffrey reddit ...
opencli --profile yaya facebook ...

Aliases should be configured with:

opencli profile list
opencli profile rename <contextId> Geoffrey
opencli profile rename <contextId> yaya

Closes #6.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances WebSocket connection handling by introducing a local reference to the active socket and implementing guards in the onopen and onclose handlers to prevent stale connections from affecting the global state. It also includes updated unit tests to validate this behavior. Feedback indicates that the onmessage handler should also include a similar guard to ensure that only the current connection processes incoming commands, thereby avoiding potential race conditions.

Comment on lines +131 to 139
thisWs.onmessage = async (event) => {
try {
const command = JSON.parse(event.data as string) as Command;
const result = await handleCommand(command);
ws?.send(JSON.stringify(result));
thisWs.send(JSON.stringify(result));
} catch (err) {
console.error('[opencli] Message handling error:', err);
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The onmessage handler should include the ws !== thisWs guard, consistent with the logic added to onopen and onclose. Without this guard, a stale WebSocket connection could still process and respond to commands if the daemon sends them before the connection is fully closed (e.g., during a race condition where a newer connection has already taken over the global ws pointer). This prevents potential duplicate command execution or state conflicts.

Suggested change
thisWs.onmessage = async (event) => {
try {
const command = JSON.parse(event.data as string) as Command;
const result = await handleCommand(command);
ws?.send(JSON.stringify(result));
thisWs.send(JSON.stringify(result));
} catch (err) {
console.error('[opencli] Message handling error:', err);
}
};
thisWs.onmessage = async (event) => {
if (ws !== thisWs) return;
try {
const command = JSON.parse(event.data as string) as Command;
const result = await handleCommand(command);
thisWs.send(JSON.stringify(result));
} catch (err) {
console.error('[opencli] Message handling error:', err);
}
};

@GreyC GreyC merged commit afd6cdb into main May 10, 2026
27 checks passed
@GreyC GreyC deleted the migrate/multi-profile-upstream branch May 10, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate multi-profile support to upstream contextId routing

1 participant