Add a VS Code-specific foreground permission responder while preserving the server-owned implementation for web, desktop, and mobile runtimes. Persist the authoritative VS Code policy in extension global state and expose matching GET/PUT bridge routes. Broadcast policy updates to the sidebar, session editor panels, and agent manager so every active webview observes the same explicit per-session policy. Resolve missing child-session lineage through OpenCode, honor nearest explicit ancestor overrides, deduplicate concurrent requests, retry transient replies, and reconcile pending permissions after enablement, bootstrap, and reconnect. Treat resolved requests as handled and route notification suppression through the same responder outcome. Keep post-toggle reconciliation failures non-fatal after policy persistence and fail closed when lineage or replies cannot be confirmed. Document that auto-accept intentionally cannot run while every OpenChamber webview is closed or suspended.
182 lines
5.2 KiB
TypeScript
182 lines
5.2 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import { type OpenCodeManager } from './opencode';
|
|
import { handleStandardGitBridgeMessage } from './bridge-git-runtime';
|
|
import { handleSpecialGitBridgeMessage } from './bridge-git-special-runtime';
|
|
import { handleFsBridgeMessage } from './bridge-fs-runtime';
|
|
import { handleConfigBridgeMessage } from './bridge-config-runtime';
|
|
import { handleSystemBridgeMessage } from './bridge-system-runtime';
|
|
import { handleProxyBridgeMessage } from './bridge-proxy-runtime';
|
|
import { handlePermissionAutoAcceptBridgeMessage } from './bridge-permission-auto-accept-runtime';
|
|
import {
|
|
fetchOpenCodeSkillsFromApi,
|
|
persistSettings,
|
|
readSettings,
|
|
readMagicPromptOverrides,
|
|
saveMagicPromptOverride,
|
|
resetMagicPromptOverride,
|
|
resetAllMagicPromptOverrides,
|
|
} from './bridge-settings-runtime';
|
|
import { execGit } from './bridge-git-process-runtime';
|
|
import {
|
|
parseDroppedFileReference,
|
|
readUriAsAttachment,
|
|
resolveUserPath,
|
|
listDirectoryEntries,
|
|
normalizeFsPath,
|
|
searchDirectory,
|
|
resolveFileReadPath,
|
|
fetchModelsMetadata,
|
|
} from './bridge-fs-helpers-runtime';
|
|
import {
|
|
tryHandleLocalFsProxy,
|
|
buildUnavailableApiResponse,
|
|
sanitizeForwardHeaders,
|
|
collectHeaders,
|
|
base64EncodeUtf8,
|
|
} from './bridge-localfs-proxy-runtime';
|
|
|
|
export interface BridgeRequest {
|
|
id: string;
|
|
type: string;
|
|
payload?: unknown;
|
|
}
|
|
|
|
export interface BridgeResponse {
|
|
id: string;
|
|
type: string;
|
|
success: boolean;
|
|
data?: unknown;
|
|
error?: string;
|
|
}
|
|
|
|
export interface BridgeContext {
|
|
manager?: OpenCodeManager;
|
|
context?: vscode.ExtensionContext;
|
|
}
|
|
|
|
const CLIENT_RELOAD_DELAY_MS = 800;
|
|
|
|
const UPDATE_CHECK_URL = process.env.OPENCHAMBER_UPDATE_API_URL || 'https://api.openchamber.dev/v1/update/check';
|
|
const GITHUB_BACKEND_DISABLED_ERROR = 'OpenChamber VS Code backend GitHub integration is disabled. Use native VS Code GitHub integrations.';
|
|
|
|
|
|
export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeContext): Promise<BridgeResponse> {
|
|
const { id, type, payload } = message;
|
|
|
|
try {
|
|
const permissionAutoAcceptResponse = await handlePermissionAutoAcceptBridgeMessage(
|
|
{ id, type, payload },
|
|
ctx?.context,
|
|
{
|
|
broadcast: (snapshot) => vscode.commands.executeCommand(
|
|
'openchamber.internal.permissionAutoAcceptSynced',
|
|
snapshot,
|
|
),
|
|
},
|
|
);
|
|
if (permissionAutoAcceptResponse) return permissionAutoAcceptResponse;
|
|
|
|
const standardGitResponse = await handleStandardGitBridgeMessage({ id, type, payload });
|
|
if (standardGitResponse) {
|
|
return standardGitResponse;
|
|
}
|
|
const specialGitResponse = await handleSpecialGitBridgeMessage(
|
|
{ id, type, payload },
|
|
ctx,
|
|
{ readSettings, execGit }
|
|
);
|
|
if (specialGitResponse) {
|
|
return specialGitResponse;
|
|
}
|
|
const fsResponse = await handleFsBridgeMessage(
|
|
{ id, type, payload },
|
|
{
|
|
resolveUserPath,
|
|
listDirectoryEntries,
|
|
normalizeFsPath,
|
|
execGit,
|
|
searchDirectory,
|
|
resolveFileReadPath,
|
|
parseDroppedFileReference,
|
|
readUriAsAttachment,
|
|
}
|
|
);
|
|
if (fsResponse) {
|
|
return fsResponse;
|
|
}
|
|
const configResponse = await handleConfigBridgeMessage(
|
|
{ id, type, payload },
|
|
ctx,
|
|
{
|
|
readSettings,
|
|
persistSettings,
|
|
readMagicPromptOverrides,
|
|
saveMagicPromptOverride,
|
|
resetMagicPromptOverride,
|
|
resetAllMagicPromptOverrides,
|
|
fetchOpenCodeSkillsFromApi,
|
|
clientReloadDelayMs: CLIENT_RELOAD_DELAY_MS,
|
|
},
|
|
);
|
|
if (configResponse) {
|
|
return configResponse;
|
|
}
|
|
const systemResponse = await handleSystemBridgeMessage(
|
|
{ id, type, payload },
|
|
ctx,
|
|
{
|
|
resolveUserPath,
|
|
fetchModelsMetadata,
|
|
updateCheckUrl: UPDATE_CHECK_URL,
|
|
clientReloadDelayMs: CLIENT_RELOAD_DELAY_MS,
|
|
},
|
|
);
|
|
if (systemResponse) {
|
|
return systemResponse;
|
|
}
|
|
const proxyResponse = await handleProxyBridgeMessage(
|
|
{ id, type, payload },
|
|
ctx,
|
|
{
|
|
tryHandleLocalFsProxy,
|
|
buildUnavailableApiResponse,
|
|
sanitizeForwardHeaders,
|
|
collectHeaders,
|
|
base64EncodeUtf8,
|
|
},
|
|
);
|
|
if (proxyResponse) {
|
|
return proxyResponse;
|
|
}
|
|
|
|
switch (type) {
|
|
case 'api:github/auth:status':
|
|
case 'api:github/auth:start':
|
|
case 'api:github/auth:complete':
|
|
case 'api:github/auth:disconnect':
|
|
case 'api:github/auth:activate':
|
|
case 'api:github/me':
|
|
case 'api:github/pr:status':
|
|
case 'api:github/pr:create':
|
|
case 'api:github/pr:update':
|
|
case 'api:github/pr:merge':
|
|
case 'api:github/pr:ready':
|
|
case 'api:github/issues:list':
|
|
case 'api:github/issues:get':
|
|
case 'api:github/issues:comments':
|
|
case 'api:github/pulls:list':
|
|
case 'api:github/pulls:context':
|
|
case 'api:github/repo:upstream':
|
|
case 'api:github/repo:branches': {
|
|
return { id, type, success: false, error: GITHUB_BACKEND_DISABLED_ERROR };
|
|
}
|
|
|
|
default:
|
|
return { id, type, success: false, error: `Unknown message type: ${type}` };
|
|
}
|
|
} catch (err) {
|
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
return { id, type, success: false, error: errorMessage };
|
|
}
|
|
}
|