promethios-bridge 2.1.1 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promethios-bridge",
3
- "version": "2.1.1",
3
+ "version": "2.1.4",
4
4
  "description": "Run Promethios agent frameworks locally on your computer with full file, terminal, browser access, ambient context capture, and the always-on-top floating chat overlay. Native Framework Mode supports OpenClaw and other frameworks via the bridge.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/bridge.js CHANGED
@@ -210,6 +210,32 @@ async function startBridge({ setupToken, apiBase, port, dev }) {
210
210
  // Health check
211
211
  app.get('/health', (req, res) => res.json({ ok: true, version: require('../package.json').version }));
212
212
 
213
+ // ── /open-external ─────────────────────────────────────────────────────────
214
+ // POST /open-external?url=<encoded-url>
215
+ // Called by the Promethios web app when the user clicks "Open [Provider] ↗"
216
+ // in a provider thread. Opens the URL in the user's real default browser.
217
+ // No auth required — only accessible from localhost.
218
+ app.post('/open-external', (req, res) => {
219
+ const url = (req.query.url || req.body?.url || '').trim();
220
+ if (!url || !/^https?:\/\//.test(url)) {
221
+ return res.status(400).json({ error: 'Invalid or missing url parameter' });
222
+ }
223
+ const { exec } = require('child_process');
224
+ const platform = process.platform;
225
+ let cmd;
226
+ if (platform === 'win32') {
227
+ cmd = `start "" "${url.replace(/"/g, '')}"`;
228
+ } else if (platform === 'darwin') {
229
+ cmd = `open "${url.replace(/"/g, '')}"`;
230
+ } else {
231
+ cmd = `xdg-open "${url.replace(/"/g, '')}"`;
232
+ }
233
+ exec(cmd, (err) => {
234
+ if (err) console.error('[open-external] Failed to open URL:', err.message);
235
+ });
236
+ res.json({ status: 'ok', url });
237
+ });
238
+
213
239
  // ── /status: used by the Electron overlay to auto-connect without manual token entry ──
214
240
  // Only accessible from localhost (127.0.0.1 or ::1) for security.
215
241
  let bridgeUsername = null; // set after registerBridge resolves
@@ -1250,7 +1250,7 @@
1250
1250
  if (hint) hint.style.display = 'none';
1251
1251
  }
1252
1252
 
1253
- async function startOAuthFlow(provider) {
1253
+ function startOAuthFlow(provider) {
1254
1254
  hideProviderLoginHint();
1255
1255
 
1256
1256
  if (!authToken) {
@@ -1262,56 +1262,22 @@
1262
1262
  return;
1263
1263
  }
1264
1264
 
1265
- const info = PROVIDER_LABELS[provider] || { name: provider, color: 'rgba(255,255,255,0.1)', text: '#e2e8f0' };
1266
1265
  const btn = document.querySelector(`.btn-connect[data-provider="${provider}"]`);
1267
- if (btn) { btn.textContent = 'Connecting…'; btn.disabled = true; }
1266
+ if (btn) { btn.textContent = 'Opening…'; btn.disabled = true; }
1268
1267
 
1269
- try {
1270
- const res = await fetch(`${RELAY_URL}/api/mcp/oauth/auto-connect`, {
1271
- method: 'POST',
1272
- headers: { 'Content-Type': 'application/json' },
1273
- body: JSON.stringify({ bridge_token: authToken, client_id: provider }),
1274
- });
1275
- const data = await res.json();
1268
+ // Open OAuth consent page in the user's REAL system browser (Chrome/Edge/Firefox).
1269
+ // shell.openExternal() is the only way that passes Google/Meta's "secure browser" check.
1270
+ // The main process starts a localhost callback server, opens the system browser with the
1271
+ // Promethios OAuth URL, and fires 'oauth-complete' back to us when the token arrives.
1272
+ window.promethios.openOAuth(provider);
1276
1273
 
1277
- if (!data.success) {
1278
- const errMsg = (data.error || '').toLowerCase();
1279
- const isLoginRequired = errMsg.includes('login') || errMsg.includes('not logged') ||
1280
- errMsg.includes('auth') || errMsg.includes('session') ||
1281
- errMsg.includes('sign in') || res.status === 401;
1282
-
1283
- if (isLoginRequired) {
1284
- const loginUrl = PROVIDER_LOGIN_URLS[provider] || `https://${provider}.com`;
1285
- showProviderLoginHint(provider,
1286
- `<strong>Sign in to ${info.name} first.</strong><br>` +
1287
- `Click <strong>Open ↗</strong> next to ${info.name} to sign in, then click <strong>Connect</strong> again.`
1288
- );
1289
- window.promethios.openExternal(loginUrl);
1290
- } else {
1291
- showToast(`Connect failed: ${data.error || 'Unknown error'}`);
1292
- }
1293
- if (btn) { btn.textContent = 'Connect'; btn.disabled = false; }
1294
- return;
1274
+ // Re-enable the button after a short delay so the user can retry if needed
1275
+ setTimeout(() => {
1276
+ if (btn && btn.disabled) {
1277
+ btn.textContent = 'Connect';
1278
+ btn.disabled = false;
1295
1279
  }
1296
-
1297
- connectedProviders[provider] = { token: data.token, clientId: provider, ts: Date.now() };
1298
- updateProviderButtons();
1299
- hideProviderLoginHint();
1300
-
1301
- modalProviderName.textContent = info.name;
1302
- modalProviderBadge.textContent = info.name;
1303
- modalProviderBadge.style.background = info.color;
1304
- modalProviderBadge.style.color = info.text;
1305
- modalPromptText.textContent = data.prompt || buildPromptText(provider, data.token);
1306
- modalCopyBtn.textContent = '⌘ Copy Prompt';
1307
- modalCopyBtn.classList.remove('copied');
1308
- promptModal.classList.add('open');
1309
- showToast(`✓ ${info.name} connected!`);
1310
-
1311
- } catch (err) {
1312
- showToast('Network error — check your connection and try again.');
1313
- if (btn) { btn.textContent = 'Connect'; btn.disabled = false; }
1314
- }
1280
+ }, 8000);
1315
1281
  }
1316
1282
 
1317
1283
  // Called by main process after OAuth completes successfully