fix(vscode): skip unsupported OpenChamber event stream
Shared session-list subscriptions opened server-only event and URL-token routes against the webview origin, causing repeated 403 responses. Skip the control-stream subscription in VS Code while preserving OpenCode bridge sync and polling. Validated with regression tests, UI type-check and lint, and the VS Code webview build. The reported gray-screen crash remains unconfirmed pending user logs.
This commit is contained in:
@@ -1,12 +1,4 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
||||
|
||||
mock.module('./runtime-url', () => ({
|
||||
getRuntimeUrlResolver: () => ({ sse: (path: string) => `http://runtime.test${path}` }),
|
||||
}));
|
||||
|
||||
mock.module('./runtime-switch', () => ({
|
||||
subscribeRuntimeEndpointChanged: () => () => undefined,
|
||||
}));
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
||||
|
||||
class MockEventSource {
|
||||
static CLOSED = 2;
|
||||
@@ -29,20 +21,37 @@ class MockEventSource {
|
||||
describe('openchamber events', () => {
|
||||
beforeEach(() => {
|
||||
MockEventSource.instances = [];
|
||||
globalThis.window = {} as Window & typeof globalThis;
|
||||
globalThis.EventSource = MockEventSource as unknown as typeof EventSource;
|
||||
Object.defineProperty(globalThis, 'window', {
|
||||
value: Object.assign(new EventTarget(), { location: new URL('http://runtime.test') }),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
Object.defineProperty(globalThis, 'EventSource', { value: MockEventSource, configurable: true, writable: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete (globalThis as { window?: unknown }).window;
|
||||
delete (globalThis as { EventSource?: unknown }).EventSource;
|
||||
Reflect.deleteProperty(globalThis, 'window');
|
||||
Reflect.deleteProperty(globalThis, 'EventSource');
|
||||
});
|
||||
|
||||
test('does not open the server-only event stream in VS Code', async () => {
|
||||
Object.defineProperty(window, '__VSCODE_CONFIG__', {
|
||||
value: { workspaceFolder: 'C:/repo', workspaceFolders: [] },
|
||||
configurable: true,
|
||||
});
|
||||
const { subscribeOpenchamberEvents } = await import('./openchamberEvents');
|
||||
const unsubscribe = subscribeOpenchamberEvents(() => undefined);
|
||||
try {
|
||||
expect(MockEventSource.instances).toHaveLength(0);
|
||||
} finally {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
test('dispatches externally created session events', async () => {
|
||||
const { subscribeOpenchamberEvents } = await import('./openchamberEvents');
|
||||
const events: unknown[] = [];
|
||||
const listener = (event: unknown) => events.push(event);
|
||||
const unsubscribe = subscribeOpenchamberEvents(listener);
|
||||
const unsubscribe = subscribeOpenchamberEvents((event) => events.push(event));
|
||||
const source = MockEventSource.instances[0];
|
||||
|
||||
source.onmessage?.({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getRuntimeUrlResolver } from './runtime-url';
|
||||
import { subscribeRuntimeEndpointChanged } from './runtime-switch';
|
||||
import { isVSCodeRuntime } from './desktop';
|
||||
|
||||
type ScheduledTaskRanEvent = {
|
||||
type: 'scheduled-task-ran';
|
||||
@@ -284,6 +285,10 @@ const cleanupRuntimeChangeSubscription = () => {
|
||||
};
|
||||
|
||||
export const subscribeOpenchamberEvents = (listener: Listener): (() => void) => {
|
||||
// VS Code runs OpenCode through its bridge, not the OpenChamber server that
|
||||
// owns this stream. Opening it here retries against vscode-webview:// forever.
|
||||
if (isVSCodeRuntime()) return () => undefined;
|
||||
|
||||
listeners.add(listener);
|
||||
ensureRuntimeChangeSubscription();
|
||||
connect();
|
||||
|
||||
@@ -103,6 +103,13 @@ The webview build emits each worker as one self-contained file. VS Code webviews
|
||||
|
||||
Message and part ordering is owned by [`packages/ui/src/sync/DOCUMENTATION.md`](../../ui/src/sync/DOCUMENTATION.md#session-message-loading). The VS Code webview consumes that shared sync implementation; bridge and proxy runtimes pass OpenCode records through without adding runtime-specific ordering.
|
||||
|
||||
The OpenChamber control stream (`/api/openchamber/events`) requires the
|
||||
OpenChamber server, which the extension does not run. `subscribeOpenchamberEvents`
|
||||
therefore returns a no-op subscription in VS Code before resolving URLs or
|
||||
opening a connection. Session sync still uses the OpenCode SSE bridge and
|
||||
global session polling. Sending the control stream to the webview origin caused
|
||||
repeated `403` responses and URL-token requests to `/auth/url-token`.
|
||||
|
||||
## Extension guideline
|
||||
|
||||
When adding new bridge route families:
|
||||
|
||||
Reference in New Issue
Block a user