clementine-agent 1.12.1 → 1.12.2

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.
@@ -15444,7 +15444,14 @@ function navigateTo(page, opts) {
15444
15444
  if (bt === 'learning' && typeof refreshSelfImprove === 'function') refreshSelfImprove();
15445
15445
  break;
15446
15446
  case 'settings':
15447
- switchDestTab('settings', opts.tab || 'channels');
15447
+ // Settings tabs use the switchTab() system (id="tab-settings-<tab>"),
15448
+ // not switchDestTab's [data-tab] selector. Default tab name is
15449
+ // 'general' (the "Channels & Env" pane) to match the HTML id.
15450
+ switchTab('settings', opts.tab || 'general');
15451
+ // 'general' has no auto-refresh in switchTab — kick it manually so
15452
+ // the Channels & Env card actually loads from /api/settings instead
15453
+ // of stalling on "Loading settings…".
15454
+ if ((opts.tab || 'general') === 'general' && typeof refreshSettings === 'function') refreshSettings();
15448
15455
  break;
15449
15456
  }
15450
15457
 
@@ -15846,6 +15853,7 @@ function switchTab(group, tab) {
15846
15853
  if (tab === 'learning' && typeof refreshSelfImprove === 'function') refreshSelfImprove();
15847
15854
  }
15848
15855
  if (group === 'settings') {
15856
+ if (tab === 'general' && typeof refreshSettings === 'function') refreshSettings();
15849
15857
  if (tab === 'integrations') { refreshSalesforce(); refreshComposioConnections(); }
15850
15858
  if (tab === 'remote') refreshRemoteAccess();
15851
15859
  if (tab === 'security') refreshAuthSessions();
@@ -25936,9 +25944,13 @@ async function saveComposioApiKey() {
25936
25944
  }
25937
25945
 
25938
25946
  async function connectComposio(slug) {
25947
+ // Open a blank popup SYNCHRONOUSLY inside the click handler. Browsers
25948
+ // only let popups through if they're a direct user gesture — once we
25949
+ // hit the await below, the gesture is "consumed" and any later
25950
+ // window.open() gets silently blocked by Chrome/Safari/Firefox.
25951
+ // We'll redirect this blank window to the OAuth URL once we have it.
25952
+ var popup = window.open('about:blank', '_blank');
25939
25953
  try {
25940
- // Use apiFetch (not raw fetch) so the Authorization: Bearer header is
25941
- // attached — the /api/* middleware rejects unauth'd POSTs with 401.
25942
25954
  var res = await apiFetch('/api/composio/toolkits/' + encodeURIComponent(slug) + '/authorize', {
25943
25955
  method: 'POST',
25944
25956
  headers: { 'content-type': 'application/json' },
@@ -25947,27 +25959,61 @@ async function connectComposio(slug) {
25947
25959
  var d = await res.json();
25948
25960
  if (res.status === 409 && d.needsAuthConfig) {
25949
25961
  toast('This toolkit needs a BYO OAuth app — opening Composio dashboard.', 'warn');
25950
- window.open(d.setupUrl, '_blank');
25962
+ if (popup && !popup.closed) popup.location.href = d.setupUrl;
25963
+ else showOAuthLinkPrompt(slug, d.setupUrl);
25951
25964
  return;
25952
25965
  }
25953
25966
  if (!res.ok) {
25967
+ if (popup && !popup.closed) popup.close();
25954
25968
  var reason = d.error || ('HTTP ' + res.status);
25955
25969
  toast('Connect failed: ' + reason, 'error');
25956
25970
  console.error('[composio] connect failed', { slug: slug, status: res.status, body: d });
25957
25971
  return;
25958
25972
  }
25959
25973
  if (d.redirectUrl) {
25960
- window.open(d.redirectUrl, '_blank');
25961
- toast('Opened ' + slug + ' authorization in a new tab. Refresh after approving.', 'info');
25962
- // Soft poll to catch the new connection without forcing a manual refresh.
25974
+ if (popup && !popup.closed) {
25975
+ popup.location.href = d.redirectUrl;
25976
+ toast('Authorize ' + slug + ' in the new tab, then come back here.', 'info');
25977
+ } else {
25978
+ // Popup got blocked even with the synchronous-open trick (some
25979
+ // browsers / extensions block about:blank too). Fall back to a
25980
+ // visible "click to open" dialog — that click is a direct gesture
25981
+ // and reliably bypasses the blocker.
25982
+ showOAuthLinkPrompt(slug, d.redirectUrl);
25983
+ }
25963
25984
  setTimeout(refreshComposioConnections, 5000);
25964
25985
  setTimeout(refreshComposioConnections, 15000);
25965
25986
  setTimeout(refreshComposioConnections, 30000);
25966
25987
  } else {
25988
+ if (popup && !popup.closed) popup.close();
25967
25989
  toast('Connected ' + slug, 'success');
25968
25990
  refreshComposioConnections();
25969
25991
  }
25970
- } catch (e) { toast('Connect failed: ' + e, 'error'); }
25992
+ } catch (e) {
25993
+ if (popup && !popup.closed) popup.close();
25994
+ toast('Connect failed: ' + e, 'error');
25995
+ }
25996
+ }
25997
+
25998
+ function showOAuthLinkPrompt(slug, url) {
25999
+ // Renders a modal with a clickable link. User clicking the link is a
26000
+ // direct gesture, so the new tab will open even when popup blockers are
26001
+ // aggressive. Used as the fallback when window.open returns null.
26002
+ var existing = document.getElementById('composio-oauth-prompt');
26003
+ if (existing) existing.remove();
26004
+ var overlay = document.createElement('div');
26005
+ overlay.id = 'composio-oauth-prompt';
26006
+ overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center';
26007
+ overlay.innerHTML =
26008
+ '<div style="background:var(--bg-primary,#1e1e1e);border:1px solid var(--border);border-radius:8px;padding:20px;max-width:480px;width:90%;box-shadow:0 8px 32px rgba(0,0,0,0.4)">' +
26009
+ '<div style="font-size:14px;font-weight:600;margin-bottom:8px">Authorize ' + esc(slug) + '</div>' +
26010
+ '<div style="font-size:12px;color:var(--text-muted);margin-bottom:14px;line-height:1.5">Your browser blocked the popup. Click below to open the authorization page in a new tab — after approving, come back here and the connection will show up within a few seconds.</div>' +
26011
+ '<div style="display:flex;gap:8px;justify-content:flex-end">' +
26012
+ '<button class="btn-sm" onclick="document.getElementById(\\'composio-oauth-prompt\\').remove()" style="padding:6px 12px;background:transparent;border:1px solid var(--border);color:var(--text-primary);border-radius:4px;cursor:pointer;font-size:12px">Cancel</button>' +
26013
+ '<a href="' + esc(url) + '" target="_blank" rel="noopener" onclick="setTimeout(function(){var e=document.getElementById(\\'composio-oauth-prompt\\');if(e)e.remove();},500)" class="btn btn-sm btn-primary" style="text-decoration:none;padding:6px 14px;display:inline-block;font-size:12px">Open authorization</a>' +
26014
+ '</div>' +
26015
+ '</div>';
26016
+ document.body.appendChild(overlay);
25971
26017
  }
25972
26018
 
25973
26019
  async function disconnectComposio(slug, connectionId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.12.1",
3
+ "version": "1.12.2",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",