mirror of
https://github.com/larsbaunwall/vscode-copilot-bridge.git
synced 2025-10-05 22:22:59 +00:00
Copilot Chat API guard + reason fields: detect missing proposed API, add reason in /healthz and 503 errors, verbose diagnostics, docs; manifest opts into chat proposals
Co-Authored-By: Lars Baunwall <larslb@thinkability.dk>
This commit is contained in:
parent
e61d191ebf
commit
71495c6812
3 changed files with 52 additions and 4 deletions
22
README.md
22
README.md
|
|
@ -49,6 +49,27 @@ Optional: Packaging a VSIX
|
|||
npm i -g @vscode/vsce
|
||||
vsce package
|
||||
- Then install the generated .vsix via “Extensions: Install from VSIX…”
|
||||
## Enabling the VS Code Chat proposed API
|
||||
|
||||
The `vscode.chat.requestChatAccess` API is currently proposed. To use this extension at runtime, enable proposed APIs for this extension:
|
||||
|
||||
- Stable VS Code:
|
||||
- Start with: `code --enable-proposed-api thinkability.copilot-bridge`
|
||||
- VS Code Insiders:
|
||||
- Proposed APIs can be used when running the extension from source (F5) or with the flag above
|
||||
- Run from source:
|
||||
- Open this folder in VS Code and press F5 (Extension Development Host)
|
||||
|
||||
When the proposed API is not enabled, the Output (“Copilot Bridge”) will show:
|
||||
“VS Code Chat proposed API not enabled; start VS Code with: code --enable-proposed-api thinkability.copilot-bridge, or run via F5/Insiders.”
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- /healthz shows `copilot: "unavailable"` with a `reason`:
|
||||
- `missing_chat_api`: VS Code Chat proposed API not enabled (use the flag above)
|
||||
- `copilot_unavailable`: Copilot access not granted (sign in to GitHub Copilot)
|
||||
- POST /v1/chat/completions returns 503 with `reason` giving the same codes as above.
|
||||
|
||||
|
||||
## Configuration (bridge.*)
|
||||
|
||||
|
|
@ -68,6 +89,7 @@ To see verbose logs:
|
|||
- Access acquisition attempts (“Copilot access missing; attempting to acquire…”, “Copilot access acquired.”)
|
||||
- SSE lifecycle (“SSE start …”, “SSE end …”)
|
||||
- Health checks (best-effort access check when verbose is on)
|
||||
- Proposed API diagnostics (e.g., “VS Code Chat proposed API not enabled…”)
|
||||
- bridge.verbose (boolean; default false): verbose logs to “Copilot Bridge” output channel
|
||||
|
||||
## Manual Testing (curl)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@
|
|||
"engines": {
|
||||
"vscode": "^1.90.0"
|
||||
},
|
||||
"enabledApiProposals": [
|
||||
"chat",
|
||||
"chatProvider"
|
||||
],
|
||||
"extensionKind": [
|
||||
"ui"
|
||||
],
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ let statusItem: vscode.StatusBarItem | undefined;
|
|||
let output: vscode.OutputChannel | undefined;
|
||||
let running = false;
|
||||
let activeRequests = 0;
|
||||
let lastReason: string | undefined;
|
||||
|
||||
export async function activate(ctx: vscode.ExtensionContext) {
|
||||
output = vscode.window.createOutputChannel('Copilot Bridge');
|
||||
|
|
@ -77,11 +78,13 @@ async function startBridge() {
|
|||
if (req.method === 'GET' && req.url === '/healthz') {
|
||||
const cfgNow = vscode.workspace.getConfiguration('bridge');
|
||||
const verboseNow = cfgNow.get<boolean>('verbose') ?? false;
|
||||
const hasProposal = !!((vscode as any).chat && typeof (vscode as any).chat.requestChatAccess === 'function');
|
||||
if (!access && verboseNow) {
|
||||
if (verboseNow) output?.appendLine(`Healthz: access=${access ? 'present' : 'missing'}`);
|
||||
if (verboseNow) output?.appendLine(`Healthz: access=${access ? 'present' : 'missing'} proposal=${hasProposal ? 'ok' : 'missing'}`);
|
||||
await getAccess();
|
||||
}
|
||||
writeJson(res, 200, { ok: true, copilot: access ? 'ok' : 'unavailable', version: vscode.version });
|
||||
const unavailableReason = access ? undefined : (!hasProposal ? 'missing_chat_api' : (lastReason || 'copilot_unavailable'));
|
||||
writeJson(res, 200, { ok: true, copilot: access ? 'ok' : 'unavailable', reason: unavailableReason, version: vscode.version });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +103,9 @@ async function startBridge() {
|
|||
await getAccess();
|
||||
}
|
||||
if (!access) {
|
||||
writeJson(res, 503, { error: { message: 'Copilot unavailable', type: 'server_error', code: 'copilot_unavailable' } });
|
||||
const hasProposal = !!((vscode as any).chat && typeof (vscode as any).chat.requestChatAccess === 'function');
|
||||
const reason = !hasProposal ? 'missing_chat_api' : (lastReason || 'copilot_unavailable');
|
||||
writeJson(res, 503, { error: { message: 'Copilot unavailable', type: 'server_error', code: 'copilot_unavailable', reason } });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -236,9 +241,22 @@ async function getAccess(force = false): Promise<vscode.ChatAccess | undefined>
|
|||
if (!force && access) return access;
|
||||
const cfg = vscode.workspace.getConfiguration('bridge');
|
||||
const verbose = cfg.get<boolean>('verbose') ?? false;
|
||||
|
||||
const hasProposal = !!((vscode as any).chat && typeof (vscode as any).chat.requestChatAccess === 'function');
|
||||
if (!hasProposal) {
|
||||
access = undefined;
|
||||
lastReason = 'missing_chat_api';
|
||||
const info = server ? server.address() : undefined;
|
||||
const bound = info && typeof info === 'object' ? `${info.address}:${info.port}` : '';
|
||||
statusItem && (statusItem.text = `Copilot Bridge: Unavailable ${bound ? `@ ${bound}` : ''}`);
|
||||
if (verbose) output?.appendLine('VS Code Chat proposed API not enabled; start VS Code with: code --enable-proposed-api thinkability.copilot-bridge, or run via F5/Insiders.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const newAccess = await vscode.chat.requestChatAccess('copilot');
|
||||
const newAccess = await (vscode as any).chat.requestChatAccess('copilot');
|
||||
access = newAccess;
|
||||
lastReason = undefined;
|
||||
const info = server ? server.address() : undefined;
|
||||
const bound = info && typeof info === 'object' ? `${info.address}:${info.port}` : '';
|
||||
statusItem && (statusItem.text = `Copilot Bridge: OK ${bound ? `@ ${bound}` : ''}`);
|
||||
|
|
@ -246,6 +264,7 @@ async function getAccess(force = false): Promise<vscode.ChatAccess | undefined>
|
|||
return access;
|
||||
} catch (e: any) {
|
||||
access = undefined;
|
||||
lastReason = 'copilot_unavailable';
|
||||
const info = server ? server.address() : undefined;
|
||||
const bound = info && typeof info === 'object' ? `${info.address}:${info.port}` : '';
|
||||
statusItem && (statusItem.text = `Copilot Bridge: Unavailable ${bound ? `@ ${bound}` : ''}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue