mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-22 04:38:24 +08:00
feat: extension auth + token flow for server-integrated agent
Update Chrome extension to use Bearer auth on all sidebar endpoints: - background.js captures auth token from /health, exposes via getToken msg - background.js sets openPanelOnActionClick for direct side panel access - sidepanel.js gets token from background, sends in all fetch headers - Health broadcasts include token so sidebar auto-authenticates - Removes popup from manifest — icon click opens side panel directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -162,6 +162,11 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type === 'getToken') {
|
||||||
|
sendResponse({ token: authToken });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type === 'fetchRefs') {
|
if (msg.type === 'fetchRefs') {
|
||||||
fetchAndRelayRefs().then(() => sendResponse({ ok: true }));
|
fetchAndRelayRefs().then(() => sendResponse({ ok: true }));
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -13,9 +13,17 @@ const OBSERVE_COMMANDS = new Set(['snapshot', 'screenshot', 'diff', 'console', '
|
|||||||
let lastId = 0;
|
let lastId = 0;
|
||||||
let eventSource = null;
|
let eventSource = null;
|
||||||
let serverUrl = null;
|
let serverUrl = null;
|
||||||
|
let serverToken = null;
|
||||||
let chatLineCount = 0;
|
let chatLineCount = 0;
|
||||||
let chatPollInterval = null;
|
let chatPollInterval = null;
|
||||||
|
|
||||||
|
// Auth headers for sidebar endpoints
|
||||||
|
function authHeaders() {
|
||||||
|
const h = { 'Content-Type': 'application/json' };
|
||||||
|
if (serverToken) h['Authorization'] = `Bearer ${serverToken}`;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Chat ───────────────────────────────────────────────────────
|
// ─── Chat ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
const chatMessages = document.getElementById('chat-messages');
|
const chatMessages = document.getElementById('chat-messages');
|
||||||
@@ -225,9 +233,10 @@ sendBtn.addEventListener('click', sendMessage);
|
|||||||
|
|
||||||
// Poll for new chat messages
|
// Poll for new chat messages
|
||||||
async function pollChat() {
|
async function pollChat() {
|
||||||
if (!serverUrl) return;
|
if (!serverUrl || !serverToken) return;
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`${serverUrl}/sidebar-chat?after=${chatLineCount}`, {
|
const resp = await fetch(`${serverUrl}/sidebar-chat?after=${chatLineCount}`, {
|
||||||
|
headers: authHeaders(),
|
||||||
signal: AbortSignal.timeout(3000),
|
signal: AbortSignal.timeout(3000),
|
||||||
});
|
});
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) return;
|
||||||
@@ -246,7 +255,7 @@ async function pollChat() {
|
|||||||
document.getElementById('clear-chat').addEventListener('click', async () => {
|
document.getElementById('clear-chat').addEventListener('click', async () => {
|
||||||
if (!serverUrl) return;
|
if (!serverUrl) return;
|
||||||
try {
|
try {
|
||||||
await fetch(`${serverUrl}/sidebar-chat/clear`, { method: 'POST' });
|
await fetch(`${serverUrl}/sidebar-chat/clear`, { method: 'POST', headers: authHeaders() });
|
||||||
} catch {}
|
} catch {}
|
||||||
// Reset local state
|
// Reset local state
|
||||||
chatLineCount = 0;
|
chatLineCount = 0;
|
||||||
@@ -441,8 +450,9 @@ async function fetchRefs() {
|
|||||||
|
|
||||||
// ─── Server Discovery ───────────────────────────────────────────
|
// ─── Server Discovery ───────────────────────────────────────────
|
||||||
|
|
||||||
function updateConnection(url) {
|
function updateConnection(url, token) {
|
||||||
serverUrl = url;
|
serverUrl = url;
|
||||||
|
serverToken = token || null;
|
||||||
if (url) {
|
if (url) {
|
||||||
document.getElementById('footer-dot').className = 'dot connected';
|
document.getElementById('footer-dot').className = 'dot connected';
|
||||||
const port = new URL(url).port;
|
const port = new URL(url).port;
|
||||||
@@ -490,11 +500,14 @@ portInput.addEventListener('keydown', (e) => {
|
|||||||
|
|
||||||
// Try to connect immediately, retry every 2s until connected
|
// Try to connect immediately, retry every 2s until connected
|
||||||
function tryConnect() {
|
function tryConnect() {
|
||||||
chrome.runtime.sendMessage({ type: 'getServerUrl' }, (resp) => {
|
chrome.runtime.sendMessage({ type: 'getPort' }, (resp) => {
|
||||||
if (resp && resp.url) {
|
if (resp && resp.port && resp.connected) {
|
||||||
updateConnection(resp.url);
|
const url = `http://127.0.0.1:${resp.port}`;
|
||||||
|
// Get the token from background
|
||||||
|
chrome.runtime.sendMessage({ type: 'getToken' }, (tokenResp) => {
|
||||||
|
updateConnection(url, tokenResp?.token);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Retry in 2s
|
|
||||||
setTimeout(tryConnect, 2000);
|
setTimeout(tryConnect, 2000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -505,9 +518,12 @@ tryConnect();
|
|||||||
|
|
||||||
chrome.runtime.onMessage.addListener((msg) => {
|
chrome.runtime.onMessage.addListener((msg) => {
|
||||||
if (msg.type === 'health') {
|
if (msg.type === 'health') {
|
||||||
chrome.runtime.sendMessage({ type: 'getServerUrl' }, (resp) => {
|
if (msg.data) {
|
||||||
updateConnection(msg.data ? resp?.url : null);
|
const url = `http://127.0.0.1:${msg.data.port || 34567}`;
|
||||||
});
|
updateConnection(url, msg.data.token);
|
||||||
|
} else {
|
||||||
|
updateConnection(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (msg.type === 'refs') {
|
if (msg.type === 'refs') {
|
||||||
if (document.querySelector('.tab[data-tab="refs"].active')) {
|
if (document.querySelector('.tab[data-tab="refs"].active')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user