synabun 2.0.5 → 2.0.7
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/CHANGELOG.md +40 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/shared.mjs +6 -3
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/packaged-runtime.js +138 -11
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tui.js +2 -1
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +627 -57
- package/neural-interface/i18n/pt-BR.json +626 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +255 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +307 -47
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +120 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +16 -3
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-title-generator.js +4 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +124 -25
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +15 -3
- package/neural-interface/public/shared/assistant/asst-panel.js +48 -14
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +99 -17
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +48 -107
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +128 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +261 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +32 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +118 -0
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +278 -31
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +212 -213
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +6 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/server.js +1219 -608
- package/package.json +4 -1
- package/setup.js +9 -8
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { providerAuthSnapshot } from './ui-provider-auth.js';
|
|
2
|
+
import { mountProviderTerminalAuth, providerApiKeyRequest, startProviderTerminalLogin } from './ui-provider-terminal-auth.js';
|
|
3
|
+
import { accountName, accountSwitchPlan, accountText, tabAccountId, serverAccountId } from './account-view.js';
|
|
4
|
+
import { mountAccountMenu } from './account-menu.js';
|
|
5
|
+
import './account-i18n.js';
|
|
1
6
|
import { hostedSidepanelId, sidepanelHost } from './ui-sidepanel-runtime.js';
|
|
2
7
|
// ═══════════════════════════════════════════
|
|
3
8
|
// SynaBun — Claude Code Panel (Main Area)
|
|
@@ -12,7 +17,7 @@ import { fetchClaudeSessions, fetchBrowserSessions, searchSessions } from './api
|
|
|
12
17
|
import { createFrameRenderer } from './utils.js';
|
|
13
18
|
import { notify, NOTIF_TYPE } from './ui-notifications.js';
|
|
14
19
|
import { registerSidepanel, setSidepanelVisible, syncSidepanelLayout, focusSidepanel, openAnotherSidepanelSession, isHostedSessionFocused } from './ui-sidepanel-windows.js';
|
|
15
|
-
import {
|
|
20
|
+
import { mountCliBanner } from './ui-cli-banner.js';
|
|
16
21
|
import { nativeLoopWindowId } from './ui-native-window-id.js';
|
|
17
22
|
import { createStickyScrollController } from './ui-scroll-follow.js';
|
|
18
23
|
import { requestGeneratedSessionTitle } from './session-title.js';
|
|
@@ -166,9 +171,8 @@ function activeTab() { return _tabs[_activeTabIdx] || null; }
|
|
|
166
171
|
let _browserEmbed = null; // { sessionId, ws, canvas, ctx, urlBar, container }
|
|
167
172
|
let _browserEmbedVisible = false;
|
|
168
173
|
|
|
169
|
-
// ── CLI
|
|
170
|
-
let
|
|
171
|
-
let _cliUnsub = null;
|
|
174
|
+
// ── CLI status (claude binary detection): the banner controller, ui-cli-banner.js ──
|
|
175
|
+
let _cliBanner = null;
|
|
172
176
|
|
|
173
177
|
// Marked.js (lazy loaded)
|
|
174
178
|
let _marked = null;
|
|
@@ -308,7 +312,7 @@ const SLASH_COMMANDS = [
|
|
|
308
312
|
{ name: 'plugin', desc: 'List active plugins and skills', kind: 'info' },
|
|
309
313
|
{ name: 'status', desc: 'Show session status summary', kind: 'info' },
|
|
310
314
|
{ name: 'cost', desc: 'Toggle the cost widget', kind: 'action' },
|
|
311
|
-
{ name: 'login', desc: '
|
|
315
|
+
{ name: 'login', desc: 'Sign this tab\'s Claude account in (opens the account menu)', kind: 'action' },
|
|
312
316
|
{ name: 'logout', desc: 'Open Claude account sign-out', kind: 'action' },
|
|
313
317
|
{ name: 'plan', desc: 'Toggle plan mode', kind: 'action' },
|
|
314
318
|
{ name: 'btw', desc: 'Add context while Claude is processing (Shift+Enter)', kind: 'info' },
|
|
@@ -322,7 +326,7 @@ const SLASH_COMMANDS = [
|
|
|
322
326
|
{ name: 'reload-plugins', desc: 'Reload plugins without restarting the session', kind: 'action', needs: 'reload_plugins' },
|
|
323
327
|
{ name: 'tasks', desc: 'Background work of this tab: running tasks (with stop) and scheduled wakeups', kind: 'info' },
|
|
324
328
|
{ name: 'usage', desc: 'This session\'s cost and the plan\'s usage limits', kind: 'info', needs: 'session_requests' },
|
|
325
|
-
{ name: 'account', desc: 'Choose the Claude account this tab
|
|
329
|
+
{ name: 'account', desc: 'Choose and manage the Claude account of this tab (the header account menu)', kind: 'action', needs: 'accounts' },
|
|
326
330
|
{ name: 'effective-settings', desc: 'The Claude Code settings in effect here, and the file each comes from', kind: 'info', needs: 'settings_view' },
|
|
327
331
|
];
|
|
328
332
|
|
|
@@ -544,72 +548,7 @@ function injectStyles() {
|
|
|
544
548
|
.cp-messages:has(.msg) .cp-empty,
|
|
545
549
|
.cp-messages:has(.msg-status) .cp-empty { display: none; }
|
|
546
550
|
|
|
547
|
-
/* ── CLI
|
|
548
|
-
.cp-cli-banner {
|
|
549
|
-
flex-shrink: 0;
|
|
550
|
-
display: grid;
|
|
551
|
-
grid-template-columns: auto 1fr auto;
|
|
552
|
-
gap: 10px;
|
|
553
|
-
align-items: center;
|
|
554
|
-
margin: 6px 8px 0;
|
|
555
|
-
padding: 10px 12px;
|
|
556
|
-
border: 1px solid rgba(255,180,80,0.45);
|
|
557
|
-
border-radius: 8px;
|
|
558
|
-
background: rgba(255,180,80,0.08);
|
|
559
|
-
color: rgba(255,235,200,0.92);
|
|
560
|
-
font-size: 11px;
|
|
561
|
-
line-height: 1.4;
|
|
562
|
-
pointer-events: auto;
|
|
563
|
-
position: relative;
|
|
564
|
-
z-index: 5;
|
|
565
|
-
}
|
|
566
|
-
.cp-cli-banner-icon {
|
|
567
|
-
width: 22px; height: 22px;
|
|
568
|
-
display: flex; align-items: center; justify-content: center;
|
|
569
|
-
border-radius: 50%;
|
|
570
|
-
background: rgba(255,180,80,0.25);
|
|
571
|
-
color: rgba(255,210,140,1);
|
|
572
|
-
font-weight: 700;
|
|
573
|
-
}
|
|
574
|
-
.cp-cli-banner-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
575
|
-
.cp-cli-banner-title {
|
|
576
|
-
font-size: 12px; font-weight: 600;
|
|
577
|
-
color: rgba(255,210,140,0.95);
|
|
578
|
-
}
|
|
579
|
-
.cp-cli-banner-body {
|
|
580
|
-
font-size: 11px;
|
|
581
|
-
color: rgba(255,255,255,0.7);
|
|
582
|
-
word-break: break-word;
|
|
583
|
-
}
|
|
584
|
-
.cp-cli-banner-body code {
|
|
585
|
-
padding: 1px 5px;
|
|
586
|
-
background: rgba(0,0,0,0.35);
|
|
587
|
-
border-radius: 3px;
|
|
588
|
-
font-size: 10.5px;
|
|
589
|
-
color: rgba(255,255,255,0.85);
|
|
590
|
-
}
|
|
591
|
-
.cp-cli-banner-actions { display: flex; gap: 6px; }
|
|
592
|
-
.cp-cli-banner-link, .cp-cli-banner-recheck {
|
|
593
|
-
display: inline-flex; align-items: center; gap: 4px;
|
|
594
|
-
padding: 4px 9px;
|
|
595
|
-
font-size: 11px;
|
|
596
|
-
font-weight: 500;
|
|
597
|
-
border-radius: 4px;
|
|
598
|
-
border: 1px solid rgba(255,255,255,0.2);
|
|
599
|
-
background: rgba(255,255,255,0.05);
|
|
600
|
-
color: rgba(255,255,255,0.9);
|
|
601
|
-
text-decoration: none;
|
|
602
|
-
cursor: pointer;
|
|
603
|
-
user-select: none;
|
|
604
|
-
font-family: inherit;
|
|
605
|
-
}
|
|
606
|
-
.cp-cli-banner-link:hover, .cp-cli-banner-recheck:hover {
|
|
607
|
-
background: rgba(255,255,255,0.12);
|
|
608
|
-
border-color: rgba(255,255,255,0.4);
|
|
609
|
-
}
|
|
610
|
-
.cp-cli-banner-recheck:disabled { opacity: 0.5; cursor: default; }
|
|
611
|
-
.claude-panel.cp-cli-blocked .cp-send { opacity: 0.4 !important; cursor: not-allowed !important; }
|
|
612
|
-
.claude-panel.cp-cli-blocked .cp-input { opacity: 0.7; }
|
|
551
|
+
/* ── CLI banner (ui-cli-banner.js): its rules are in cp/cp-styles.js ── */
|
|
613
552
|
|
|
614
553
|
/* ── Browser embed (live screencast inside panel) ── */
|
|
615
554
|
.cp-browser-embed {
|
|
@@ -2975,6 +2914,7 @@ function connectTab(tab) {
|
|
|
2975
2914
|
ws.addEventListener('open', () => {
|
|
2976
2915
|
if (tab.ws !== ws) return;
|
|
2977
2916
|
clearTimeout(tab.reconnectTimer);
|
|
2917
|
+
_ensureClaudeTerminalAuth(tab);
|
|
2978
2918
|
connectionOpened(tab); // no statement of the tab's mode was made on this socket yet
|
|
2979
2919
|
// Reclaim this tab's server session if it outlived the old socket — a turn in
|
|
2980
2920
|
// flight, a permission card, background agents or a scheduled wakeup. The
|
|
@@ -3046,6 +2986,7 @@ function switchTab(idx) {
|
|
|
3046
2986
|
tab.messagesEl._exitToken = (tab.messagesEl._exitToken || 0) + 1; // cancel pending exits
|
|
3047
2987
|
_attachMessagesEl(tab);
|
|
3048
2988
|
tab.messagesEl.style.display = '';
|
|
2989
|
+
_ensureClaudeTerminalAuth(tab);
|
|
3049
2990
|
tab.scrollController?.setActive(true);
|
|
3050
2991
|
tab.messagesEl.classList.remove('cp-tab-exit');
|
|
3051
2992
|
tab.messagesEl.classList.add('cp-tab-enter');
|
|
@@ -3127,6 +3068,7 @@ function switchTab(idx) {
|
|
|
3127
3068
|
_setCompactingUI(!!tab.compacting);
|
|
3128
3069
|
populateViewDropdown(_panel?.querySelector('#cp-view'), tab);
|
|
3129
3070
|
populateModeDropdown(_panel?.querySelector('#cp-mode'), tab);
|
|
3071
|
+
_renderAccountMenu();
|
|
3130
3072
|
_paintTemporary(tab);
|
|
3131
3073
|
saveTabs();
|
|
3132
3074
|
}
|
|
@@ -3149,6 +3091,8 @@ function closeTab(idx, { detachAutomation = false } = {}) {
|
|
|
3149
3091
|
if (tab.automationRunId) forgetWorkerEnd(tab.automationRunId);
|
|
3150
3092
|
flushSessionSnapshotSave(tab);
|
|
3151
3093
|
recordHookEvent(tab, 'SessionEnd', tab.sessionId || '(new)');
|
|
3094
|
+
tab.authView?.dispose();
|
|
3095
|
+
tab.terminalAuthView?.dispose();
|
|
3152
3096
|
tab.closed = true;
|
|
3153
3097
|
tab.titleRequestController?.abort();
|
|
3154
3098
|
tab.titleRequestController = null;
|
|
@@ -4479,7 +4423,7 @@ function writeSessionSnapshot(tab) {
|
|
|
4479
4423
|
const sid = tab?.sessionId || null;
|
|
4480
4424
|
const $msgs = tab?.messagesEl || null;
|
|
4481
4425
|
if (!sid || !$msgs) return;
|
|
4482
|
-
const html = $msgs
|
|
4426
|
+
const html = providerAuthSnapshot($msgs);
|
|
4483
4427
|
if (!html.trim()) return;
|
|
4484
4428
|
// Skip snapshot if the transcript only contains the empty-state placeholder
|
|
4485
4429
|
if ($msgs.children.length === 1 && $msgs.firstElementChild?.classList?.contains('cp-empty')) return;
|
|
@@ -5134,7 +5078,7 @@ function _contextMenuData(tab, { full = false } = {}) {
|
|
|
5134
5078
|
folder: tab.init?.cwd || tab.project || '',
|
|
5135
5079
|
// The mode as the project bar words it (it knows Bypass and "from settings").
|
|
5136
5080
|
mode: ($mode && !$mode.hidden && $mode.querySelector('.cp-dd-label')?.textContent) || tab.permissionMode || '',
|
|
5137
|
-
account: [tab.accountInfo?.email, tab.accountInfo?.organization].filter(Boolean).join(' · '),
|
|
5081
|
+
account: [tab.accountInfo?.authMethod === 'apiKey' ? 'API key' : tab.accountInfo?.email, tab.accountInfo?.organization].filter(Boolean).join(' · '),
|
|
5138
5082
|
flags: sessionFlags(tab.session, { toolPolicy: tab.toolPolicy, fastModeState: tab.fastModeState, account: tab.accountId }),
|
|
5139
5083
|
limit: tab.rateLimit?.text || '',
|
|
5140
5084
|
backgroundTasks: (tab.backgroundWork || []).length,
|
|
@@ -5323,6 +5267,7 @@ function _processTabMsg(tab, msg) {
|
|
|
5323
5267
|
if (tab === activeTab()) {
|
|
5324
5268
|
renderStatusline(tab);
|
|
5325
5269
|
populateModeDropdown(_panel?.querySelector('#cp-mode'), tab);
|
|
5270
|
+
_renderAccountMenu();
|
|
5326
5271
|
}
|
|
5327
5272
|
break;
|
|
5328
5273
|
}
|
|
@@ -5546,6 +5491,8 @@ function _processTabMsg(tab, msg) {
|
|
|
5546
5491
|
break;
|
|
5547
5492
|
}
|
|
5548
5493
|
appendError(tab, msg.message);
|
|
5494
|
+
// The tab's account is gone, or belongs to another conversation: the accounts card is where to choose.
|
|
5495
|
+
if (msg.code === 'account_unavailable' || msg.code === 'account_change_refused') _showAccounts(tab);
|
|
5549
5496
|
notify('panel', NOTIF_TYPE.ERROR, tab.label || 'Claude Code', { tabId: tab.id });
|
|
5550
5497
|
if (typeof msg.message === 'string' && /Claude CLI not found|ENOENT/i.test(msg.message)) {
|
|
5551
5498
|
flagClaudeCliInstallFailure(msg.message);
|
|
@@ -7567,6 +7514,7 @@ function _afterTemporary(tab, sid, label) {
|
|
|
7567
7514
|
for (const waiting of tab._sessionRequests?.values() || []) { clearTimeout(waiting.timer); try { waiting.reject(new Error('The temporary chat has ended.')); } catch {} }
|
|
7568
7515
|
// An open history search lists its prompts.
|
|
7569
7516
|
if (tab === activeTab()) _panel?.querySelector('.cp-rsearch')?.remove();
|
|
7517
|
+
tab.terminalAuthView?.dispose();
|
|
7570
7518
|
tabAfterTemporary(tab, newTabState(sid, label || 'New chat', tab.id));
|
|
7571
7519
|
}
|
|
7572
7520
|
|
|
@@ -8617,47 +8565,149 @@ function _showUsage(tab) {
|
|
|
8617
8565
|
}).catch(err => appendInfoCard(tab, { title: 'Usage', kind: 'info', body: err.message }));
|
|
8618
8566
|
}
|
|
8619
8567
|
|
|
8620
|
-
//
|
|
8568
|
+
// ── Claude accounts ──
|
|
8569
|
+
// Every account and its state come from the server (lib/claude-accounts.js
|
|
8570
|
+
// describe(): its mode, whether it is signed in, its identity: an email or a
|
|
8571
|
+
// masked key, never a key); account-view.js decides the words. One list for
|
|
8572
|
+
// every tab, and one place to choose and manage accounts: the account menu in
|
|
8573
|
+
// the header (account-menu.js, the same component as the Codex panel's).
|
|
8574
|
+
// /account, /login and the sign-in screen's "Other accounts…" open it.
|
|
8575
|
+
// `claude:accounts-changed` (any window, any surface) reloads the list.
|
|
8621
8576
|
// A conversation belongs to the account it started under (its transcript lives
|
|
8622
|
-
// in that account's config directory)
|
|
8623
|
-
//
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8577
|
+
// in that account's config directory): a tab with one opens another account in
|
|
8578
|
+
// a new chat, and the tab's own account changes only before its first message.
|
|
8579
|
+
let _claudeAccountRows = null;
|
|
8580
|
+
let _claudeAccountsLoad = null;
|
|
8581
|
+
let _claudeAccountsError = '';
|
|
8582
|
+
let _accountMenu = null;
|
|
8583
|
+
function _loadClaudeAccounts({ force = false, refresh = false } = {}) {
|
|
8584
|
+
if (!force && !refresh && _claudeAccountRows) return Promise.resolve(_claudeAccountRows);
|
|
8585
|
+
if (_claudeAccountsLoad && !refresh) return _claudeAccountsLoad;
|
|
8586
|
+
const load = fetch(`/api/claude/accounts${refresh ? '?refresh=true' : ''}`, { cache: 'no-store' })
|
|
8587
|
+
.then(r => r.ok ? r.json() : Promise.reject(new Error(accountText('readFailed'))))
|
|
8588
|
+
.then((d) => {
|
|
8589
|
+
_claudeAccountRows = Array.isArray(d?.accounts) ? d.accounts : [];
|
|
8590
|
+
_claudeAccountsError = '';
|
|
8591
|
+
_claudeAccountsChanged();
|
|
8592
|
+
return _claudeAccountRows;
|
|
8593
|
+
})
|
|
8594
|
+
.catch((error) => { _claudeAccountsError = accountText('readFailed'); _claudeAccountsChanged(); throw error; })
|
|
8595
|
+
.finally(() => { if (_claudeAccountsLoad === load) _claudeAccountsLoad = null; });
|
|
8596
|
+
_claudeAccountsLoad = load;
|
|
8597
|
+
return load;
|
|
8598
|
+
}
|
|
8599
|
+
function _claudeAccountRow(id) {
|
|
8600
|
+
return (_claudeAccountRows || []).find(row => row.id === serverAccountId(id)) || null;
|
|
8601
|
+
}
|
|
8602
|
+
function _claudeAccountsChanged() {
|
|
8603
|
+
_renderAccountMenu();
|
|
8604
|
+
}
|
|
8605
|
+
|
|
8606
|
+
// The header account menu: the active tab's account and what it runs on, every account, and their
|
|
8607
|
+
// management (add, sign in with Claude, an API key, the "Uses" switch, sign out, rename, remove).
|
|
8608
|
+
function _mountAccountMenu() {
|
|
8609
|
+
if (_accountMenu || !_panel) return _accountMenu;
|
|
8610
|
+
const actions = _panel.querySelector('.cp-header-actions');
|
|
8611
|
+
if (!actions) return null;
|
|
8612
|
+
const path = id => `/api/claude/accounts/${encodeURIComponent(id)}`;
|
|
8613
|
+
_accountMenu = mountAccountMenu({
|
|
8614
|
+
provider: 'claude', container: actions, before: actions.firstChild, layer: () => _panel,
|
|
8615
|
+
read: () => {
|
|
8616
|
+
const tab = activeTab();
|
|
8617
|
+
return {
|
|
8618
|
+
rows: _claudeAccountRows || [], currentId: tab?.accountId || '', hasConversation: !!tab?.sessionId,
|
|
8619
|
+
available: !!tab && hasCapability(tab, 'accounts') && !tab.automationActive,
|
|
8620
|
+
busy: !!(tab?.running || tab?.automationActive), loading: !_claudeAccountRows, error: _claudeAccountsError,
|
|
8621
|
+
};
|
|
8622
|
+
},
|
|
8623
|
+
actions: {
|
|
8624
|
+
load: ({ refresh }) => _loadClaudeAccounts({ force: true, refresh }),
|
|
8625
|
+
pick: (id) => { const tab = activeTab(); if (tab) _pickAccount(tab, id); },
|
|
8626
|
+
add: async () => (await _claudeAuthRequest('/api/claude/accounts', { method: 'POST', body: '{}' })).account,
|
|
8627
|
+
signIn: (id) => startProviderTerminalLogin('claude', id, `Claude sign-in · ${_claudeAccountRow(id)?.label || id}`),
|
|
8628
|
+
saveKey: (id, key) => providerApiKeyRequest('claude', id, key),
|
|
8629
|
+
setMode: (id, mode) => _claudeAuthRequest(path(id), { method: 'PATCH', body: JSON.stringify({ mode }) }),
|
|
8630
|
+
signOut: (id, credential) => _claudeAuthRequest(`${path(id)}/auth/logout`, { method: 'POST', body: JSON.stringify({ credential }) }),
|
|
8631
|
+
rename: (id, label) => _claudeAuthRequest(path(id), { method: 'PATCH', body: JSON.stringify({ label }) }),
|
|
8632
|
+
remove: (id) => _claudeAuthRequest(path(id), { method: 'DELETE' }),
|
|
8633
|
+
},
|
|
8634
|
+
});
|
|
8635
|
+
return _accountMenu;
|
|
8636
|
+
}
|
|
8637
|
+
function _renderAccountMenu() {
|
|
8638
|
+
if (!_accountMenu) return;
|
|
8639
|
+
const tab = activeTab();
|
|
8640
|
+
if (tab && hasCapability(tab, 'accounts') && !_claudeAccountRows && !_claudeAccountsLoad) _loadClaudeAccounts().catch(() => {});
|
|
8641
|
+
_accountMenu.render();
|
|
8642
|
+
}
|
|
8643
|
+
|
|
8644
|
+
// Pick an account for a tab: now when it has no conversation yet, else in a new chat (a new tab).
|
|
8645
|
+
function _pickAccount(tab, id) {
|
|
8646
|
+
const rows = _claudeAccountRows || [];
|
|
8647
|
+
const plan = accountSwitchPlan({ rows, currentId: tab.accountId || '', targetId: id, hasConversation: !!tab.sessionId });
|
|
8648
|
+
const target = _claudeAccountRow(id);
|
|
8649
|
+
if (plan.effect === 'missing' || plan.effect === 'same') { appendStatus(tab, plan.message); return; }
|
|
8650
|
+
if (plan.effect === 'next-chat') {
|
|
8651
|
+
if (_tabs.length >= MAX_TABS) { appendStatus(tab, `${plan.message} ${accountText('tooManyTabs')}`); return; }
|
|
8652
|
+
const fresh = createTab(null, 'New chat');
|
|
8653
|
+
fresh.accountId = tabAccountId(id);
|
|
8654
|
+
_ensureClaudeTerminalAuth(fresh);
|
|
8655
|
+
saveTabs();
|
|
8656
|
+
if (fresh === activeTab()) _renderAccountMenu();
|
|
8657
|
+
appendStatus(fresh, accountText('newChatUnder', { label: target ? accountName(target) : id }));
|
|
8658
|
+
return;
|
|
8659
|
+
}
|
|
8660
|
+
tab.accountId = tabAccountId(id);
|
|
8661
|
+
_ensureClaudeTerminalAuth(tab);
|
|
8662
|
+
saveTabs();
|
|
8663
|
+
if (tab === activeTab()) { renderStatusline(tab); _renderAccountMenu(); }
|
|
8664
|
+
appendStatus(tab, plan.message);
|
|
8665
|
+
}
|
|
8666
|
+
|
|
8667
|
+
// /account (and /login, and the sign-in screen's "Other accounts…"): the header account menu.
|
|
8668
|
+
// `/account <name>` picks that account at once; `manage` opens one account's management.
|
|
8669
|
+
function _showAccounts(tab, wanted = '', { manage = '' } = {}) {
|
|
8670
|
+
if (!hasCapability(tab, 'accounts')) { appendStatus(tab, accountText('needsServer')); return; }
|
|
8671
|
+
if (wanted) {
|
|
8672
|
+
_loadClaudeAccounts({ force: true }).then((accounts) => {
|
|
8639
8673
|
const w = wanted.trim().toLowerCase();
|
|
8640
|
-
const match = accounts.find(a => a.id.toLowerCase() === w || String(a.label || '').toLowerCase() === w || String(a.email || '').toLowerCase() === w);
|
|
8674
|
+
const match = accounts.find(a => a.id.toLowerCase() === w || String(a.label || '').toLowerCase() === w || String(a.subscription?.email || '').toLowerCase() === w);
|
|
8641
8675
|
if (!match) { appendStatus(tab, `No account "${wanted}". Use /account to list them.`); return; }
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8676
|
+
_pickAccount(tab, match.id);
|
|
8677
|
+
}).catch(() => appendStatus(tab, accountText('readFailed')));
|
|
8678
|
+
return;
|
|
8679
|
+
}
|
|
8680
|
+
if (tab !== activeTab()) return;
|
|
8681
|
+
_mountAccountMenu()?.open(manage ? { manage } : {});
|
|
8682
|
+
}
|
|
8683
|
+
|
|
8684
|
+
async function _claudeAuthRequest(url, options = {}) {
|
|
8685
|
+
const response = await fetch(url, { ...options, cache: 'no-store', headers: { 'Content-Type': 'application/json' } });
|
|
8686
|
+
const data = await response.json().catch(() => ({}));
|
|
8687
|
+
if (!response.ok || data.error) throw new Error(data.error || 'Claude authentication request failed.');
|
|
8688
|
+
return data;
|
|
8689
|
+
}
|
|
8690
|
+
|
|
8691
|
+
function _ensureClaudeTerminalAuth(tab) {
|
|
8692
|
+
const accountId = tab.accountId || 'default';
|
|
8693
|
+
if (tab.closed || !tab.messagesEl) return;
|
|
8694
|
+
if (tab.terminalAuthView && tab.terminalAuthAccount === accountId) { void tab.terminalAuthView.refresh(); return; }
|
|
8695
|
+
tab.terminalAuthView?.dispose();
|
|
8696
|
+
tab.terminalAuthAccount = accountId;
|
|
8697
|
+
const named = accountId !== 'default' ? _claudeAccountRow(accountId) : null;
|
|
8698
|
+
tab.terminalAuthView = mountProviderTerminalAuth({
|
|
8699
|
+
host: tab.messagesEl, provider: 'claude', accountId, logo: CLAUDE_ICON,
|
|
8700
|
+
...(named ? { message: `Sign in to Claude to use ${named.label}.` } : {}),
|
|
8701
|
+
// Another account (or a new one) is one click away from the sign-in screen: the header account menu.
|
|
8702
|
+
moreAction: { label: accountText('otherAccounts'), run: () => _showAccounts(tab) },
|
|
8703
|
+
isCurrent: () => !tab.closed && (tab.accountId || 'default') === accountId,
|
|
8704
|
+
readAccount: async () => {
|
|
8705
|
+
const account = (await _claudeAuthRequest(`/api/claude/accounts/${encodeURIComponent(accountId)}/auth?refresh=true`)).account;
|
|
8706
|
+
if (!tab.closed && (tab.accountId || 'default') === accountId) tab.accountInfo = account;
|
|
8707
|
+
return account;
|
|
8708
|
+
},
|
|
8709
|
+
onSignedIn: () => { _loadClaudeAccounts({ force: true }).catch(() => {}); },
|
|
8710
|
+
});
|
|
8661
8711
|
}
|
|
8662
8712
|
|
|
8663
8713
|
// /mcp: every server of the session with its state, and what can be done about it.
|
|
@@ -9176,8 +9226,15 @@ function runSlashCommand(tab, raw) {
|
|
|
9176
9226
|
return true;
|
|
9177
9227
|
}
|
|
9178
9228
|
case 'cost': { clearInput(); emit('cost:toggle'); return true; }
|
|
9179
|
-
case 'login': { clearInput();
|
|
9180
|
-
case 'logout': {
|
|
9229
|
+
case 'login': { clearInput(); _showAccounts(tab, '', { manage: serverAccountId(tab.accountId) }); return true; }
|
|
9230
|
+
case 'logout': {
|
|
9231
|
+
clearInput();
|
|
9232
|
+
if (tab.running || tab.automationActive) { appendStatus(tab, 'Finish or stop the current turn before signing out.'); return true; }
|
|
9233
|
+
void _claudeAuthRequest(`/api/claude/accounts/${encodeURIComponent(tab.accountId || 'default')}/auth/logout`, { method: 'POST', body: '{}' })
|
|
9234
|
+
.then(() => { tab.accountInfo = null; tab.terminalAuthView?.update({ loggedIn: false }); })
|
|
9235
|
+
.catch(() => appendStatus(tab, 'Could not sign out. Try again.'));
|
|
9236
|
+
return true;
|
|
9237
|
+
}
|
|
9181
9238
|
case 'plan': {
|
|
9182
9239
|
clearInput();
|
|
9183
9240
|
// The one path for plan mode: on is the user's pick of Plan, off goes back
|
|
@@ -9199,8 +9256,9 @@ function runSlashCommand(tab, raw) {
|
|
|
9199
9256
|
function send({ shift = false } = {}) {
|
|
9200
9257
|
const tab = activeTab();
|
|
9201
9258
|
if (!tab) return;
|
|
9202
|
-
if ((
|
|
9203
|
-
|
|
9259
|
+
if (_cliBlocked() && !tab.automationActive) {
|
|
9260
|
+
// The banner above the transcript says why, and offers the way out.
|
|
9261
|
+
_cliBanner?.nudge();
|
|
9204
9262
|
return;
|
|
9205
9263
|
}
|
|
9206
9264
|
const $input = _panel?.querySelector('#cp-input');
|
|
@@ -10094,116 +10152,50 @@ function syncReservedWidth(knownWidth) {
|
|
|
10094
10152
|
}
|
|
10095
10153
|
|
|
10096
10154
|
// ── CLI installation status (banner + send-block) ──
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
if (!
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
`;
|
|
10120
|
-
container.parentNode.insertBefore(banner, container);
|
|
10121
|
-
const btn = banner.querySelector('.cp-cli-banner-recheck');
|
|
10122
|
-
btn?.addEventListener('click', async () => {
|
|
10123
|
-
btn.disabled = true;
|
|
10124
|
-
btn.textContent = 'Checking…';
|
|
10125
|
-
try { await recheckCliStatus('claude-code'); }
|
|
10126
|
-
finally {
|
|
10127
|
-
if (btn.isConnected) {
|
|
10128
|
-
btn.disabled = false;
|
|
10129
|
-
btn.textContent = 'Re-check';
|
|
10130
|
-
}
|
|
10131
|
-
}
|
|
10132
|
-
});
|
|
10133
|
-
_renderCliBannerContent(banner);
|
|
10134
|
-
return banner;
|
|
10135
|
-
}
|
|
10136
|
-
|
|
10137
|
-
// Distinguish "never installed" from "installed but would not launch" —
|
|
10138
|
-
// telling the user to install something already present sends them chasing a
|
|
10139
|
-
// reinstall they do not need.
|
|
10140
|
-
function _renderCliBannerContent(banner) {
|
|
10141
|
-
const titleEl = banner.querySelector('.cp-cli-banner-title');
|
|
10142
|
-
const bodyEl = banner.querySelector('.cp-cli-banner-body');
|
|
10143
|
-
if (!titleEl || !bodyEl) return;
|
|
10144
|
-
const label = getCliLabel('claude-code');
|
|
10145
|
-
bodyEl.replaceChildren();
|
|
10146
|
-
|
|
10147
|
-
if (_cliInstalled === null) {
|
|
10148
|
-
titleEl.textContent = `${label} CLI not installed`;
|
|
10149
|
-
bodyEl.append('Run ');
|
|
10150
|
-
const code = document.createElement('code');
|
|
10151
|
-
code.textContent = getCliInstallCommand('claude-code');
|
|
10152
|
-
bodyEl.append(code, ' or follow the install guide.');
|
|
10153
|
-
return;
|
|
10154
|
-
}
|
|
10155
|
-
|
|
10156
|
-
titleEl.textContent = `${label} CLI failed to start`;
|
|
10157
|
-
bodyEl.append(`${label} v${_cliInstalled} is installed, but it could not be launched. `);
|
|
10158
|
-
if (_cliFailureReason) {
|
|
10159
|
-
const code = document.createElement('code');
|
|
10160
|
-
code.textContent = _cliFailureReason;
|
|
10161
|
-
bodyEl.append(code);
|
|
10162
|
-
} else {
|
|
10163
|
-
bodyEl.append('Check the CLI path in Settings, then re-check.');
|
|
10155
|
+
// One controller (ui-cli-banner.js) draws the banner above the transcript while
|
|
10156
|
+
// Claude Code is not usable: not installed (with Install, which runs Anthropic's
|
|
10157
|
+
// own installer on the server), being installed, failed to install, or found but
|
|
10158
|
+
// failed to start (with the error it gave). It also holds the class that keeps
|
|
10159
|
+
// sending off. What it says is cli-install-view.js (`cliInstall.*` strings).
|
|
10160
|
+
function _cliBannerController() {
|
|
10161
|
+
if (!_cliBanner) {
|
|
10162
|
+
_cliBanner = mountCliBanner({
|
|
10163
|
+
toolKey: 'claude-code',
|
|
10164
|
+
prefix: 'cp',
|
|
10165
|
+
panel: () => _panel,
|
|
10166
|
+
anchor: () => _panel?.querySelector('#cp-messages-container'),
|
|
10167
|
+
onBlockedChange: (blocked) => {
|
|
10168
|
+
const $send = _panel?.querySelector('#cp-send');
|
|
10169
|
+
if (!$send) return;
|
|
10170
|
+
if (blocked) { $send.disabled = true; return; }
|
|
10171
|
+
const tab = activeTab();
|
|
10172
|
+
const $input = _panel?.querySelector('#cp-input');
|
|
10173
|
+
if (tab?.running) $send.disabled = false;
|
|
10174
|
+
else $send.disabled = !$input?.value.trim() && !tab?.attachedImages?.length && !tab?.attachedFiles?.length;
|
|
10175
|
+
},
|
|
10176
|
+
});
|
|
10164
10177
|
}
|
|
10178
|
+
return _cliBanner;
|
|
10165
10179
|
}
|
|
10166
10180
|
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
banner?.remove();
|
|
10181
|
+
/** Sending is held back: Claude Code is missing, broken, failed to install, or being installed. */
|
|
10182
|
+
function _cliBlocked() {
|
|
10183
|
+
return !!_cliBanner?.blocked();
|
|
10171
10184
|
}
|
|
10172
10185
|
|
|
10173
10186
|
function _refreshCliBanners() {
|
|
10174
10187
|
if (!_panel) return;
|
|
10175
|
-
|
|
10176
|
-
_panel.classList.toggle('cp-cli-blocked', missing);
|
|
10177
|
-
if (missing) _ensureCliBannerEl();
|
|
10178
|
-
else _removeCliBannerEl();
|
|
10179
|
-
const $send = _panel.querySelector('#cp-send');
|
|
10180
|
-
if ($send && missing) $send.disabled = true;
|
|
10188
|
+
_cliBannerController().refresh();
|
|
10181
10189
|
}
|
|
10182
10190
|
|
|
10191
|
+
// A session could not start Claude Code: "failed to start" with its words at
|
|
10192
|
+
// once, then the server is asked again and its answer decides what stays.
|
|
10183
10193
|
export function flagClaudeCliInstallFailure(reason = null) {
|
|
10184
|
-
|
|
10185
|
-
_cliFailureReason = reason ? String(reason).slice(0, 300) : null;
|
|
10186
|
-
_refreshCliBanners();
|
|
10187
|
-
// Clear from the RETURNED value, not the subscriber callback: the callback
|
|
10188
|
-
// only fires when the version string changes, and a launch failure does not
|
|
10189
|
-
// change it, so the banner would otherwise latch permanently.
|
|
10190
|
-
recheckCliStatus('claude-code')
|
|
10191
|
-
.then((info) => {
|
|
10192
|
-
if (!info?.installed) return;
|
|
10193
|
-
_cliInstallFailureForced = false;
|
|
10194
|
-
_cliFailureReason = null;
|
|
10195
|
-
_refreshCliBanners();
|
|
10196
|
-
})
|
|
10197
|
-
.catch(() => {});
|
|
10194
|
+
_cliBannerController().flagFailure(reason);
|
|
10198
10195
|
}
|
|
10199
10196
|
|
|
10200
10197
|
function _ensureCliSubscription() {
|
|
10201
|
-
|
|
10202
|
-
_cliUnsub = subscribeCliStatus('claude-code', (info) => {
|
|
10203
|
-
_cliInstalled = info?.installed || null;
|
|
10204
|
-
if (_cliInstalled) { _cliInstallFailureForced = false; _cliFailureReason = null; }
|
|
10205
|
-
_refreshCliBanners();
|
|
10206
|
-
});
|
|
10198
|
+
_cliBannerController();
|
|
10207
10199
|
}
|
|
10208
10200
|
|
|
10209
10201
|
// ── Delegated transcript actions ──
|
|
@@ -11192,6 +11184,13 @@ function wireEvents() {
|
|
|
11192
11184
|
const $close = _panel.querySelector('.cp-close-btn');
|
|
11193
11185
|
const $minimize = _panel.querySelector('.cp-minimize-btn');
|
|
11194
11186
|
const $slide = _panel.querySelector('.cp-slide-btn');
|
|
11187
|
+
_mountAccountMenu();
|
|
11188
|
+
// Any window or surface changed an account (signed in, a key saved, its mode, renamed, added,
|
|
11189
|
+
// removed): the account menu re-reads the list, and the tabs on it re-check sign-in.
|
|
11190
|
+
on('sync:claude:accounts-changed', (msg) => {
|
|
11191
|
+
_loadClaudeAccounts({ force: true }).catch(() => {});
|
|
11192
|
+
for (const t of _tabs) if (!msg?.accountId || serverAccountId(t.accountId) === msg.accountId) void t.terminalAuthView?.refresh();
|
|
11193
|
+
});
|
|
11195
11194
|
const $new = _panel.querySelector('.cp-new-btn');
|
|
11196
11195
|
|
|
11197
11196
|
const $brand = _panel.querySelector('.cp-brand');
|