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
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
getCachedCodexAccounts,
|
|
20
20
|
getCachedMcpProfilePresets,
|
|
21
21
|
getCachedOpencodeAgents,
|
|
22
|
+
getClaudeWorkerAccountId,
|
|
23
|
+
getCodexWorkerAccountId,
|
|
22
24
|
getEffortLevelsForModel,
|
|
23
25
|
getModelsForProfile,
|
|
24
26
|
modelEffortIds,
|
|
@@ -27,8 +29,11 @@ import {
|
|
|
27
29
|
import { getHiddenModels, HIDDEN_MODELS_EVENT, hiddenModelMatch } from '../ocp-hidden-models.js';
|
|
28
30
|
import { getProviderMeta } from '../provider-icons.js';
|
|
29
31
|
import { on } from '../state.js';
|
|
30
|
-
import { fetchAssistantCatalog, fetchProjects } from './asst-api.js';
|
|
32
|
+
import { fetchAssistantCatalog, fetchProjects, patchClaudeAccount, patchCodexAccount, setClaudeWorkerAccount, setCodexWorkerAccount } from './asst-api.js';
|
|
33
|
+
import { accountBadge, accountCompactMode, accountIdentity, accountName, accountUsable, modeChoices } from '../account-view.js';
|
|
34
|
+
import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
31
35
|
import { closeMenu, currentMenu, isMenuOpen, openMenu } from './asst-menu.js';
|
|
36
|
+
import { createMascotControl } from './asst-mascot.js';
|
|
32
37
|
import {
|
|
33
38
|
ACCOUNT_PROVIDERS,
|
|
34
39
|
brainRequiresSwitch,
|
|
@@ -438,6 +443,9 @@ export async function openModelMenu(anchor, opts = {}) {
|
|
|
438
443
|
if (!anchor.isConnected || (loading && currentMenu() !== loading.el)) return null;
|
|
439
444
|
const metadata = modelCatalogStatus();
|
|
440
445
|
const items = [];
|
|
446
|
+
if (Array.isArray(opts.accountItems) && opts.accountItems.length) {
|
|
447
|
+
items.push({ kind: 'header', label: tr('assistant.brain.account', 'Account profile') }, ...opts.accountItems, { kind: 'separator' });
|
|
448
|
+
}
|
|
441
449
|
if (includeHere) {
|
|
442
450
|
items.push({ kind: 'radio', id: '__here', label: hereLabel || tr('assistant.routes.brainDecides', 'Let the brain decide'), selected: !current, onSelect: () => onSelect?.({ here: true }) });
|
|
443
451
|
}
|
|
@@ -498,7 +506,7 @@ export async function openModelMenu(anchor, opts = {}) {
|
|
|
498
506
|
role: 'menu',
|
|
499
507
|
className: `asst-model-menu${opts.routingEditor ? ' asst-route-model-menu' : ''}`,
|
|
500
508
|
zIndex: opts.zIndex ?? null,
|
|
501
|
-
repositionOnResize:
|
|
509
|
+
repositionOnResize: true,
|
|
502
510
|
filterEmptyLabel: tr('assistant.routes.menu.noMatches', 'No matching models. Check Models for hidden choices.'),
|
|
503
511
|
});
|
|
504
512
|
if (opts.routingEditor && menu) {
|
|
@@ -582,6 +590,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
582
590
|
let loading = null;
|
|
583
591
|
// The empty state's "Choose a project" (mountEmptyProject): the project field's second button.
|
|
584
592
|
let heroProject = null;
|
|
593
|
+
let heroProjectMascot = null;
|
|
585
594
|
|
|
586
595
|
function currentBrain() {
|
|
587
596
|
return normalizeBrain(hooks.getBrain?.());
|
|
@@ -621,9 +630,16 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
621
630
|
return accounts.find(a => a.id === brain.accountId) || null;
|
|
622
631
|
}
|
|
623
632
|
|
|
633
|
+
const accountProvider = (brain) => (brain.provider === 'codex' ? 'codex' : 'claude');
|
|
624
634
|
function accountLabel(brain) {
|
|
625
635
|
const acc = accountOf(brain);
|
|
626
|
-
|
|
636
|
+
const name = acc ? accountName(acc, accountProvider(brain)) : (brain.accountId || t('assistant.brain.accountDefault', 'Default'));
|
|
637
|
+
// An account says what it runs on (its subscription or its API key), or that it is signed out.
|
|
638
|
+
if (acc && acc.mode !== undefined) {
|
|
639
|
+
const badge = accountBadge(acc, accountProvider(brain));
|
|
640
|
+
return badge.text ? `${name} · ${badge.text}` : name;
|
|
641
|
+
}
|
|
642
|
+
return name;
|
|
627
643
|
}
|
|
628
644
|
|
|
629
645
|
function modelLabel(brain = currentBrain()) {
|
|
@@ -679,6 +695,13 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
679
695
|
const v = values(brain);
|
|
680
696
|
const modelTip = [v.meta.label, brain.model || t('assistant.brain.modelDefault', 'Default model'), v.account ? t('assistant.brain.asAccount', 'as {account}', { account: v.account }) : ''].filter(Boolean).join(' · ');
|
|
681
697
|
setBtn('model', { label: v.model, icon: v.meta.icon, color: v.meta.color, tooltip: modelTip, aria: `${t('assistant.brain.model', 'Model')}: ${modelTip}` });
|
|
698
|
+
if (ACCOUNT_PROVIDERS.has(brain.provider)) {
|
|
699
|
+
const acc = accountOf(brain);
|
|
700
|
+
const label = buttons.model.querySelector('.asst-dd-label');
|
|
701
|
+
label.classList.add('asst-model-account-label');
|
|
702
|
+
const mode = acc ? accountCompactMode(acc, accountProvider(brain)) : t('accounts.checking', 'Checking…');
|
|
703
|
+
label.replaceChildren(el('span', 'asst-model-name', v.model), el('span', '', '·'), el('span', 'asst-model-account', acc ? accountName(acc, accountProvider(brain)) : brain.accountId || t('assistant.brain.accountDefault', 'Default')), el('span', '', '·'), el('span', 'asst-model-mode', mode));
|
|
704
|
+
} else buttons.model.querySelector('.asst-dd-label').classList.remove('asst-model-account-label');
|
|
682
705
|
// A display name ("Sonnet 5") reads in sans; a raw id nothing names ("opus[1m]") stays mono.
|
|
683
706
|
buttons.model?.toggleAttribute('data-raw', /^[a-z0-9][\w.\-[\]:/]*$/.test(String(v.model || '')));
|
|
684
707
|
|
|
@@ -688,9 +711,12 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
688
711
|
setBtn('effort', { label: v.effort, icon: ICON_EFFORT, hidden: !v.effortSupported, tooltip: `${t('assistant.brain.effort', 'Thinking effort')}: ${v.effort}` });
|
|
689
712
|
}
|
|
690
713
|
setBtn('variant', { label: v.variant, icon: ICON_EFFORT, hidden: !v.variantSupported, tooltip: `${t('assistant.brain.variant', 'Model variant')}: ${v.variant}` });
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
|
|
714
|
+
// A Claude or Codex brain always shows it: which account and which credential (its subscription or
|
|
715
|
+
// its API key) it runs on, or that the account is signed out.
|
|
716
|
+
const brainAcc = ACCOUNT_PROVIDERS.has(brain.provider) ? accountOf(brain) : null;
|
|
717
|
+
const accountTip = [v.account, brainAcc ? accountIdentity(brainAcc) : ''].filter(Boolean).join(' · ');
|
|
718
|
+
setBtn('account', { label: v.account, icon: ICON_ACCOUNT, hidden: true, tooltip: `${t('assistant.brain.account', 'Account profile')}: ${accountTip}` });
|
|
719
|
+
buttons.account?.classList.toggle('asst-dd-warn', !!brainAcc && brainAcc.signedIn === false);
|
|
694
720
|
setBtn('project', { label: v.project, icon: ICON_FOLDER, tooltip: brain.project || t('assistant.brain.projectNoneTip', 'Working project: home directory'), aria: `${t('assistant.brain.project', 'Working project')}: ${brain.project || v.project}` });
|
|
695
721
|
setBtn('mode', { label: v.mode, icon: ICON_SHIELD, tooltip: `${t('assistant.brain.mode', 'Permission mode')}: ${v.mode}` });
|
|
696
722
|
const tests = testDensity();
|
|
@@ -721,10 +747,13 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
721
747
|
* sends focus back to it. A new slot replaces the last one; null removes it.
|
|
722
748
|
*/
|
|
723
749
|
function mountEmptyProject(slot) {
|
|
750
|
+
if (heroProject && isMenuOpen(heroProject)) closeMenu();
|
|
751
|
+
heroProjectMascot?.destroy();
|
|
752
|
+
heroProjectMascot = null;
|
|
724
753
|
heroProject?.remove();
|
|
725
754
|
heroProject = null;
|
|
726
755
|
if (destroyed || !slot || typeof slot.appendChild !== 'function') return null;
|
|
727
|
-
const btn = el('button', 'asst-dd asst-empty-project-btn');
|
|
756
|
+
const btn = el('button', 'asst-dd asst-empty-control-btn asst-empty-project-btn');
|
|
728
757
|
btn.type = 'button';
|
|
729
758
|
btn.dataset.field = 'project';
|
|
730
759
|
btn.setAttribute('aria-haspopup', 'menu');
|
|
@@ -743,6 +772,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
743
772
|
});
|
|
744
773
|
slot.appendChild(btn);
|
|
745
774
|
heroProject = btn;
|
|
775
|
+
heroProjectMascot = createMascotControl(btn, { director: hooks.mascot, pose: 'project' });
|
|
746
776
|
renderEmptyProject();
|
|
747
777
|
return btn;
|
|
748
778
|
}
|
|
@@ -818,12 +848,14 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
818
848
|
const anchor = anchorFor(field, anchorOverride);
|
|
819
849
|
const brain = currentBrain();
|
|
820
850
|
if (field === 'model') {
|
|
851
|
+
if (ACCOUNT_PROVIDERS.has(brain.provider)) await ensureLoaded(brain.provider);
|
|
821
852
|
await openModelMenu(anchor, {
|
|
822
853
|
brain,
|
|
823
854
|
t: hooks.t,
|
|
824
855
|
placement,
|
|
825
856
|
current: { provider: brain.provider, model: brain.model },
|
|
826
857
|
warnSwitch: true,
|
|
858
|
+
accountItems: ACCOUNT_PROVIDERS.has(brain.provider) ? accountItems(brain, { composer: true }) : [],
|
|
827
859
|
onToast: hooks.onToast,
|
|
828
860
|
onManageModels: hooks.onManageModels,
|
|
829
861
|
onSelect: (target) => {
|
|
@@ -888,29 +920,23 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
888
920
|
|
|
889
921
|
if (field === 'account') {
|
|
890
922
|
if (!ACCOUNT_PROVIDERS.has(brain.provider)) return;
|
|
891
|
-
|
|
892
|
-
const items = accounts.map((a) => ({
|
|
893
|
-
kind: 'radio',
|
|
894
|
-
id: a.id,
|
|
895
|
-
label: a.label || a.email || a.id,
|
|
896
|
-
desc: a.email && a.email !== a.label ? a.email : (a.isDefault ? t('assistant.accounts.ambient', 'ambient config') : ''),
|
|
897
|
-
selected: a.id === brain.accountId,
|
|
898
|
-
onSelect: () => commit({ accountId: a.id }, 'account'),
|
|
899
|
-
}));
|
|
900
|
-
items.push({ kind: 'separator' });
|
|
901
|
-
items.push({ id: '__add', label: t('assistant.accounts.add', 'Add account…'), onSelect: () => hooks.onAddAccount?.(brain.provider) });
|
|
902
|
-
items.push({ id: '__manage', label: t('assistant.accounts.manage', 'Manage…'), onSelect: () => hooks.onManageAccounts?.(brain.provider) });
|
|
903
|
-
menu(t('assistant.brain.account', 'Account profile'), items);
|
|
923
|
+
menu(t('assistant.brain.account', 'Account profile'), accountItems(brain), { width: 300 });
|
|
904
924
|
return;
|
|
905
925
|
}
|
|
906
926
|
|
|
907
927
|
if (field === 'project') {
|
|
928
|
+
const actor = anchor === heroProject ? heroProjectMascot : null;
|
|
929
|
+
const pick = project => {
|
|
930
|
+
const changed = (currentBrain().project || null) !== (project || null);
|
|
931
|
+
commit({ project }, 'project');
|
|
932
|
+
actor?.commit(changed ? 'hop' : 'nod');
|
|
933
|
+
};
|
|
908
934
|
const projects = _projectsCache || [];
|
|
909
|
-
const items = [{ kind: 'radio', id: '', label: t('assistant.brain.projectNone', 'Home directory'), desc: t('assistant.brain.projectNoneDesc', 'no project'), selected: !brain.project, restoreFocus, onSelect: () =>
|
|
910
|
-
for (const p of projects) items.push({ kind: 'radio', id: p.path, label: p.label || basename(p.path), desc: p.path, selected: p.path === brain.project, restoreFocus, onSelect: () =>
|
|
935
|
+
const items = [{ kind: 'radio', id: '', label: t('assistant.brain.projectNone', 'Home directory'), desc: t('assistant.brain.projectNoneDesc', 'no project'), selected: !brain.project, restoreFocus, onSelect: () => pick(null) }];
|
|
936
|
+
for (const p of projects) items.push({ kind: 'radio', id: p.path, label: p.label || basename(p.path), desc: p.path, selected: p.path === brain.project, restoreFocus, onSelect: () => pick(p.path) });
|
|
911
937
|
items.push({ kind: 'separator' });
|
|
912
|
-
items.push({ kind: 'input', placeholder: t('assistant.brain.projectCustom', 'Type a directory path…'), value: brain.project && !projects.some(p => p.path === brain.project) ? brain.project : '', restoreFocus, onSubmit:
|
|
913
|
-
menu(t('assistant.brain.project', 'Working project'), items, { width: 280, filter: projects.length > 8, filterPlaceholder: t('assistant.brain.filterProjects', 'Filter projects…') });
|
|
938
|
+
items.push({ kind: 'input', placeholder: t('assistant.brain.projectCustom', 'Type a directory path…'), value: brain.project && !projects.some(p => p.path === brain.project) ? brain.project : '', restoreFocus, onSubmit: pick });
|
|
939
|
+
menu(t('assistant.brain.project', 'Working project'), items, { width: 280, filter: projects.length > 8, filterPlaceholder: t('assistant.brain.filterProjects', 'Filter projects…'), onOpen: node => actor?.open(node), onClose: () => actor?.close() });
|
|
914
940
|
return;
|
|
915
941
|
}
|
|
916
942
|
|
|
@@ -953,6 +979,69 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
953
979
|
}
|
|
954
980
|
}
|
|
955
981
|
|
|
982
|
+
// The brain's account menu (Claude or Codex): every account with what it runs on and who it is (a
|
|
983
|
+
// signed-out one is listed, not offered), the mode of the brain's account when it holds both credentials,
|
|
984
|
+
// and the account dispatched workers of that provider use when a dispatch names none.
|
|
985
|
+
function accountItems(brain, { composer = false } = {}) {
|
|
986
|
+
const codex = brain.provider === 'codex';
|
|
987
|
+
const p = codex ? 'codex' : 'claude';
|
|
988
|
+
const accounts = codex ? getCachedCodexAccounts() : getCachedClaudeAccounts();
|
|
989
|
+
const refetch = () => (codex ? fetchCodexAccounts(true) : fetchClaudeAccounts(true));
|
|
990
|
+
const items = accounts.map((a) => {
|
|
991
|
+
const badge = accountBadge(a, p);
|
|
992
|
+
const usable = accountUsable(a);
|
|
993
|
+
return {
|
|
994
|
+
kind: 'radio', id: a.id, label: accountName(a, p),
|
|
995
|
+
desc: [accountIdentity(a), accountUsageText(a, hooks.t), !usable ? t('assistant.accounts.signInFirst', 'Signed out: sign it in from Manage…') : composer && a.id !== brain.accountId ? t('assistant.brain.newConversation', 'starts a new conversation') : ''].filter(Boolean).join(' · '),
|
|
996
|
+
tag: badge.text, disabled: !usable,
|
|
997
|
+
selected: a.id === brain.accountId,
|
|
998
|
+
onSelect: () => commit({ accountId: a.id }, 'account'),
|
|
999
|
+
};
|
|
1000
|
+
});
|
|
1001
|
+
items.unshift({ kind: 'radio', id: 'auto', label: autoAccountLabel(hooks.t), desc: t('assistant.accountUsage.composerHint', 'Pick the subscription with most usage left and start a new conversation.'), onSelect: () => commit({ accountId: 'auto' }, 'account') });
|
|
1002
|
+
if (composer) for (const a of accounts.filter(a => !accountUsable(a))) items.push({ id: `signin:${a.id}`, label: t('assistant.accounts.signInAccount', 'Sign in to {label}…', { label: accountName(a, p) }), onSelect: () => hooks.onManageAccounts?.(brain.provider, a.id) });
|
|
1003
|
+
const current = accounts.find((a) => a.id === brain.accountId);
|
|
1004
|
+
const modes = modeChoices(current, p);
|
|
1005
|
+
if (modes.length) {
|
|
1006
|
+
items.push({ kind: 'separator' });
|
|
1007
|
+
items.push({ kind: 'header', label: t('assistant.accounts.usesHeader', '{label} uses (every session on it)', { label: accountName(current, p) }) });
|
|
1008
|
+
for (const m of modes) {
|
|
1009
|
+
items.push({
|
|
1010
|
+
kind: 'radio', id: `mode:${m.mode}`, label: m.label, desc: [m.detail, !m.current ? t('assistant.brain.newConversation', 'starts a new conversation') : ''].filter(Boolean).join(' · '), selected: m.current, disabled: hooks.getRunning?.() === true,
|
|
1011
|
+
onSelect: async () => {
|
|
1012
|
+
if (m.current) return;
|
|
1013
|
+
try {
|
|
1014
|
+
await (codex ? patchCodexAccount : patchClaudeAccount)(current.id, { mode: m.mode }); await refetch();
|
|
1015
|
+
hooks.onChange?.(currentBrain(), { requiresSwitch: true, restartBrain: true, field: 'accountMode', previous: brain }); render();
|
|
1016
|
+
}
|
|
1017
|
+
catch (err) { hooks.onToast?.(err?.message || String(err)); }
|
|
1018
|
+
},
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const worker = codex ? getCodexWorkerAccountId() : getClaudeWorkerAccountId();
|
|
1023
|
+
if (!composer) {
|
|
1024
|
+
items.push({ kind: 'separator' });
|
|
1025
|
+
items.push({ kind: 'header', label: codex ? t('assistant.accounts.codexWorkersHeader', 'Codex workers use') : t('assistant.accounts.workersHeader', 'Claude Code workers use') });
|
|
1026
|
+
for (const a of accounts) {
|
|
1027
|
+
const usable = accountUsable(a);
|
|
1028
|
+
items.push({
|
|
1029
|
+
kind: 'radio', id: `worker:${a.id}`, label: accountName(a, p), tag: accountBadge(a, p).text,
|
|
1030
|
+
disabled: !usable && a.id !== worker, selected: a.id === worker,
|
|
1031
|
+
onSelect: async () => {
|
|
1032
|
+
if (a.id === worker) return;
|
|
1033
|
+
try { await (codex ? setCodexWorkerAccount : setClaudeWorkerAccount)(a.id); await refetch(); render(); }
|
|
1034
|
+
catch (err) { hooks.onToast?.(err?.message || String(err)); }
|
|
1035
|
+
},
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
items.push({ kind: 'separator' });
|
|
1040
|
+
items.push({ id: '__add', label: t('assistant.accounts.add', 'Add account…'), onSelect: () => hooks.onAddAccount?.(brain.provider) });
|
|
1041
|
+
items.push({ id: '__manage', label: t('assistant.accounts.manage', 'Manage…'), onSelect: () => hooks.onManageAccounts?.(brain.provider) });
|
|
1042
|
+
return items;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
956
1045
|
async function switchProvider(provider, model = '') {
|
|
957
1046
|
const before = currentBrain();
|
|
958
1047
|
if (provider === before.provider) return;
|
|
@@ -976,6 +1065,16 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
976
1065
|
|
|
977
1066
|
render();
|
|
978
1067
|
refresh().catch(() => {});
|
|
1068
|
+
// Any window changed a Claude or Codex account (signed in or out, a key, its mode, the worker choice,
|
|
1069
|
+
// added, removed): its list is read again and the picker re-drawn, so it never shows a stale state.
|
|
1070
|
+
const accountsChanged = (provider) => (msg) => {
|
|
1071
|
+
const which = provider || (msg?.provider === 'codex' ? 'codex' : msg?.provider === 'claude-code' ? 'claude-code' : null);
|
|
1072
|
+
const jobs = [];
|
|
1073
|
+
if (!which || which === 'claude-code') jobs.push(fetchClaudeAccounts(true));
|
|
1074
|
+
if (!which || which === 'codex') jobs.push(fetchCodexAccounts(true));
|
|
1075
|
+
Promise.all(jobs).then(() => { if (!destroyed) render(); }).catch(() => {});
|
|
1076
|
+
};
|
|
1077
|
+
const offAccounts = [on('sync:claude:accounts-changed', accountsChanged('claude-code')), on('sync:codex:accounts-changed', accountsChanged('codex')), on('sync:assistant:accounts-changed', accountsChanged(null))];
|
|
979
1078
|
|
|
980
1079
|
return {
|
|
981
1080
|
el: root,
|
|
@@ -989,6 +1088,6 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
989
1088
|
mountEmptyProject,
|
|
990
1089
|
setBrain() { render(); },
|
|
991
1090
|
close: closeMenu,
|
|
992
|
-
destroy() { destroyed = true; closeMenu(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
|
|
1091
|
+
destroy() { destroyed = true; for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
|
|
993
1092
|
};
|
|
994
1093
|
}
|