synabun 2.0.0 → 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 +97 -0
- package/NOTICE +5 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +59 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +153 -18
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +26 -3
- package/hooks/claude-code/stop.mjs +5 -5
- 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/lib/start-launcher.js +88 -1
- 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 -3
- 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 +2126 -209
- package/neural-interface/i18n/pt-BR.json +2123 -207
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +316 -57
- package/neural-interface/lib/assistant-brains/claude.js +19 -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-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +91 -14
- package/neural-interface/lib/assistant-dispatch.js +494 -138
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +73 -12
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-pricing.js +21 -6
- 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 +488 -59
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +183 -0
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +123 -9
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +33 -5
- 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/claude-temporary.js +46 -7
- 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/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- 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/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -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-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +234 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +26 -8
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- 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/request-gate.js +589 -0
- 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/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -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 +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +19 -3
- package/neural-interface/public/onboarding.html +107 -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 +50 -14
- 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-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
- package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
- package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
- package/neural-interface/public/shared/assistant/asst-render.js +197 -23
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +95 -343
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +186 -2
- package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
- package/neural-interface/public/shared/blob-store.js +111 -22
- 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 +80 -118
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
- 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/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +61 -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-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +161 -165
- 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-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +32 -2
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -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-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +33 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +1710 -770
- package/neural-interface/public/shared/synabun-mascot.js +68 -58
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +369 -229
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +187 -19
- package/neural-interface/public/shared/ui-menubar.js +25 -33
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +48 -22
- 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 +509 -490
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +22 -0
- package/neural-interface/public/shared/ui-terminal.js +40 -5
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- 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/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +253 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +2085 -1629
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +5 -8
- package/setup.js +53 -15
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { mountCodexSettings } from './cdx-settings.js';
|
|
2
|
+
import { t } from '../i18n.js';
|
|
3
|
+
import { getCliState, isCliInstalling } from '../cli-status.js';
|
|
4
|
+
import { cliInstallText } from '../cli-install-view.js';
|
|
1
5
|
// ═══════════════════════════════════════════
|
|
2
6
|
// SynaBun — Codex Panel: State, Tabs & Sessions
|
|
3
7
|
// ═══════════════════════════════════════════
|
|
4
8
|
|
|
5
9
|
import { storage } from '../storage.js';
|
|
10
|
+
import { providerAuthSnapshot } from '../ui-provider-auth.js';
|
|
11
|
+
import { mountProviderTerminalAuth, providerApiKeyRequest, startProviderTerminalLogin } from '../ui-provider-terminal-auth.js';
|
|
12
|
+
import { accountName, accountSummary, accountSwitchPlan, accountText, serverAccountId } from '../account-view.js';
|
|
13
|
+
import { mountAccountMenu } from '../account-menu.js';
|
|
14
|
+
import '../account-i18n.js';
|
|
6
15
|
import { restoreMcpAppCards, teardownMcpApps, retireMcpAppNode, stripMcpAppRuntime } from './cdx-mcp-app-host.js';
|
|
7
16
|
import { renderTurnDiff, completeTurnOutputs } from './cdx-render.js';
|
|
8
17
|
import { hydrateOutputControls } from './cdx-output.js';
|
|
@@ -13,6 +22,7 @@ import {
|
|
|
13
22
|
} from './cdx-plan.js';
|
|
14
23
|
import { fetchBlobNamespace, putBlob, deleteBlob, flushBlobs } from '../blob-store.js';
|
|
15
24
|
import { state as appState, on as appOn } from '../state.js';
|
|
25
|
+
import { forgetGreeting, mountGreeting } from '../greeting/greeting-card.js';
|
|
16
26
|
import { fetchProjects, searchSessions } from '../api.js';
|
|
17
27
|
import {
|
|
18
28
|
getCodexModelName,
|
|
@@ -24,16 +34,18 @@ import { syncSidepanelLayout, focusSidepanel, isHostedSessionFocused } from '../
|
|
|
24
34
|
import { notify, NOTIF_TYPE } from '../ui-notifications.js';
|
|
25
35
|
import { createStickyScrollController } from '../ui-scroll-follow.js';
|
|
26
36
|
import { requestGeneratedSessionTitle } from '../session-title.js';
|
|
37
|
+
import { fileLinkText } from '../ui-file-links.js';
|
|
38
|
+
import { WORKER_END_CLASS, backToAssistant, buildWorkerEndCard, endWorkerRunOnClose, forgetWorkerEnd } from '../ui-worker-end.js';
|
|
27
39
|
import {
|
|
28
40
|
CODEX_COMMANDS, CODEX_COMMAND_BY_NAME, parseCodexCommand, codexCommandHints,
|
|
29
|
-
capabilityAvailability, latestCompletedCodexText, configValue,
|
|
30
|
-
|
|
41
|
+
capabilityAvailability, latestCompletedCodexText, configValue,
|
|
42
|
+
SESSION_CONFIG_FIELDS, flattenNativeCatalog, fastServiceTier,
|
|
31
43
|
} from './cdx-commands.js';
|
|
32
44
|
import {
|
|
33
45
|
knownCodexCapabilities, storeCodexCapabilities, dropCodexCapabilities, invalidateCodexCapabilities,
|
|
34
46
|
currentCodexCapabilities, requestWithCodexCapability,
|
|
35
47
|
} from './cdx-capabilities.js';
|
|
36
|
-
import { openCodexCatalog, openCodexReview, openCodexGoal, openCodexFeedback, openCodexDiff, refreshCodexDiff, refreshCodexAttachments,
|
|
48
|
+
import { openCodexCatalog, openCodexReview, openCodexGoal, openCodexFeedback, openCodexDiff, refreshCodexDiff, refreshCodexAttachments, updateCodexGateway } from './cdx-features.js';
|
|
37
49
|
export { refreshCodexDiff } from './cdx-features.js';
|
|
38
50
|
import {
|
|
39
51
|
STOR, EFFORT_LEVELS, MAX_THREAD_SNAPSHOTS, MAX_THREAD_SNAPSHOT_CHARS,
|
|
@@ -409,6 +421,7 @@ function buildWsCallbacks(tab, options = {}) {
|
|
|
409
421
|
}
|
|
410
422
|
restoreMcpAppCards(_messagesEl, { tab: _boundTab, items: _items, request: requestSocketPayloadForTab, draft: setMcpAppDraft });
|
|
411
423
|
if (_boundTab) _boundTab.bootstrapped = true;
|
|
424
|
+
ensureCodexTerminalAuth(tab);
|
|
412
425
|
if (msg.mcpProfile && _boundTab) {
|
|
413
426
|
_boundTab.mcpProfile = msg.mcpProfile;
|
|
414
427
|
if (activeTab() === _boundTab) _onActiveTabChanged?.(_boundTab);
|
|
@@ -467,6 +480,8 @@ function buildWsCallbacks(tab, options = {}) {
|
|
|
467
480
|
clearTranscript();
|
|
468
481
|
}
|
|
469
482
|
restoreServerRequests(heldRequests);
|
|
483
|
+
// The history rebuilt the transcript: a dispatched worker's end card goes back at its end.
|
|
484
|
+
placeWorkerEnd(tab);
|
|
470
485
|
if (options.historyOnly) {
|
|
471
486
|
tab._automationHydrated = true;
|
|
472
487
|
closeAutomationHistorySocket(tab);
|
|
@@ -637,7 +652,8 @@ function buildWsCallbacks(tab, options = {}) {
|
|
|
637
652
|
return;
|
|
638
653
|
}
|
|
639
654
|
if (msg.type === 'codex_config_repaired') {
|
|
640
|
-
|
|
655
|
+
clearCodexConfigWarnings();
|
|
656
|
+
appendSystem(t('codexSettings.configRepaired'), 'muted');
|
|
641
657
|
return;
|
|
642
658
|
}
|
|
643
659
|
resolveGenericRequest(msg);
|
|
@@ -1247,6 +1263,8 @@ function writeThreadSnapshot(tab, { force = false } = {}) {
|
|
|
1247
1263
|
const accountId = String(tab.accountId || 'default');
|
|
1248
1264
|
const snapshotNodes = [...messagesEl.children].filter((node) => (
|
|
1249
1265
|
!node.classList?.contains('cxp-empty') && !node.classList?.contains('cxp-thinking')
|
|
1266
|
+
&& !node.classList?.contains(WORKER_END_CLASS)
|
|
1267
|
+
&& !node.hasAttribute('data-provider-auth-card')
|
|
1250
1268
|
));
|
|
1251
1269
|
const ownsEveryNode = codexTranscriptNodesHaveOwnership(snapshotNodes, { threadId, accountId });
|
|
1252
1270
|
if (!ownsEveryNode) {
|
|
@@ -1257,7 +1275,9 @@ function writeThreadSnapshot(tab, { force = false } = {}) {
|
|
|
1257
1275
|
}
|
|
1258
1276
|
const snapshotEl = messagesEl.cloneNode(true);
|
|
1259
1277
|
stripMcpAppRuntime(snapshotEl);
|
|
1260
|
-
|
|
1278
|
+
// A worker's end card is drawn again from its run, never restored from markup.
|
|
1279
|
+
snapshotEl.querySelectorAll(`.${WORKER_END_CLASS}`).forEach((node) => node.remove());
|
|
1280
|
+
const html = providerAuthSnapshot(snapshotEl);
|
|
1261
1281
|
if (!html.trim()) return;
|
|
1262
1282
|
const ownedItemNodes = snapshotNodes.filter((node) => node.dataset?.itemId);
|
|
1263
1283
|
const itemCount = normalizeSnapshotItemCount(tab?.transcriptItemCount)
|
|
@@ -1506,6 +1526,11 @@ function storedAutoAccept() {
|
|
|
1506
1526
|
function releaseAutomationForManualUse(tab = _boundTab) {
|
|
1507
1527
|
const runId = detachTerminalCodexAutomation(tab, storedAutoAccept());
|
|
1508
1528
|
if (!runId) return false;
|
|
1529
|
+
// While the tab still shows the run's thread, the dock's Focus on the run raises it (cdx-panel.js).
|
|
1530
|
+
tab.releasedRunId = runId;
|
|
1531
|
+
forgetWorkerEnd(runId);
|
|
1532
|
+
tab._workerEnd = null;
|
|
1533
|
+
placeWorkerEnd(tab);
|
|
1509
1534
|
window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId } }));
|
|
1510
1535
|
if (isActiveTab(tab)) syncToolbarState();
|
|
1511
1536
|
saveTabs();
|
|
@@ -2031,6 +2056,8 @@ export function syncLegacyState() {
|
|
|
2031
2056
|
else storage.removeItem(STOR.project);
|
|
2032
2057
|
if (tab.threadId) storage.setItem(STOR.threadId, tab.threadId);
|
|
2033
2058
|
else storage.removeItem(STOR.threadId);
|
|
2059
|
+
if (tab.threadId) storage.setItem(STOR.threadAccount, tab.accountId || 'default');
|
|
2060
|
+
else storage.removeItem(STOR.threadAccount);
|
|
2034
2061
|
if (tab.threadId && tab.sessionLabel) storage.setItem(STOR.title, tab.sessionLabel);
|
|
2035
2062
|
else storage.removeItem(STOR.title);
|
|
2036
2063
|
}
|
|
@@ -2572,6 +2599,8 @@ function disposeDiscardedTab(tab) {
|
|
|
2572
2599
|
if (!tab) return;
|
|
2573
2600
|
flushThreadSnapshotSave(tab);
|
|
2574
2601
|
disconnectTab(tab);
|
|
2602
|
+
tab.authView?.dispose();
|
|
2603
|
+
tab.terminalAuthView?.dispose();
|
|
2575
2604
|
tab.closed = true;
|
|
2576
2605
|
if (tab.snapshotTimer) {
|
|
2577
2606
|
clearTimeout(tab.snapshotTimer);
|
|
@@ -2843,6 +2872,7 @@ export function createTab(saved = {}, { autoSwitch = true } = {}) {
|
|
|
2843
2872
|
automationRunId,
|
|
2844
2873
|
automationActive: !!saved.automationActive,
|
|
2845
2874
|
automationOwnerId: saved.automationOwnerId || null,
|
|
2875
|
+
releasedRunId: automationRunId ? null : (saved.releasedRunId || null),
|
|
2846
2876
|
};
|
|
2847
2877
|
tab.messagesEl = createMessagesEl(tab);
|
|
2848
2878
|
tab.pillEl = createTrayPill(tab);
|
|
@@ -2870,15 +2900,18 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
|
|
|
2870
2900
|
const tab = _tabs[idx];
|
|
2871
2901
|
teardownMcpApps(tab, 'Tab closed');
|
|
2872
2902
|
if (tab.automationRunId && !detachAutomation) {
|
|
2903
|
+
const runId = tab.automationRunId;
|
|
2873
2904
|
window.dispatchEvent(new CustomEvent('native-loop:dismissed', {
|
|
2874
|
-
detail: { runId
|
|
2905
|
+
detail: { runId },
|
|
2875
2906
|
}));
|
|
2876
|
-
|
|
2907
|
+
// A worker that finished its task is completed; any other run is stopped.
|
|
2908
|
+
endWorkerRunOnClose(runId, () => fetch('/api/loop/stop', {
|
|
2877
2909
|
method: 'POST',
|
|
2878
2910
|
headers: { 'Content-Type': 'application/json' },
|
|
2879
|
-
body: JSON.stringify({ runId
|
|
2880
|
-
}).catch(() => {});
|
|
2911
|
+
body: JSON.stringify({ runId }),
|
|
2912
|
+
}).catch(() => {}));
|
|
2881
2913
|
}
|
|
2914
|
+
if (tab.automationRunId) forgetWorkerEnd(tab.automationRunId);
|
|
2882
2915
|
wsDisconnectTab(tab, { releaseWriter: !tabHasActiveWriterWork(tab) });
|
|
2883
2916
|
tab.closed = true;
|
|
2884
2917
|
tab.titleRequestController?.abort();
|
|
@@ -2923,6 +2956,31 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
|
|
|
2923
2956
|
saveTabs();
|
|
2924
2957
|
}
|
|
2925
2958
|
|
|
2959
|
+
/**
|
|
2960
|
+
* The end of a worker the Assistant dispatched (ui-worker-end.js), as
|
|
2961
|
+
* `tab._workerEnd` says it: one card, the last node of the transcript, put
|
|
2962
|
+
* back after a history render and never in a snapshot. Its button closes the
|
|
2963
|
+
* tab the way the X does (a warm worker that finished its task is completed,
|
|
2964
|
+
* one still at work is stopped; its record stays for the Assistant) and shows
|
|
2965
|
+
* the Assistant session that dispatched it.
|
|
2966
|
+
*/
|
|
2967
|
+
export function placeWorkerEnd(tab) {
|
|
2968
|
+
const messagesEl = tab?.messagesEl;
|
|
2969
|
+
if (!messagesEl || tab.closed) return;
|
|
2970
|
+
messagesEl.querySelectorAll(`:scope > .${WORKER_END_CLASS}`).forEach((node) => node.remove());
|
|
2971
|
+
const view = tab._workerEnd;
|
|
2972
|
+
if (!view) return;
|
|
2973
|
+
messagesEl.appendChild(buildWorkerEndCard(view, {
|
|
2974
|
+
classes: { card: 'cxp-worker-end', title: 'cxp-post-plan-header', detail: 'cxp-post-plan-note', button: 'cxp-request-btn' },
|
|
2975
|
+
onBack: () => {
|
|
2976
|
+
const idx = _tabs.indexOf(tab);
|
|
2977
|
+
if (idx >= 0) closeTab(idx);
|
|
2978
|
+
backToAssistant(view.assistantSessionId);
|
|
2979
|
+
},
|
|
2980
|
+
}));
|
|
2981
|
+
if (isActiveTab(tab)) scrollEnd(true);
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2926
2984
|
export function saveTabs() {
|
|
2927
2985
|
try {
|
|
2928
2986
|
commitBoundState();
|
|
@@ -2977,6 +3035,7 @@ export function saveTabs() {
|
|
|
2977
3035
|
automationRunId: tab.automationRunId || null,
|
|
2978
3036
|
automationActive: !!tab.automationActive,
|
|
2979
3037
|
automationOwnerId: tab.automationOwnerId || null,
|
|
3038
|
+
releasedRunId: tab.releasedRunId || null,
|
|
2980
3039
|
};
|
|
2981
3040
|
});
|
|
2982
3041
|
const localRunIds = new Set(localTabs.map((tab) => tab.automationRunId).filter(Boolean));
|
|
@@ -3078,10 +3137,14 @@ export function restoreTabs() {
|
|
|
3078
3137
|
}
|
|
3079
3138
|
} catch {}
|
|
3080
3139
|
|
|
3140
|
+
// A window with no tabs of its own reopens the last thread, under the account it belongs to: its rollout
|
|
3141
|
+
// lives in that account's CODEX_HOME, never Default's.
|
|
3142
|
+
const lastThreadId = storage.getItem(STOR.threadId) || null;
|
|
3081
3143
|
createTab({
|
|
3082
3144
|
project: storage.getItem(STOR.project) || '',
|
|
3083
|
-
threadId:
|
|
3145
|
+
threadId: lastThreadId,
|
|
3084
3146
|
title: storage.getItem(STOR.title) || '',
|
|
3147
|
+
...(lastThreadId ? { accountId: storage.getItem(STOR.threadAccount) || 'default' } : {}),
|
|
3085
3148
|
}, { autoSwitch: false });
|
|
3086
3149
|
_activeTabIdx = 0;
|
|
3087
3150
|
updateActiveTabView();
|
|
@@ -3097,6 +3160,34 @@ export function updateTrayPillRunning() {
|
|
|
3097
3160
|
}
|
|
3098
3161
|
|
|
3099
3162
|
|
|
3163
|
+
// ── Greeting (shared/greeting/, lib/greeting/) ──
|
|
3164
|
+
// What SynaBun says when a person comes back and opens a new thread: one or two sentences in the
|
|
3165
|
+
// empty state, written by SynaBun ahead of time (the thread's model never greets). Only for the
|
|
3166
|
+
// bound tab while it holds no thread and shows no automation run; the short wait lets a tab that
|
|
3167
|
+
// was made for a run, or is restoring a thread, say so first. Its id goes back with the first
|
|
3168
|
+
// prompt (`greetingId`), so the model is told once what was said.
|
|
3169
|
+
function offerGreetingSoon() {
|
|
3170
|
+
const tab = _boundTab;
|
|
3171
|
+
if (!tab) return;
|
|
3172
|
+
setTimeout(() => { try { offerGreeting(tab); } catch { /* a thread always opens */ } }, 300);
|
|
3173
|
+
}
|
|
3174
|
+
function offerGreeting(tab) {
|
|
3175
|
+
if (tab !== _boundTab || tab.closed || _threadId || _running || _startingThread || tab.automationActive || tab.automationRunId) return;
|
|
3176
|
+
const host = _messagesEl?.querySelector(':scope > .cxp-empty');
|
|
3177
|
+
if (!host || host.style.display === 'none') return;
|
|
3178
|
+
// "Restoring Codex thread…" / "Starting fresh Codex thread…": not an idle new thread.
|
|
3179
|
+
if (host.querySelector('.cxp-empty-status')?.textContent) return;
|
|
3180
|
+
const key = () => `${tab.id}.${tab.greetSeq || 0}`;
|
|
3181
|
+
const session = key();
|
|
3182
|
+
mountGreeting(host, {
|
|
3183
|
+
surface: 'codex-panel', session, cwd: _project || tab.project || '',
|
|
3184
|
+
onOpenMemory: (id) => { if (id) emit('memory:open', { id }); },
|
|
3185
|
+
isCurrent: () => tab === _boundTab && !_threadId && session === key() && !!_messagesEl?.contains(host),
|
|
3186
|
+
}).then((greeting) => {
|
|
3187
|
+
if (session === key()) tab.greeting = greeting ? { id: greeting.id, session } : null;
|
|
3188
|
+
}).catch(() => {});
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3100
3191
|
// ── Thread state (lines 4711-5000) ──
|
|
3101
3192
|
function hideEmpty() {
|
|
3102
3193
|
const empty = _messagesEl?.querySelector('.cxp-empty');
|
|
@@ -3125,6 +3216,7 @@ export function clearTranscript(emptyText = freshThreadText()) {
|
|
|
3125
3216
|
for (const child of [..._messagesEl.children]) retireMcpAppNode(child, () => child.remove());
|
|
3126
3217
|
_messagesEl.insertAdjacentHTML('afterbegin', `<div class="cxp-empty"><div class="cxp-empty-logo">${OPENAI_ICON}</div><span class="cxp-empty-name">Codex</span><span class="cxp-empty-status"></span></div>`);
|
|
3127
3218
|
document.dispatchEvent(new CustomEvent('cdx-empty-rebuilt'));
|
|
3219
|
+
if (!emptyText) offerGreetingSoon();
|
|
3128
3220
|
_items.clear();
|
|
3129
3221
|
_requestCards.clear();
|
|
3130
3222
|
_blockingServerRequests = new Map();
|
|
@@ -3285,6 +3377,8 @@ export function resetThreadState({ tone = 'ready', preserveStatus = false } = {}
|
|
|
3285
3377
|
clearPostCompactionState(_boundTab, { updateTranscript: false });
|
|
3286
3378
|
setSessionLabel('New session', { persist: false });
|
|
3287
3379
|
if (!preserveStatus) setStatus(_project ? 'Fresh thread ready' : 'Select a project', tone);
|
|
3380
|
+
// A new thread in the same tab is a new opening for the greeting.
|
|
3381
|
+
if (_boundTab) { _boundTab.greetSeq = (_boundTab.greetSeq || 0) + 1; _boundTab.greeting = null; }
|
|
3288
3382
|
clearTranscript();
|
|
3289
3383
|
syncInputEnabled();
|
|
3290
3384
|
syncSessionControls();
|
|
@@ -3812,12 +3906,24 @@ export function cxpSessRebuildList(menu, listEl, tab) {
|
|
|
3812
3906
|
}
|
|
3813
3907
|
|
|
3814
3908
|
// ═══════════════════════════════════════════
|
|
3815
|
-
//
|
|
3909
|
+
// Codex accounts (per tab): the header account menu
|
|
3816
3910
|
// ═══════════════════════════════════════════
|
|
3911
|
+
// Every account and its state come from the server (lib/codex-accounts.js
|
|
3912
|
+
// describe(): its mode, whether it is signed in, its identity: an email or a
|
|
3913
|
+
// masked key, never a key); account-view.js decides the words, account-menu.js
|
|
3914
|
+
// draws the menu (the same component as the Claude panel's). /account, /login,
|
|
3915
|
+
// the sign-in screen's "Other accounts…" and the Context settings popover's
|
|
3916
|
+
// Account row open it. A conversation belongs to the account it started under
|
|
3917
|
+
// (its thread lives in that account's CODEX_HOME): a tab with one opens another
|
|
3918
|
+
// account in a new tab; before its first message the tab itself switches.
|
|
3919
|
+
|
|
3920
|
+
let _codexAccountsError = '';
|
|
3921
|
+
let _codexAccountsLoad = null;
|
|
3922
|
+
let _accountMenu = null;
|
|
3817
3923
|
|
|
3818
3924
|
function accountLabelFor(id) {
|
|
3819
3925
|
const acct = _codexAccounts.find((a) => a.id === id);
|
|
3820
|
-
return acct ? (acct
|
|
3926
|
+
return acct ? accountName(acct, 'codex') : (id === 'default' ? accountText('defaultName', {}, 'codex') : 'account');
|
|
3821
3927
|
}
|
|
3822
3928
|
|
|
3823
3929
|
// The tab's account as the Context settings popover names it: the label, and
|
|
@@ -3826,29 +3932,35 @@ function accountView(tab) {
|
|
|
3826
3932
|
const acctId = tab?.accountId || _accountId || 'default';
|
|
3827
3933
|
const acct = _codexAccounts.find((a) => a.id === acctId);
|
|
3828
3934
|
return {
|
|
3829
|
-
label: acct
|
|
3830
|
-
|
|
3831
|
-
: (acctId === 'default' ? 'Default' : 'Account'),
|
|
3832
|
-
tip: acct?.email
|
|
3833
|
-
? `Account: ${acct.email}${acct.planType ? ` (${acct.planType})` : ''} — click to switch`
|
|
3834
|
-
: 'Switch ChatGPT account for this tab',
|
|
3935
|
+
label: acct ? accountName(acct, 'codex') : accountLabelFor(acctId),
|
|
3936
|
+
tip: acct ? accountSummary(acct, 'codex') : accountText('menuTitle', {}, 'codex'),
|
|
3835
3937
|
};
|
|
3836
3938
|
}
|
|
3837
3939
|
|
|
3838
|
-
// The account is a row of the Context settings popover.
|
|
3940
|
+
// The account is a row of the Context settings popover and the header menu's trigger.
|
|
3839
3941
|
export function syncAccountChip() {
|
|
3840
3942
|
syncContextMenu();
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
}
|
|
3850
|
-
|
|
3851
|
-
|
|
3943
|
+
_accountMenu?.render();
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
/** Read every account with its state (GET /api/codex/accounts); `refresh` asks Codex about each first. */
|
|
3947
|
+
export function refreshCodexAccounts({ refresh = false } = {}) {
|
|
3948
|
+
if (_codexAccountsLoad && !refresh) return _codexAccountsLoad;
|
|
3949
|
+
const load = fetch(`/api/codex/accounts${refresh ? '?refresh=true' : ''}`, { cache: 'no-store' })
|
|
3950
|
+
.then((response) => response.json().catch(() => ({})).then((data) => {
|
|
3951
|
+
if (!response.ok || !Array.isArray(data?.accounts)) throw new Error(data?.error || accountText('readFailed', {}, 'codex'));
|
|
3952
|
+
_codexAccounts = data.accounts;
|
|
3953
|
+
_codexAccountsError = '';
|
|
3954
|
+
return _codexAccounts;
|
|
3955
|
+
}))
|
|
3956
|
+
.catch((error) => { _codexAccountsError = accountText('readFailed', {}, 'codex'); throw error; })
|
|
3957
|
+
.finally(() => {
|
|
3958
|
+
if (_codexAccountsLoad === load) _codexAccountsLoad = null;
|
|
3959
|
+
syncAccountChip();
|
|
3960
|
+
});
|
|
3961
|
+
_codexAccountsLoad = load;
|
|
3962
|
+
load.catch(() => {});
|
|
3963
|
+
return load;
|
|
3852
3964
|
}
|
|
3853
3965
|
|
|
3854
3966
|
function resetViewForAccountChange(label) {
|
|
@@ -3874,29 +3986,33 @@ function resetViewForAccountChange(label) {
|
|
|
3874
3986
|
}
|
|
3875
3987
|
setSessionLabel('New session', { persist: false });
|
|
3876
3988
|
clearTranscript(freshThreadText());
|
|
3989
|
+
if (_boundTab) ensureCodexTerminalAuth(_boundTab);
|
|
3877
3990
|
if (label) setStatus(label, 'ready');
|
|
3878
3991
|
}
|
|
3879
3992
|
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
if (acct && info.email) {
|
|
3884
|
-
acct.email = info.email;
|
|
3885
|
-
acct.planType = info.planType || acct.planType;
|
|
3886
|
-
if (!acct.label || acct.label === 'New account') acct.label = info.email;
|
|
3887
|
-
}
|
|
3993
|
+
// What the server says an account holds, for the sign-in screen: { loggedIn } (true / false / unknown).
|
|
3994
|
+
function signInStateOf(row) {
|
|
3995
|
+
return row && typeof row.signedIn === 'boolean' ? { loggedIn: row.signedIn } : null;
|
|
3888
3996
|
}
|
|
3889
3997
|
|
|
3890
3998
|
function handleAccountMessage(msg) {
|
|
3999
|
+
const ownsAccount = (!msg.accountId || msg.accountId === _boundTab?.accountId)
|
|
4000
|
+
&& (!msg.state?.accountId || msg.state.accountId === _boundTab?.accountId);
|
|
3891
4001
|
if (Array.isArray(msg.accounts)) _codexAccounts = msg.accounts;
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
4002
|
+
// The server's reading of the account (accountState / account_state) is what every surface shows.
|
|
4003
|
+
const state = msg.type === 'account_state' ? msg.account : msg.accountState;
|
|
4004
|
+
if (state?.id) {
|
|
4005
|
+
const index = _codexAccounts.findIndex((a) => a.id === state.id);
|
|
4006
|
+
if (index >= 0) _codexAccounts[index] = state; else _codexAccounts.push(state);
|
|
4007
|
+
}
|
|
4008
|
+
if (ownsAccount && !msg.error) {
|
|
4009
|
+
const signIn = signInStateOf(state);
|
|
4010
|
+
if (signIn) _boundTab?.terminalAuthView?.update(signIn);
|
|
4011
|
+
if (msg.type === 'account_logged_out' && !state) _boundTab?.terminalAuthView?.update({ loggedIn: false });
|
|
4012
|
+
}
|
|
4013
|
+
if (msg.type === 'account_info' && !msg.error && (!msg.accountId || msg.accountId === _accountId)) refreshModelCatalog(_boundTab);
|
|
3896
4014
|
if (msg.type === 'account_logged_out' && !msg.error) refreshModelCatalog(_boundTab);
|
|
3897
|
-
if (msg.type === '
|
|
3898
|
-
applyAccountReadToCache(msg.accountId || _accountId || 'default', msg.account);
|
|
3899
|
-
}
|
|
4015
|
+
if (msg.type === 'account_state' && ownsAccount) { invalidateCodexFeatureCaches(_boundTab); refreshModelCatalog(_boundTab); }
|
|
3900
4016
|
if (msg.type === 'account_switched' && msg.accountId) {
|
|
3901
4017
|
_accountId = msg.accountId;
|
|
3902
4018
|
if (_boundTab) _boundTab.accountId = msg.accountId;
|
|
@@ -3904,139 +4020,118 @@ function handleAccountMessage(msg) {
|
|
|
3904
4020
|
resetViewForAccountChange(`Switched to ${accountLabelFor(msg.accountId)}`);
|
|
3905
4021
|
refreshModelCatalog(_boundTab);
|
|
3906
4022
|
}
|
|
3907
|
-
if (msg.type === 'account_add_started') {
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
saveTabs();
|
|
3912
|
-
}
|
|
4023
|
+
if (msg.type === 'account_add_started' && msg.accountId) {
|
|
4024
|
+
_accountId = msg.accountId;
|
|
4025
|
+
if (_boundTab) _boundTab.accountId = msg.accountId;
|
|
4026
|
+
saveTabs();
|
|
3913
4027
|
resetViewForAccountChange('');
|
|
3914
|
-
appendSystem('Opening ChatGPT login in your browser… complete sign-in, then return here. The account name updates automatically once you finish.', 'working');
|
|
3915
4028
|
}
|
|
3916
4029
|
syncAccountChip();
|
|
3917
|
-
const menu = panelEl('#cxp-account-menu');
|
|
3918
|
-
if (menu?.classList.contains('open')) renderAccountMenu();
|
|
3919
4030
|
}
|
|
3920
4031
|
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
if (!
|
|
3924
|
-
const
|
|
3925
|
-
|
|
4032
|
+
// The header account menu (mounted by cdx-panel.js into the header's actions).
|
|
4033
|
+
export function mountCodexAccountMenu(actionsEl, panelEl) {
|
|
4034
|
+
if (_accountMenu || !actionsEl) return _accountMenu;
|
|
4035
|
+
const path = (id) => `/api/codex/accounts/${encodeURIComponent(id)}`;
|
|
4036
|
+
_accountMenu = mountAccountMenu({
|
|
4037
|
+
provider: 'codex', container: actionsEl, before: actionsEl.firstChild, layer: () => panelEl,
|
|
4038
|
+
read: () => {
|
|
4039
|
+
const tab = activeTab();
|
|
4040
|
+
return {
|
|
4041
|
+
rows: _codexAccounts, currentId: tab?.accountId || 'default', hasConversation: !!tab?.threadId,
|
|
4042
|
+
available: !!tab && !tab.automationRunId, busy: !!(tab?.running || tab?.automationActive),
|
|
4043
|
+
loading: !_codexAccounts.length && !_codexAccountsError, error: _codexAccountsError,
|
|
4044
|
+
};
|
|
4045
|
+
},
|
|
4046
|
+
actions: {
|
|
4047
|
+
load: ({ refresh }) => refreshCodexAccounts({ refresh }),
|
|
4048
|
+
pick: (id) => { const tab = activeTab(); if (tab) pickCodexAccount(tab, id); },
|
|
4049
|
+
add: async () => (await codexAccountRequest('/api/codex/accounts', { method: 'POST', body: '{}' })).account,
|
|
4050
|
+
signIn: (id) => startProviderTerminalLogin('codex', id, `Codex sign-in · ${accountLabelFor(id)}`),
|
|
4051
|
+
saveKey: (id, key) => providerApiKeyRequest('codex', id, key),
|
|
4052
|
+
setMode: (id, mode) => codexAccountRequest(path(id), { method: 'PATCH', body: JSON.stringify({ mode }) }),
|
|
4053
|
+
signOut: (id, credential) => codexAccountRequest(`${path(id)}/auth/logout`, { method: 'POST', body: JSON.stringify({ credential }) }),
|
|
4054
|
+
rename: (id, label) => codexAccountRequest(path(id), { method: 'PATCH', body: JSON.stringify({ label }) }),
|
|
4055
|
+
remove: (id) => removeCodexAccount(id),
|
|
4056
|
+
},
|
|
4057
|
+
});
|
|
4058
|
+
refreshCodexAccounts().catch(() => {});
|
|
4059
|
+
return _accountMenu;
|
|
3926
4060
|
}
|
|
3927
4061
|
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
if (!
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
if (!_codexAccounts.length) {
|
|
3938
|
-
const empty = document.createElement('div');
|
|
3939
|
-
empty.className = 'cxp-account-empty';
|
|
3940
|
-
empty.textContent = 'Loading accounts…';
|
|
3941
|
-
list.appendChild(empty);
|
|
3942
|
-
}
|
|
3943
|
-
for (const acct of _codexAccounts) {
|
|
3944
|
-
const row = document.createElement('div');
|
|
3945
|
-
row.className = 'cxp-account-row' + (acct.id === activeAcctId ? ' active' : '');
|
|
3946
|
-
const main = document.createElement('button');
|
|
3947
|
-
main.className = 'cxp-account-row-main';
|
|
3948
|
-
main.type = 'button';
|
|
3949
|
-
const sub = acct.email && acct.email !== acct.label
|
|
3950
|
-
? `<span class="cxp-account-row-sub">${esc(acct.email)}${acct.planType ? ` · ${esc(acct.planType)}` : ''}</span>`
|
|
3951
|
-
: (acct.planType ? `<span class="cxp-account-row-sub">${esc(acct.planType)}</span>` : '');
|
|
3952
|
-
main.innerHTML = `
|
|
3953
|
-
<span class="cxp-account-check">${acct.id === activeAcctId ? '✓' : ''}</span>
|
|
3954
|
-
<span class="cxp-account-row-text">
|
|
3955
|
-
<span class="cxp-account-row-label">${esc(acct.label || acct.email || (acct.isDefault ? 'Default' : 'Account'))}</span>
|
|
3956
|
-
${sub}
|
|
3957
|
-
</span>`;
|
|
3958
|
-
main.addEventListener('click', () => { menu.classList.remove('open'); switchToAccount(acct.id); });
|
|
3959
|
-
row.appendChild(main);
|
|
3960
|
-
if (!acct.isDefault) {
|
|
3961
|
-
const removeBtn = document.createElement('button');
|
|
3962
|
-
removeBtn.className = 'cxp-account-row-remove';
|
|
3963
|
-
removeBtn.type = 'button';
|
|
3964
|
-
removeBtn.title = 'Remove account';
|
|
3965
|
-
removeBtn.innerHTML = '✕';
|
|
3966
|
-
removeBtn.addEventListener('click', (e) => { e.stopPropagation(); removeCodexAccount(acct.id); });
|
|
3967
|
-
row.appendChild(removeBtn);
|
|
3968
|
-
}
|
|
3969
|
-
list.appendChild(row);
|
|
3970
|
-
}
|
|
3971
|
-
menu.appendChild(list);
|
|
3972
|
-
|
|
3973
|
-
const addBtn = document.createElement('button');
|
|
3974
|
-
addBtn.className = 'cxp-account-add';
|
|
3975
|
-
addBtn.type = 'button';
|
|
3976
|
-
addBtn.textContent = '+ Add ChatGPT account…';
|
|
3977
|
-
addBtn.addEventListener('click', () => { menu.classList.remove('open'); addCodexAccount(); });
|
|
3978
|
-
menu.appendChild(addBtn);
|
|
4062
|
+
/** Open the header account menu (`manage`: one account's management). */
|
|
4063
|
+
export function openAccountMenu({ manage = '' } = {}) {
|
|
4064
|
+
if (!_accountMenu) return;
|
|
4065
|
+
_accountMenu.open(manage ? { manage } : {});
|
|
4066
|
+
}
|
|
4067
|
+
// The Context settings popover's Account row and older callers.
|
|
4068
|
+
export function toggleAccountMenu() {
|
|
4069
|
+
if (_accountMenu?.isOpen()) _accountMenu.close({ restore: true });
|
|
4070
|
+
else openAccountMenu();
|
|
3979
4071
|
}
|
|
3980
4072
|
|
|
3981
|
-
async function
|
|
3982
|
-
const
|
|
3983
|
-
|
|
3984
|
-
if ((
|
|
3985
|
-
|
|
3986
|
-
try {
|
|
3987
|
-
setStatus('Switching account…', 'working');
|
|
3988
|
-
await requestSocketPayloadForTab(tab, 'account_switch', { accountId });
|
|
3989
|
-
// account_switched push (handled in handleAccountMessage) updates the view.
|
|
3990
|
-
} catch (err) {
|
|
3991
|
-
if (!tab.closed) withTab(tab, () => {
|
|
3992
|
-
appendSystem(err.message || 'Could not switch account', 'error');
|
|
3993
|
-
setStatus(err.message || 'Switch failed', 'error');
|
|
3994
|
-
});
|
|
3995
|
-
}
|
|
4073
|
+
async function codexAccountRequest(url, options = {}) {
|
|
4074
|
+
const response = await fetch(url, { ...options, cache: 'no-store', headers: { 'Content-Type': 'application/json' } });
|
|
4075
|
+
const data = await response.json().catch(() => ({}));
|
|
4076
|
+
if (!response.ok || data.error) throw new Error(data.error || 'Codex account request failed.');
|
|
4077
|
+
return data;
|
|
3996
4078
|
}
|
|
3997
4079
|
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
const
|
|
4005
|
-
if (
|
|
4006
|
-
|
|
4007
|
-
if (authUrl) window.open(authUrl, '_blank', 'noopener,noreferrer');
|
|
4008
|
-
if (isActiveTab(tab)) syncAccountChip();
|
|
4009
|
-
} catch (err) {
|
|
4010
|
-
if (!tab.closed) withTab(tab, () => {
|
|
4011
|
-
appendSystem(err.message || 'Could not start login', 'error');
|
|
4012
|
-
setStatus(err.message || 'Login failed', 'error');
|
|
4013
|
-
});
|
|
4080
|
+
// Pick an account for a tab: the tab switches before its first message; a tab with a conversation keeps it
|
|
4081
|
+
// and the other account opens in a new tab.
|
|
4082
|
+
function pickCodexAccount(tab, id) {
|
|
4083
|
+
const plan = accountSwitchPlan({ rows: _codexAccounts, currentId: tab.accountId || 'default', targetId: id, hasConversation: !!tab.threadId, provider: 'codex' });
|
|
4084
|
+
if (plan.effect === 'missing' || plan.effect === 'same') { withTab(tab, () => appendSystem(plan.message, 'muted')); return; }
|
|
4085
|
+
if (plan.effect === 'next-chat') {
|
|
4086
|
+
const fresh = createTab({ project: tab.project || '', accountId: id });
|
|
4087
|
+
if (fresh) withTab(fresh, () => appendSystem(accountText('newChatUnder', { label: accountLabelFor(id) }, 'codex'), 'muted'));
|
|
4088
|
+
return;
|
|
4014
4089
|
}
|
|
4090
|
+
if (tab.running) { withTab(tab, () => appendSystem(accountText('busyTurn', {}, 'codex'), 'error')); return; }
|
|
4091
|
+
setStatus('Switching account…', 'working');
|
|
4092
|
+
requestSocketPayloadForTab(tab, 'account_switch', { accountId: id })
|
|
4093
|
+
.then(() => { if (!tab.closed) withTab(tab, () => appendSystem(plan.message, 'muted')); })
|
|
4094
|
+
.catch((err) => {
|
|
4095
|
+
if (!tab.closed) withTab(tab, () => {
|
|
4096
|
+
appendSystem(err.message || 'Could not switch account', 'error');
|
|
4097
|
+
setStatus(err.message || 'Switch failed', 'error');
|
|
4098
|
+
});
|
|
4099
|
+
});
|
|
4015
4100
|
}
|
|
4016
4101
|
|
|
4102
|
+
// Remove an account. The server refuses while a tab (this window's or another's), the Assistant or a run
|
|
4103
|
+
// uses it, with what to do: the menu says it. A conversation is never reset to free an account.
|
|
4017
4104
|
async function removeCodexAccount(accountId) {
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
const
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4105
|
+
await codexAccountRequest(`/api/codex/accounts/${encodeURIComponent(accountId)}`, { method: 'DELETE' });
|
|
4106
|
+
}
|
|
4107
|
+
|
|
4108
|
+
function ensureCodexTerminalAuth(tab) {
|
|
4109
|
+
const accountId = tab.accountId || 'default';
|
|
4110
|
+
if (tab.closed || !tab.messagesEl) return;
|
|
4111
|
+
if (tab.terminalAuthView && tab.terminalAuthAccount === accountId) { void tab.terminalAuthView.refresh(); return; }
|
|
4112
|
+
tab.terminalAuthView?.dispose();
|
|
4113
|
+
tab.terminalAuthAccount = accountId;
|
|
4114
|
+
const named = accountId !== 'default' ? _codexAccounts.find((a) => a.id === accountId) : null;
|
|
4115
|
+
tab.terminalAuthView = mountProviderTerminalAuth({
|
|
4116
|
+
host: tab.messagesEl, provider: 'codex', accountId, logo: OPENAI_ICON,
|
|
4117
|
+
...(named ? { message: `Sign in to Codex to use ${accountName(named, 'codex')}.` } : {}),
|
|
4118
|
+
// Another account (or a new one) is one click away from the sign-in screen: the header account menu.
|
|
4119
|
+
moreAction: { label: accountText('otherAccounts', {}, 'codex'), run: () => { if (isActiveTab(tab)) openAccountMenu(); } },
|
|
4120
|
+
isCurrent: () => !tab.closed && (tab.accountId || 'default') === accountId,
|
|
4121
|
+
// The account as the server reads it (its ChatGPT sign-in and the key it holds), not one app-server's view.
|
|
4122
|
+
readAccount: async () => {
|
|
4123
|
+
const result = await codexAccountRequest(`/api/codex/accounts/${encodeURIComponent(accountId)}/auth?refresh=true`);
|
|
4124
|
+
const row = result.account;
|
|
4125
|
+
if (row?.id) {
|
|
4126
|
+
const index = _codexAccounts.findIndex((a) => a.id === row.id);
|
|
4127
|
+
if (index >= 0) _codexAccounts[index] = row; else _codexAccounts.push(row);
|
|
4128
|
+
if (isActiveTab(tab)) syncAccountChip();
|
|
4129
|
+
}
|
|
4130
|
+
return signInStateOf(row);
|
|
4131
|
+
},
|
|
4132
|
+
requestApiKey: (provider, id, key) => providerApiKeyRequest(provider, id, key),
|
|
4133
|
+
onSignedIn: () => { invalidateCodexFeatureCaches(tab); refreshModelCatalog(tab); refreshCodexAccounts().catch(() => {}); },
|
|
4134
|
+
});
|
|
4040
4135
|
}
|
|
4041
4136
|
|
|
4042
4137
|
export async function renderSessionMenu() {
|
|
@@ -4896,7 +4991,7 @@ export function syncInputEnabled() {
|
|
|
4896
4991
|
if (send) {
|
|
4897
4992
|
const isStopMode = !!(tab?.running || _running);
|
|
4898
4993
|
send.classList.toggle('cxp-send-stopping', !!isStopMode);
|
|
4899
|
-
send.title = actionBlockedByCli ?
|
|
4994
|
+
send.title = actionBlockedByCli ? codexCliBlockedText() : (isStopMode ? 'Stop' : 'Send');
|
|
4900
4995
|
send.disabled = actionBlockedByCli || (isStopMode ? false : (!_connected || !_bootstrapped || _running || _startingThread || blockedByRequest || (!hasText && !hasImages && !hasPaths && !hasMentions)));
|
|
4901
4996
|
}
|
|
4902
4997
|
if (fresh) {
|
|
@@ -4954,6 +5049,14 @@ export function dispatchPrompt(prompt, { tab = activeTab(), images = null, paths
|
|
|
4954
5049
|
};
|
|
4955
5050
|
if (tab?.model) msg.model = tab.model;
|
|
4956
5051
|
if (tab?.effort && tab.effort !== 'off') msg.effort = tab.effort;
|
|
5052
|
+
// A person's own turn: the greeting engine keeps when they were last here. The greeting this
|
|
5053
|
+
// tab opened with goes back once, with the first prompt of its new thread.
|
|
5054
|
+
msg.human = true;
|
|
5055
|
+
if (tab?.greeting?.id) {
|
|
5056
|
+
if (msg.fresh) msg.greetingId = tab.greeting.id;
|
|
5057
|
+
forgetGreeting('codex-panel', tab.greeting.session);
|
|
5058
|
+
tab.greeting = null;
|
|
5059
|
+
}
|
|
4957
5060
|
if (imageList.length) {
|
|
4958
5061
|
msg.images = imageList;
|
|
4959
5062
|
}
|
|
@@ -5186,6 +5289,12 @@ function noteCodexRevert(event) {
|
|
|
5186
5289
|
|
|
5187
5290
|
// ── Notification handler (lines 7538-7698) ──
|
|
5188
5291
|
export function handleNotify(method, params) {
|
|
5292
|
+
// Codex's own account changed: the server's reading of the account (its ChatGPT sign-in and the
|
|
5293
|
+
// key SynaBun holds) decides what the tab shows, so the sign-in screen and the menu ask it again.
|
|
5294
|
+
if (method === 'account/updated') {
|
|
5295
|
+
void _boundTab?.terminalAuthView?.refresh();
|
|
5296
|
+
refreshCodexAccounts().catch(() => {});
|
|
5297
|
+
}
|
|
5189
5298
|
if (['skills/changed', 'app/list/updated', 'account/updated', 'account/rateLimits/updated', 'config/updated', 'thread/attachment/updated', 'thread/settings/updated', 'account/gatewayOAuth/changed'].includes(method)) {
|
|
5190
5299
|
invalidateCodexFeatureCaches(_boundTab);
|
|
5191
5300
|
}
|
|
@@ -5550,7 +5659,7 @@ export function handleNotify(method, params) {
|
|
|
5550
5659
|
break;
|
|
5551
5660
|
}
|
|
5552
5661
|
case 'configWarning': {
|
|
5553
|
-
|
|
5662
|
+
showCodexConfigWarning(params);
|
|
5554
5663
|
break;
|
|
5555
5664
|
}
|
|
5556
5665
|
case 'deprecationNotice':
|
|
@@ -6257,7 +6366,7 @@ function formatStatusSnapshotMarkdown(snapshot, tab = activeTab()) {
|
|
|
6257
6366
|
`- Protocol baseline: \`${runtime.protocolBaseline || 'not reported'}\``,
|
|
6258
6367
|
];
|
|
6259
6368
|
if (account) {
|
|
6260
|
-
lines.push(`- Account: \`${account.email || account.name || account.type || 'signed in'}\`${account.planType ? ` (${account.planType})` : ''}`);
|
|
6369
|
+
lines.push(`- Account: \`${account.type === 'apiKey' ? 'API key' : account.email || account.name || account.type || 'signed in'}\`${account.planType ? ` (${account.planType})` : ''}`);
|
|
6261
6370
|
} else {
|
|
6262
6371
|
lines.push('- Account: signed out');
|
|
6263
6372
|
}
|
|
@@ -6660,6 +6769,15 @@ async function handleLocalSlashCommand(payload = currentComposerPayload()) {
|
|
|
6660
6769
|
invalidateCodexFeatureCaches(tab);
|
|
6661
6770
|
appendMarkdown('Logged out of Codex.');
|
|
6662
6771
|
break;
|
|
6772
|
+
case 'login':
|
|
6773
|
+
// Sign-in lives in the header account menu: this tab's account, its two ways to sign in.
|
|
6774
|
+
consumeComposer(input, tab, { clearAttachments: false });
|
|
6775
|
+
openAccountMenu({ manage: serverAccountId(tab.accountId) });
|
|
6776
|
+
break;
|
|
6777
|
+
case 'account':
|
|
6778
|
+
consumeComposer(input, tab, { clearAttachments: false });
|
|
6779
|
+
openAccountMenu();
|
|
6780
|
+
break;
|
|
6663
6781
|
case 'diff':
|
|
6664
6782
|
consumeComposer(input, tab, { clearAttachments: false });
|
|
6665
6783
|
if (!tab.project) {
|
|
@@ -6720,6 +6838,13 @@ async function handleLocalSlashCommand(payload = currentComposerPayload()) {
|
|
|
6720
6838
|
return true;
|
|
6721
6839
|
}
|
|
6722
6840
|
|
|
6841
|
+
// Why Send is off while the CLI banner shows (ui-cli-banner.js): the banner's own title, in the page's
|
|
6842
|
+
// language — not installed, being installed, or failed to start.
|
|
6843
|
+
function codexCliBlockedText() {
|
|
6844
|
+
const key = isCliInstalling('codex') ? 'titleInstalling' : getCliState('codex') === 'broken' ? 'titleBroken' : 'titleMissing';
|
|
6845
|
+
return cliInstallText(key, { label: 'Codex' });
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6723
6848
|
// ── Send prompt & queue (lines 8165-8320) ──
|
|
6724
6849
|
export function sendPrompt() {
|
|
6725
6850
|
const payload = currentComposerPayload();
|
|
@@ -6729,7 +6854,7 @@ export function sendPrompt() {
|
|
|
6729
6854
|
// Block when CLI binary missing — banner already informs user.
|
|
6730
6855
|
const panel = document.getElementById('codex-panel');
|
|
6731
6856
|
if (panel?.classList.contains('cxp-cli-blocked')) {
|
|
6732
|
-
appendSystem(
|
|
6857
|
+
appendSystem(`${codexCliBlockedText()}.`, 'error');
|
|
6733
6858
|
return;
|
|
6734
6859
|
}
|
|
6735
6860
|
handleLocalSlashCommand(payload).then((handled) => {
|
|
@@ -6971,6 +7096,13 @@ export function renderImageStrip() {
|
|
|
6971
7096
|
}
|
|
6972
7097
|
|
|
6973
7098
|
// ── Whiteboard "Send to Panel" — receive image via event bus ──
|
|
7099
|
+
// Any window or surface changed a Codex account (signed in or out, a key, its mode, renamed, added,
|
|
7100
|
+
// removed): the menu re-reads the list, and the tabs on that account re-check sign-in.
|
|
7101
|
+
appOn('sync:codex:accounts-changed', (msg = {}) => {
|
|
7102
|
+
refreshCodexAccounts().catch(() => {});
|
|
7103
|
+
for (const tab of _tabs) if (!msg.accountId || serverAccountId(tab.accountId) === msg.accountId) void tab.terminalAuthView?.refresh();
|
|
7104
|
+
});
|
|
7105
|
+
|
|
6974
7106
|
appOn('wb:send-to-panel', ({ dataUrl }) => {
|
|
6975
7107
|
if (!dataUrl) return;
|
|
6976
7108
|
if (appState.lastActivePanel !== 'codex' || isHostedSessionFocused()) return;
|
|
@@ -7365,512 +7497,59 @@ export function confirmSlashHint() {
|
|
|
7365
7497
|
}
|
|
7366
7498
|
|
|
7367
7499
|
// ── Phase 6: Settings panel ──
|
|
7368
|
-
|
|
7500
|
+
function clearCodexConfigWarnings({ obsoleteOnly = false } = {}) {
|
|
7501
|
+
_getPanelEl()?.querySelectorAll('.cxp-config-warning').forEach(node => {
|
|
7502
|
+
if (!obsoleteOnly || /features\.personality/.test(node.textContent || '')) node.remove();
|
|
7503
|
+
});
|
|
7504
|
+
// Remove historical copies of the obsolete Settings warning as well.
|
|
7505
|
+
_getPanelEl()?.querySelectorAll('.cxp-system.error').forEach(node => {
|
|
7506
|
+
if (/features\.personality.*ignored/s.test(node.textContent || '')) node.remove();
|
|
7507
|
+
});
|
|
7508
|
+
}
|
|
7509
|
+
|
|
7510
|
+
function showCodexConfigWarning(params) {
|
|
7511
|
+
clearCodexConfigWarnings();
|
|
7512
|
+
const warning = document.createElement('div'); warning.className = 'cxp-config-warning'; warning.setAttribute('role', 'status');
|
|
7513
|
+
const text = document.createElement('div'); text.textContent = formatCodexConfigWarning(params);
|
|
7514
|
+
const hint = document.createElement('div'); hint.textContent = t('codexSettings.warningHelp');
|
|
7515
|
+
const settings = document.createElement('button'); settings.className = 'cxp-btn cxp-btn-sm'; settings.textContent = t('codexSettings.openSettings'); settings.onclick = () => openSettingsPanel();
|
|
7516
|
+
const dismiss = document.createElement('button'); dismiss.className = 'cxp-btn cxp-btn-sm'; dismiss.textContent = t('codexSettings.dismiss'); dismiss.onclick = () => warning.remove();
|
|
7517
|
+
warning.append(text, hint, settings, dismiss);
|
|
7518
|
+
_getPanelEl()?.querySelector('.cxp-input-wrap')?.before(warning);
|
|
7519
|
+
}
|
|
7369
7520
|
|
|
7370
|
-
|
|
7521
|
+
export async function openSettingsPanel({ section = '' } = {}) {
|
|
7371
7522
|
const settingsTab = activeTab();
|
|
7372
7523
|
if (!settingsTab) return;
|
|
7373
7524
|
const settingsOwner = { accountId: settingsTab.accountId, threadId: settingsTab.threadId, connectionEpoch: settingsTab.connectionEpoch };
|
|
7374
|
-
const requestForSettings = (type, data = {}) => requestForCodexOwner(settingsTab, settingsOwner, type, data,
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
<div class="cxp-settings-section">
|
|
7394
|
-
<div class="cxp-settings-section-title">Runtime and features</div>
|
|
7395
|
-
<div class="cxp-settings-loading" id="cxp-settings-runtime">Loading…</div>
|
|
7396
|
-
<div id="cxp-settings-features"></div>
|
|
7397
|
-
</div>
|
|
7398
|
-
<div class="cxp-settings-section">
|
|
7399
|
-
<div class="cxp-settings-section-title">MCP Servers</div>
|
|
7400
|
-
<div class="cxp-settings-loading" id="cxp-settings-mcp">Loading…</div>
|
|
7401
|
-
</div>
|
|
7402
|
-
<div class="cxp-settings-section">
|
|
7403
|
-
<div class="cxp-settings-section-title">Always Allowed Folders</div>
|
|
7404
|
-
<div class="cxp-settings-loading" id="cxp-settings-permissions">Loading…</div>
|
|
7405
|
-
</div>
|
|
7406
|
-
<div class="cxp-settings-section">
|
|
7407
|
-
<div class="cxp-settings-section-title">Configuration</div>
|
|
7408
|
-
<div class="cxp-settings-loading" id="cxp-settings-config">Loading…</div>
|
|
7409
|
-
</div>
|
|
7410
|
-
</div>
|
|
7411
|
-
</div>
|
|
7412
|
-
`;
|
|
7413
|
-
_getPanelEl().appendChild(overlay);
|
|
7414
|
-
overlay.querySelector('.cxp-settings-close')?.addEventListener('click', () => overlay.remove());
|
|
7415
|
-
overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); });
|
|
7416
|
-
|
|
7417
|
-
// Fetch data in parallel
|
|
7418
|
-
try {
|
|
7419
|
-
const [accountData, mcpData, configData, rateLimitData, permissionData, requirementsData, capabilityData] = await Promise.allSettled([
|
|
7420
|
-
requestForSettings('account_read', {}),
|
|
7421
|
-
requestForSettings('mcp_status', {}),
|
|
7422
|
-
requestForSettings('config_read', { cwd: settingsTab.project || null, threadId: settingsTab.threadId || null }),
|
|
7423
|
-
requestForSettings('rate_limits_read', {}),
|
|
7424
|
-
requestForSettings('permission_roots_list', {}),
|
|
7425
|
-
requestForSettings('config_requirements', {}),
|
|
7426
|
-
getCodexCapabilities(settingsTab),
|
|
7427
|
-
]);
|
|
7428
|
-
if (settingsTab.closed) {
|
|
7429
|
-
overlay.remove();
|
|
7430
|
-
return;
|
|
7431
|
-
}
|
|
7432
|
-
const capabilities = capabilityData.status === 'fulfilled' ? capabilityData.value : {};
|
|
7433
|
-
const runtime = settingsTab.codexRuntime || {};
|
|
7434
|
-
const runtimeEl = overlay.querySelector('#cxp-settings-runtime');
|
|
7435
|
-
runtimeEl.className = 'cxp-settings-hint';
|
|
7436
|
-
runtimeEl.textContent = `CLI ${runtime.cliVersion || 'unknown'} · Automation SDK ${runtime.sdkVersion || 'not installed'} · Protocol ${runtime.protocolBaseline || 'unknown'}\n${runtime.codexBin || 'Executable path not reported'}\nAccount: ${runtime.accountId || settingsTab.accountId || 'default'}`;
|
|
7437
|
-
const featureEl = overlay.querySelector('#cxp-settings-features');
|
|
7438
|
-
for (const kind of ['skills', 'mcp', 'apps', 'plugins', 'hooks', 'ps', 'experimental', 'goal', 'feedback']) {
|
|
7439
|
-
const command = CODEX_COMMAND_BY_NAME.get(kind);
|
|
7440
|
-
const available = capabilityAvailability(capabilities, command.capability);
|
|
7441
|
-
const btn = document.createElement('button');
|
|
7442
|
-
btn.className = 'cxp-btn cxp-btn-sm';
|
|
7443
|
-
btn.textContent = `${command.name}${available.experimental ? ' · Experimental' : ''}`;
|
|
7444
|
-
btn.disabled = !available.supported || (['goal', 'ps'].includes(kind) && !settingsTab.threadId);
|
|
7445
|
-
btn.title = !available.supported ? available.reason : command.desc;
|
|
7446
|
-
btn.addEventListener('click', async () => {
|
|
7447
|
-
const context = {
|
|
7448
|
-
host: _getPanelEl(), request: requestForSettings, tab: settingsTab, capabilities,
|
|
7449
|
-
authenticate: (name) => startMcpOAuthLogin(settingsTab, name),
|
|
7450
|
-
onChanged: () => invalidateCodexFeatureCaches(settingsTab),
|
|
7451
|
-
};
|
|
7452
|
-
try {
|
|
7453
|
-
if (kind === 'goal') await openCodexGoal(context);
|
|
7454
|
-
else if (kind === 'feedback') await openCodexFeedback(context);
|
|
7455
|
-
else await openCodexCatalog({ ...context, kind });
|
|
7456
|
-
} catch (error) { appendSettingsError(error.message); }
|
|
7457
|
-
});
|
|
7458
|
-
featureEl.append(btn);
|
|
7459
|
-
if (!available.supported) {
|
|
7460
|
-
const reason = document.createElement('div'); reason.className = 'cxp-settings-hint';
|
|
7461
|
-
reason.textContent = `${command.name}: ${available.reason || 'Unavailable in this runtime'}`; featureEl.append(reason);
|
|
7462
|
-
}
|
|
7463
|
-
}
|
|
7464
|
-
|
|
7465
|
-
// Account
|
|
7466
|
-
const accountEl = overlay.querySelector('#cxp-settings-account');
|
|
7467
|
-
if (accountEl) {
|
|
7468
|
-
const acctResult = accountData.status === 'fulfilled' ? (accountData.value?.account || {}) : {};
|
|
7469
|
-
const acct = acctResult?.account || null;
|
|
7470
|
-
const acctName = acct?.email || acct?.name || acct?.type;
|
|
7471
|
-
const limitLines = rateLimitData.status === 'fulfilled'
|
|
7472
|
-
? rateLimitLines(rateLimitData.value?.rateLimits || {})
|
|
7473
|
-
: [];
|
|
7474
|
-
if (acctName) {
|
|
7475
|
-
accountEl.innerHTML = '';
|
|
7476
|
-
accountEl.className = '';
|
|
7477
|
-
const value = document.createElement('div');
|
|
7478
|
-
value.className = 'cxp-settings-value';
|
|
7479
|
-
value.textContent = acctName;
|
|
7480
|
-
accountEl.appendChild(value);
|
|
7481
|
-
if (acct?.planType) {
|
|
7482
|
-
const plan = document.createElement('div');
|
|
7483
|
-
plan.className = 'cxp-settings-hint';
|
|
7484
|
-
plan.textContent = `Plan: ${acct.planType}`;
|
|
7485
|
-
accountEl.appendChild(plan);
|
|
7486
|
-
}
|
|
7487
|
-
if (limitLines.length) {
|
|
7488
|
-
const limits = document.createElement('div');
|
|
7489
|
-
limits.className = 'cxp-settings-hint';
|
|
7490
|
-
limits.innerHTML = limitLines.map((line) => `<div>${esc(line.replace(/^- /, ''))}</div>`).join('');
|
|
7491
|
-
accountEl.appendChild(limits);
|
|
7492
|
-
}
|
|
7493
|
-
const logoutBtn = document.createElement('button');
|
|
7494
|
-
logoutBtn.className = 'cxp-settings-login-btn';
|
|
7495
|
-
logoutBtn.textContent = 'Logout';
|
|
7496
|
-
logoutBtn.addEventListener('click', async () => {
|
|
7497
|
-
logoutBtn.disabled = true;
|
|
7498
|
-
try {
|
|
7499
|
-
await requestForSettings('account_logout', {});
|
|
7500
|
-
invalidateCodexFeatureCaches(settingsTab);
|
|
7501
|
-
overlay.remove();
|
|
7502
|
-
if (isActiveTab(settingsTab)) openSettingsPanel();
|
|
7503
|
-
} catch (err) {
|
|
7504
|
-
logoutBtn.disabled = false;
|
|
7505
|
-
appendSettingsError(err.message || 'Logout failed');
|
|
7506
|
-
}
|
|
7507
|
-
});
|
|
7508
|
-
accountEl.appendChild(logoutBtn);
|
|
7509
|
-
} else {
|
|
7510
|
-
accountEl.innerHTML = '';
|
|
7511
|
-
accountEl.className = '';
|
|
7512
|
-
const hint = document.createElement('div');
|
|
7513
|
-
hint.className = 'cxp-settings-hint';
|
|
7514
|
-
hint.textContent = 'Not signed in';
|
|
7515
|
-
const loginBtn = document.createElement('button');
|
|
7516
|
-
loginBtn.className = 'cxp-settings-login-btn';
|
|
7517
|
-
loginBtn.textContent = 'Login';
|
|
7518
|
-
loginBtn.addEventListener('click', async () => {
|
|
7519
|
-
loginBtn.disabled = true;
|
|
7520
|
-
loginBtn.textContent = 'Opening…';
|
|
7521
|
-
try {
|
|
7522
|
-
const result = await requestForSettings('codex_login', {});
|
|
7523
|
-
const authUrl = result?.login?.login?.authUrl || result?.login?.authUrl;
|
|
7524
|
-
if (authUrl) window.open(authUrl, '_blank', 'noopener,noreferrer');
|
|
7525
|
-
loginBtn.textContent = authUrl ? 'Browser opened' : 'Waiting…';
|
|
7526
|
-
} catch {
|
|
7527
|
-
loginBtn.textContent = 'Login';
|
|
7528
|
-
loginBtn.disabled = false;
|
|
7529
|
-
}
|
|
7530
|
-
});
|
|
7531
|
-
accountEl.append(hint, loginBtn);
|
|
7532
|
-
}
|
|
7533
|
-
}
|
|
7534
|
-
|
|
7535
|
-
await mountCodexAccountSecurity({ host: accountEl, request: requestForSettings, tab: settingsTab, capabilities });
|
|
7536
|
-
|
|
7537
|
-
// MCP Servers
|
|
7538
|
-
const mcpEl = overlay.querySelector('#cxp-settings-mcp');
|
|
7539
|
-
if (mcpEl) {
|
|
7540
|
-
const servers = mcpData.status === 'fulfilled' ? (mcpData.value?.servers || []) : [];
|
|
7541
|
-
if (!servers.length) {
|
|
7542
|
-
mcpEl.innerHTML = '';
|
|
7543
|
-
mcpEl.className = '';
|
|
7544
|
-
const hint = document.createElement('div');
|
|
7545
|
-
hint.className = 'cxp-settings-hint';
|
|
7546
|
-
hint.textContent = 'Configure in ~/.codex/config.toml under [mcp_servers]';
|
|
7547
|
-
mcpEl.appendChild(hint);
|
|
7548
|
-
} else {
|
|
7549
|
-
mcpEl.innerHTML = '';
|
|
7550
|
-
for (const server of servers) {
|
|
7551
|
-
const row = document.createElement('div');
|
|
7552
|
-
row.className = 'cxp-mcp-row';
|
|
7553
|
-
const name = document.createElement('span');
|
|
7554
|
-
name.className = 'cxp-mcp-name';
|
|
7555
|
-
name.textContent = server.name || 'Unknown';
|
|
7556
|
-
const status = document.createElement('span');
|
|
7557
|
-
status.className = 'cxp-mcp-status';
|
|
7558
|
-
status.textContent = server.status || 'unknown';
|
|
7559
|
-
status.dataset.status = server.status || 'unknown';
|
|
7560
|
-
const trusted = document.createElement('span');
|
|
7561
|
-
trusted.className = 'cxp-mcp-status';
|
|
7562
|
-
trusted.dataset.status = 'running';
|
|
7563
|
-
trusted.textContent = 'always allowed';
|
|
7564
|
-
trusted.hidden = !server.alwaysAllowed;
|
|
7565
|
-
const refreshBtn = document.createElement('button');
|
|
7566
|
-
refreshBtn.className = 'cxp-btn cxp-btn-sm';
|
|
7567
|
-
refreshBtn.textContent = 'Refresh';
|
|
7568
|
-
refreshBtn.addEventListener('click', async () => {
|
|
7569
|
-
try {
|
|
7570
|
-
await requestForSettings('mcp_refresh', { name: server.name });
|
|
7571
|
-
refreshBtn.textContent = 'Done';
|
|
7572
|
-
setTimeout(() => { refreshBtn.textContent = 'Refresh'; }, 2000);
|
|
7573
|
-
} catch (err) {
|
|
7574
|
-
appendSettingsError(`MCP refresh failed: ${err.message}`);
|
|
7575
|
-
}
|
|
7576
|
-
});
|
|
7577
|
-
row.append(name, status, trusted, refreshBtn);
|
|
7578
|
-
if (server.alwaysAllowed) {
|
|
7579
|
-
const revokeBtn = document.createElement('button');
|
|
7580
|
-
revokeBtn.className = 'cxp-btn cxp-btn-sm';
|
|
7581
|
-
revokeBtn.textContent = 'Revoke';
|
|
7582
|
-
revokeBtn.title = 'Require approval for this MCP server again';
|
|
7583
|
-
revokeBtn.addEventListener('click', async () => {
|
|
7584
|
-
revokeBtn.disabled = true;
|
|
7585
|
-
try {
|
|
7586
|
-
await requestForSettings('mcp_approval_set', {
|
|
7587
|
-
name: server.name,
|
|
7588
|
-
approvalMode: 'prompt',
|
|
7589
|
-
});
|
|
7590
|
-
server.approvalMode = 'prompt';
|
|
7591
|
-
server.alwaysAllowed = false;
|
|
7592
|
-
trusted.hidden = true;
|
|
7593
|
-
revokeBtn.remove();
|
|
7594
|
-
} catch (err) {
|
|
7595
|
-
revokeBtn.disabled = false;
|
|
7596
|
-
appendSettingsError(`MCP approval update failed: ${err.message}`);
|
|
7597
|
-
}
|
|
7598
|
-
});
|
|
7599
|
-
row.appendChild(revokeBtn);
|
|
7600
|
-
}
|
|
7601
|
-
if (server.requiresOAuth) {
|
|
7602
|
-
const loginBtn = document.createElement('button');
|
|
7603
|
-
loginBtn.className = 'cxp-btn cxp-btn-sm cxp-mcp-oauth-btn';
|
|
7604
|
-
loginBtn.dataset.mcpServer = server.name;
|
|
7605
|
-
loginBtn.textContent = 'Authenticate';
|
|
7606
|
-
loginBtn.addEventListener('click', async () => {
|
|
7607
|
-
try {
|
|
7608
|
-
await startMcpOAuthLogin(settingsTab, server.name);
|
|
7609
|
-
} catch (err) {
|
|
7610
|
-
appendSettingsError(`MCP OAuth failed: ${err.message}`);
|
|
7611
|
-
}
|
|
7612
|
-
});
|
|
7613
|
-
row.appendChild(loginBtn);
|
|
7614
|
-
}
|
|
7615
|
-
mcpEl.appendChild(row);
|
|
7616
|
-
}
|
|
7617
|
-
}
|
|
7618
|
-
}
|
|
7619
|
-
|
|
7620
|
-
const permissionsEl = overlay.querySelector('#cxp-settings-permissions');
|
|
7621
|
-
if (permissionsEl) {
|
|
7622
|
-
const stored = permissionData.status === 'fulfilled'
|
|
7623
|
-
? permissionData.value?.permissions?.filesystem || {}
|
|
7624
|
-
: {};
|
|
7625
|
-
const roots = ['write', 'read'].flatMap((access) => (
|
|
7626
|
-
(Array.isArray(stored[access]) ? stored[access] : []).map((path) => ({ access, path }))
|
|
7627
|
-
));
|
|
7628
|
-
permissionsEl.innerHTML = '';
|
|
7629
|
-
permissionsEl.className = '';
|
|
7630
|
-
if (!roots.length) {
|
|
7631
|
-
const hint = document.createElement('div');
|
|
7632
|
-
hint.className = 'cxp-settings-hint';
|
|
7633
|
-
hint.textContent = 'None';
|
|
7634
|
-
permissionsEl.appendChild(hint);
|
|
7635
|
-
} else {
|
|
7636
|
-
for (const root of roots) {
|
|
7637
|
-
const row = document.createElement('div');
|
|
7638
|
-
row.className = 'cxp-mcp-row';
|
|
7639
|
-
const path = document.createElement('span');
|
|
7640
|
-
path.className = 'cxp-mcp-name cxp-permission-root-path';
|
|
7641
|
-
path.textContent = root.path;
|
|
7642
|
-
path.title = root.path;
|
|
7643
|
-
const access = document.createElement('span');
|
|
7644
|
-
access.className = 'cxp-mcp-status';
|
|
7645
|
-
access.textContent = root.access;
|
|
7646
|
-
const remove = document.createElement('button');
|
|
7647
|
-
remove.type = 'button';
|
|
7648
|
-
remove.className = 'cxp-bar-action';
|
|
7649
|
-
remove.innerHTML = ICON_X;
|
|
7650
|
-
remove.title = 'Revoke folder access';
|
|
7651
|
-
remove.setAttribute('aria-label', `Revoke access to ${root.path}`);
|
|
7652
|
-
remove.addEventListener('click', async () => {
|
|
7653
|
-
remove.disabled = true;
|
|
7654
|
-
try {
|
|
7655
|
-
await requestForSettings('permission_root_remove', root);
|
|
7656
|
-
row.remove();
|
|
7657
|
-
if (!permissionsEl.querySelector('.cxp-mcp-row')) {
|
|
7658
|
-
const hint = document.createElement('div');
|
|
7659
|
-
hint.className = 'cxp-settings-hint';
|
|
7660
|
-
hint.textContent = 'None';
|
|
7661
|
-
permissionsEl.appendChild(hint);
|
|
7662
|
-
}
|
|
7663
|
-
} catch (err) {
|
|
7664
|
-
remove.disabled = false;
|
|
7665
|
-
appendSettingsError(err.message || 'Could not revoke folder access');
|
|
7666
|
-
}
|
|
7667
|
-
});
|
|
7668
|
-
row.append(path, access, remove);
|
|
7669
|
-
permissionsEl.appendChild(row);
|
|
7670
|
-
}
|
|
7671
|
-
}
|
|
7672
|
-
}
|
|
7673
|
-
|
|
7674
|
-
// Config — editable fields
|
|
7675
|
-
const configEl = overlay.querySelector('#cxp-settings-config');
|
|
7676
|
-
if (configEl) {
|
|
7677
|
-
const rawCfg = configData.status === 'fulfilled' ? (configData.value?.config || configData.value || {}) : {};
|
|
7678
|
-
const { effective: cfg, layers } = normalizeConfigResult(rawCfg);
|
|
7679
|
-
if (configData.status === 'fulfilled' && configData.value?.sessionOverrides) {
|
|
7680
|
-
settingsTab.sessionConfigOverrides = Object.fromEntries(Object.entries(SESSION_CONFIG_FIELDS)
|
|
7681
|
-
.filter(([, field]) => Object.hasOwn(configData.value.sessionOverrides, field))
|
|
7682
|
-
.map(([key, field]) => [key, configData.value.sessionOverrides[field]]));
|
|
7683
|
-
}
|
|
7684
|
-
const requirements = requirementsData.status === 'fulfilled' ? requirementsData.value : {};
|
|
7685
|
-
|
|
7686
|
-
// Get dynamic model list (fall back to hardcoded defaults)
|
|
7687
|
-
let models;
|
|
7688
|
-
try { models = await requestModelList(settingsTab); } catch { models = null; }
|
|
7689
|
-
const modelList = mergeCodexModelList(models);
|
|
7690
|
-
|
|
7691
|
-
configEl.innerHTML = '';
|
|
7692
|
-
const fieldDefs = [
|
|
7693
|
-
{ key: 'model', label: 'Model', type: 'select', options: modelList.map(m => ({ value: getCodexModelName(m), label: m?.label || getCodexModelName(m) })) },
|
|
7694
|
-
{ key: 'review_model', label: 'Review Model', type: 'select', options: [{ value: '', label: '(none)' }, ...modelList.map(m => ({ value: getCodexModelName(m), label: m?.label || getCodexModelName(m) }))] },
|
|
7695
|
-
{ key: 'model_provider', label: 'Provider', type: 'text', placeholder: 'openai, ollama…' },
|
|
7696
|
-
{ key: 'approval_policy', label: 'Approval Policy', type: 'select', options: [
|
|
7697
|
-
{ value: 'never', label: 'Never (No prompts)' },
|
|
7698
|
-
{ value: 'untrusted', label: 'Untrusted' },
|
|
7699
|
-
{ value: 'on-request', label: 'On Request' },
|
|
7700
|
-
]},
|
|
7701
|
-
{ key: 'sandbox_mode', label: 'Sandbox Mode', type: 'select', options: [
|
|
7702
|
-
{ value: 'workspace-write', label: 'Workspace Write' },
|
|
7703
|
-
{ value: 'read-only', label: 'Read Only' },
|
|
7704
|
-
{ value: 'danger-full-access', label: 'Full Access (Dangerous)' },
|
|
7705
|
-
]},
|
|
7706
|
-
{ key: 'personality', label: 'Personality', type: 'select', options: [
|
|
7707
|
-
{ value: '', label: '(default)' },
|
|
7708
|
-
{ value: 'pragmatic', label: 'Pragmatic' },
|
|
7709
|
-
{ value: 'friendly', label: 'Friendly' },
|
|
7710
|
-
]},
|
|
7711
|
-
{ key: 'service_tier', label: 'Service Tier', type: 'select', options: [
|
|
7712
|
-
{ value: '', label: '(default)' },
|
|
7713
|
-
]},
|
|
7714
|
-
{ key: 'model_reasoning_effort', label: 'Reasoning Effort', type: 'select', options: [
|
|
7715
|
-
{ value: '', label: '(default)' },
|
|
7716
|
-
{ value: 'minimal', label: 'Minimal' },
|
|
7717
|
-
{ value: 'low', label: 'Low' },
|
|
7718
|
-
{ value: 'medium', label: 'Medium' },
|
|
7719
|
-
{ value: 'high', label: 'High' },
|
|
7720
|
-
{ value: 'xhigh', label: 'Extra High' },
|
|
7721
|
-
{ value: 'max', label: 'Maximum' },
|
|
7722
|
-
{ value: 'ultra', label: 'Ultra' },
|
|
7723
|
-
]},
|
|
7724
|
-
{ key: 'model_reasoning_summary', label: 'Reasoning Summary', type: 'select', options: [
|
|
7725
|
-
{ value: '', label: '(default)' },
|
|
7726
|
-
{ value: 'auto', label: 'Auto' },
|
|
7727
|
-
{ value: 'concise', label: 'Concise' },
|
|
7728
|
-
{ value: 'detailed', label: 'Detailed' },
|
|
7729
|
-
{ value: 'none', label: 'None' },
|
|
7730
|
-
]},
|
|
7731
|
-
{ key: 'model_verbosity', label: 'Response Verbosity', type: 'select', options: [
|
|
7732
|
-
{ value: '', label: '(default)' },
|
|
7733
|
-
{ value: 'low', label: 'Low' },
|
|
7734
|
-
{ value: 'medium', label: 'Medium' },
|
|
7735
|
-
{ value: 'high', label: 'High' },
|
|
7736
|
-
]},
|
|
7737
|
-
{ key: 'model_context_window', label: 'Context Window', type: 'number', placeholder: 'tokens' },
|
|
7738
|
-
{ key: 'model_auto_compact_token_limit', label: 'Auto-Compact', type: 'number', placeholder: 'token limit' },
|
|
7739
|
-
{ key: 'web_search', label: 'Web Search', type: 'select', options: [
|
|
7740
|
-
{ value: '', label: '(default)' },
|
|
7741
|
-
{ value: 'disabled', label: 'Disabled' },
|
|
7742
|
-
{ value: 'cached', label: 'Cached' },
|
|
7743
|
-
{ value: 'indexed', label: 'Indexed' },
|
|
7744
|
-
{ value: 'live', label: 'Live' },
|
|
7745
|
-
] },
|
|
7746
|
-
{ key: 'features.fast_mode', label: 'Enable Fast Toggle', type: 'checkbox' },
|
|
7747
|
-
{ key: 'features.personality', label: 'Enable Personality', type: 'checkbox' },
|
|
7748
|
-
{ key: 'features.apps', label: 'Enable Apps', type: 'checkbox' },
|
|
7749
|
-
{ key: 'features.unified_exec', label: 'Unified Exec', type: 'checkbox' },
|
|
7750
|
-
];
|
|
7751
|
-
const effortDefinition = fieldDefs.find((def) => def.key === 'model_reasoning_effort');
|
|
7752
|
-
const modelMetadata = selectedModelOption(modelList, cfg.model || settingsTab.model);
|
|
7753
|
-
if (modelMetadata) effortDefinition.options = normalizeReasoningEfforts(modelMetadata).map((option) => ({ value: option.id === 'off' ? '' : option.id, label: option.label || option.id }));
|
|
7754
|
-
fieldDefs.find((def) => def.key === 'service_tier').options.push(...modelServiceTiers(modelMetadata));
|
|
7755
|
-
const form = document.createElement('div');
|
|
7756
|
-
form.className = 'cxp-config-form';
|
|
7757
|
-
const scopeLabel = document.createElement('label');
|
|
7758
|
-
scopeLabel.className = 'cxp-config-row';
|
|
7759
|
-
scopeLabel.textContent = 'Apply to ';
|
|
7760
|
-
const scope = document.createElement('select');
|
|
7761
|
-
scope.className = 'cxp-config-input';
|
|
7762
|
-
const savedOption = document.createElement('option'); savedOption.value = 'saved'; savedOption.textContent = 'Saved defaults';
|
|
7763
|
-
const sessionOption = document.createElement('option'); sessionOption.value = 'session'; sessionOption.textContent = 'Current thread';
|
|
7764
|
-
sessionOption.disabled = !settingsTab.threadId || !capabilityAvailability(capabilities, 'thread_settings_update').supported;
|
|
7765
|
-
scope.append(savedOption, sessionOption); scopeLabel.append(scope); form.append(scopeLabel);
|
|
7766
|
-
const scopeHint = document.createElement('div'); scopeHint.className = 'cxp-settings-hint';
|
|
7767
|
-
scopeHint.textContent = 'Saved defaults affect future sessions. Current thread settings apply to subsequent turns.';
|
|
7768
|
-
form.append(scopeHint);
|
|
7769
|
-
const inputs = {};
|
|
7770
|
-
const restrictions = {};
|
|
7771
|
-
for (const def of fieldDefs) {
|
|
7772
|
-
const row = document.createElement('div');
|
|
7773
|
-
row.className = 'cxp-config-row';
|
|
7774
|
-
const label = document.createElement('label');
|
|
7775
|
-
label.className = 'cxp-config-label';
|
|
7776
|
-
label.textContent = def.label;
|
|
7777
|
-
row.appendChild(label);
|
|
7778
|
-
const val = configValue(cfg, def.key);
|
|
7779
|
-
const restriction = configRestriction(requirements, def.key);
|
|
7780
|
-
restrictions[def.key] = restriction;
|
|
7781
|
-
if (def.type === 'select') {
|
|
7782
|
-
const sel = document.createElement('select');
|
|
7783
|
-
sel.className = 'cxp-config-input';
|
|
7784
|
-
for (const opt of def.options) {
|
|
7785
|
-
const o = document.createElement('option');
|
|
7786
|
-
o.value = opt.value;
|
|
7787
|
-
o.textContent = opt.label;
|
|
7788
|
-
if (String(val ?? '') === opt.value) o.selected = true;
|
|
7789
|
-
if (restriction.allowed && opt.value && !restriction.allowed.some((allowed) => JSON.stringify(allowed) === JSON.stringify(opt.value))) o.disabled = true;
|
|
7790
|
-
sel.appendChild(o);
|
|
7791
|
-
}
|
|
7792
|
-
if (val != null && !def.options.some((opt) => opt.value === String(val))) {
|
|
7793
|
-
const unknown = document.createElement('option');
|
|
7794
|
-
unknown.value = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
|
7795
|
-
unknown.textContent = `${unknown.value} (configured)`; unknown.selected = true;
|
|
7796
|
-
sel.appendChild(unknown);
|
|
7797
|
-
sel.dataset.originalValue = JSON.stringify(val);
|
|
7798
|
-
}
|
|
7799
|
-
row.appendChild(sel);
|
|
7800
|
-
inputs[def.key] = sel;
|
|
7801
|
-
} else if (def.type === 'checkbox') {
|
|
7802
|
-
const cb = document.createElement('input');
|
|
7803
|
-
cb.type = 'checkbox';
|
|
7804
|
-
cb.className = 'cxp-config-checkbox';
|
|
7805
|
-
cb.checked = typeof restriction.fixed === 'boolean' ? restriction.fixed : !!val;
|
|
7806
|
-
row.appendChild(cb);
|
|
7807
|
-
inputs[def.key] = cb;
|
|
7808
|
-
} else {
|
|
7809
|
-
const inp = document.createElement('input');
|
|
7810
|
-
inp.type = def.type === 'number' ? 'number' : 'text';
|
|
7811
|
-
inp.className = 'cxp-config-input';
|
|
7812
|
-
inp.value = val != null ? val : '';
|
|
7813
|
-
inp.placeholder = def.placeholder || '';
|
|
7814
|
-
row.appendChild(inp);
|
|
7815
|
-
inputs[def.key] = inp;
|
|
7816
|
-
}
|
|
7817
|
-
// Config layer badge
|
|
7818
|
-
if (layers[def.key] || restriction.reason) {
|
|
7819
|
-
const badge = document.createElement('span');
|
|
7820
|
-
badge.className = 'cxp-config-layer';
|
|
7821
|
-
badge.textContent = `(${restriction.reason || layers[def.key]})`;
|
|
7822
|
-
row.appendChild(badge);
|
|
7823
|
-
}
|
|
7824
|
-
form.appendChild(row);
|
|
7525
|
+
const requestForSettings = (type, data = {}) => requestForCodexOwner(settingsTab, settingsOwner, type, data, t('codexSettings.changedConversation'));
|
|
7526
|
+
const capabilities = await getCodexCapabilities(settingsTab);
|
|
7527
|
+
if (settingsTab.closed || !isActiveTab(settingsTab)) return;
|
|
7528
|
+
const view = await mountCodexSettings({
|
|
7529
|
+
host: _getPanelEl(), tab: settingsTab,
|
|
7530
|
+
request: requestForSettings,
|
|
7531
|
+
capabilities,
|
|
7532
|
+
account: _codexAccounts.find(a => a.id === (settingsTab.accountId || 'default')),
|
|
7533
|
+
models: async () => (await requestModelList(settingsTab)) || [],
|
|
7534
|
+
normalizeConfig: normalizeConfigResult,
|
|
7535
|
+
manageAccount: () => { if (isActiveTab(settingsTab)) openAccountMenu({ manage: settingsTab.accountId || 'default' }); },
|
|
7536
|
+
authenticate: name => startMcpOAuthLogin(settingsTab, name),
|
|
7537
|
+
onSaved: ({ scope, patch, overrides }) => {
|
|
7538
|
+
settingsTab.sessionConfigOverrides = overrides;
|
|
7539
|
+
if (scope === 'session') {
|
|
7540
|
+
if (Object.hasOwn(patch, 'model')) settingsTab.model = patch.model;
|
|
7541
|
+
if (Object.hasOwn(patch, 'model_reasoning_effort')) settingsTab.effort = patch.model_reasoning_effort || 'off';
|
|
7542
|
+
if (Object.hasOwn(patch, 'approval_policy')) settingsTab.autoAccept = patch.approval_policy === 'never';
|
|
7543
|
+
syncToolbarState(); saveTabs();
|
|
7825
7544
|
}
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
else inputs[def.key].value = value != null ? (typeof value === 'object' ? JSON.stringify(value) : String(value)) : '';
|
|
7835
|
-
}
|
|
7836
|
-
saveBtn.textContent = scope.value === 'session' ? 'Apply to Current Thread' : 'Save Defaults';
|
|
7837
|
-
};
|
|
7838
|
-
scope.addEventListener('change', refreshScope);
|
|
7839
|
-
refreshScope();
|
|
7840
|
-
saveBtn.addEventListener('click', async () => {
|
|
7841
|
-
saveBtn.disabled = true;
|
|
7842
|
-
saveBtn.textContent = 'Saving…';
|
|
7843
|
-
try {
|
|
7844
|
-
const currentConfig = scope.value === 'session' ? { ...cfg, ...(settingsTab.sessionConfigOverrides || {}) } : cfg;
|
|
7845
|
-
const newCfg = configChanges(fieldDefs, inputs, currentConfig);
|
|
7846
|
-
if (scope.value === 'session') {
|
|
7847
|
-
if (settingsTab.running) throw new Error('Wait for the active turn to finish before applying thread settings.');
|
|
7848
|
-
await requestForSettings('thread_settings_update', { threadId: settingsTab.threadId, ...sessionSettings(newCfg) });
|
|
7849
|
-
settingsTab.sessionConfigOverrides = { ...(settingsTab.sessionConfigOverrides || {}), ...newCfg };
|
|
7850
|
-
if (Object.hasOwn(newCfg, 'model')) settingsTab.model = newCfg.model;
|
|
7851
|
-
if (Object.hasOwn(newCfg, 'model_reasoning_effort')) settingsTab.effort = newCfg.model_reasoning_effort || 'off';
|
|
7852
|
-
if (Object.hasOwn(newCfg, 'approval_policy')) settingsTab.autoAccept = newCfg.approval_policy === 'never';
|
|
7853
|
-
syncToolbarState(); saveTabs();
|
|
7854
|
-
} else if (Object.keys(newCfg).length) {
|
|
7855
|
-
await requestForSettings('config_write', { config: newCfg });
|
|
7856
|
-
Object.assign(cfg, newCfg);
|
|
7857
|
-
}
|
|
7858
|
-
invalidateCodexFeatureCaches(settingsTab);
|
|
7859
|
-
saveBtn.textContent = 'Saved!';
|
|
7860
|
-
refreshModelCatalog(settingsTab);
|
|
7861
|
-
setTimeout(() => { refreshScope(); saveBtn.disabled = false; }, 1500);
|
|
7862
|
-
} catch (err) {
|
|
7863
|
-
saveBtn.textContent = 'Save Failed';
|
|
7864
|
-
appendSettingsError(`Config save failed: ${err.message}`);
|
|
7865
|
-
setTimeout(() => { refreshScope(); saveBtn.disabled = false; }, 1500);
|
|
7866
|
-
}
|
|
7867
|
-
});
|
|
7868
|
-
form.appendChild(saveBtn);
|
|
7869
|
-
configEl.appendChild(form);
|
|
7870
|
-
}
|
|
7871
|
-
// Opened for one section (the Context settings popover's "Manage"): bring it into view.
|
|
7872
|
-
if (section) overlay.querySelector(`#cxp-settings-${section}`)?.closest('.cxp-settings-section')?.scrollIntoView({ block: 'start' });
|
|
7873
|
-
} catch (error) { appendSettingsError(error.message || 'Could not load Codex settings.'); }
|
|
7545
|
+
invalidateCodexFeatureCaches(settingsTab); refreshModelCatalog(settingsTab);
|
|
7546
|
+
clearCodexConfigWarnings({ obsoleteOnly: true });
|
|
7547
|
+
},
|
|
7548
|
+
});
|
|
7549
|
+
if (section && view) {
|
|
7550
|
+
const target = section === 'mcp' ? view.sections.tools : view.sections[section];
|
|
7551
|
+
target?.scrollIntoView({ block: 'start' });
|
|
7552
|
+
}
|
|
7874
7553
|
}
|
|
7875
7554
|
|
|
7876
7555
|
export function startCompaction() {
|
|
@@ -7929,6 +7608,7 @@ export function initContextBridge() {
|
|
|
7929
7608
|
withRequestTab: withTab,
|
|
7930
7609
|
sendSocket,
|
|
7931
7610
|
emit,
|
|
7611
|
+
fileLinkText,
|
|
7932
7612
|
activeTab,
|
|
7933
7613
|
isActiveTab,
|
|
7934
7614
|
dispatchPrompt,
|