synabun 2.1.0 → 2.2.1
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 +139 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +504 -39
- package/neural-interface/i18n/pt-BR.json +504 -39
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/codex.js +6 -1
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +211 -50
- package/neural-interface/lib/assistant-envelope.js +153 -1
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +40 -7
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +423 -23
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +165 -22
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +80 -6
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +15 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
- package/neural-interface/public/shared/assistant/asst-render.js +112 -18
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
- package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +1 -0
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +21 -4
- package/neural-interface/public/shared/loading-teardown.js +34 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/storage-retry.js +47 -0
- package/neural-interface/public/shared/storage.js +25 -4
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +362 -35
- package/neural-interface/public/shared/synabun-mascot.js +27 -8
- package/neural-interface/public/shared/synabun-ticker.js +147 -12
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +374 -134
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-loading.js +5 -4
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +731 -466
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -208,6 +208,7 @@ export function normalizeSynaBunChoiceElicitation(params = {}) {
|
|
|
208
208
|
if (options.length < 2) return null;
|
|
209
209
|
return {
|
|
210
210
|
id,
|
|
211
|
+
multiSelect: question.multiSelect === true,
|
|
211
212
|
header: String(question?.header || id),
|
|
212
213
|
question: String(question?.question || ''),
|
|
213
214
|
options,
|
|
@@ -226,16 +227,16 @@ export function buildSynaBunChoiceContent(questions = [], answers = {}) {
|
|
|
226
227
|
const content = {};
|
|
227
228
|
for (const question of questions) {
|
|
228
229
|
const id = question?.id;
|
|
229
|
-
const
|
|
230
|
-
if (!id || !
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
content[
|
|
230
|
+
const selected = id ? answers[id]?.answers || [] : [];
|
|
231
|
+
if (!id || !selected.length) continue;
|
|
232
|
+
const values = selected.map((answer) => {
|
|
233
|
+
const option = (question.options || []).find((candidate) => candidate.label === answer);
|
|
234
|
+
if (option) return option.value || option.label;
|
|
235
|
+
const meta = question._synabunMcp || {};
|
|
236
|
+
content[meta.otherField || `${id}__other`] = answer;
|
|
237
|
+
return meta.otherValue || SYNABUN_OTHER_VALUE;
|
|
238
|
+
});
|
|
239
|
+
content[id] = question.multiSelect ? values : values[0];
|
|
239
240
|
}
|
|
240
241
|
return content;
|
|
241
242
|
}
|
|
@@ -19,6 +19,7 @@ import { OUTPUT_LIMITS, boundedText, jsonAnswer, prettyJsonFences, readableStruc
|
|
|
19
19
|
import { renderReadableOutput, renderReadableBlock, cleanOutputBlocks, hydrateOutputControls, observeOutputBlocks, renderDiffView, restoreDiffViews } from './cdx-output.js';
|
|
20
20
|
import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget, readFileLink } from '../file-links.js';
|
|
21
21
|
import { sanitizeHtmlString } from '../assistant/asst-sanitize.js';
|
|
22
|
+
import { createTextCache } from '../text-cache.js';
|
|
22
23
|
export { setCardExpanded } from './cdx-cards.js';
|
|
23
24
|
|
|
24
25
|
// ── Shared context (set by cdx-tabs via setRenderContext) ──
|
|
@@ -105,8 +106,9 @@ export function esc(s) {
|
|
|
105
106
|
// `<img onerror>` in a reply would run in SynaBun's origin with no click. Every
|
|
106
107
|
// Markdown sink of the panel takes md()'s output, so it goes through the
|
|
107
108
|
// allowlist sanitiser the Assistant, Claude and OpenCode panels use, here, once.
|
|
108
|
-
const _mdCache =
|
|
109
|
-
const
|
|
109
|
+
const _mdCache = createTextCache();
|
|
110
|
+
export const markdownCacheStats = () => _mdCache.stats();
|
|
111
|
+
export const clearMarkdownCache = () => _mdCache.clear();
|
|
110
112
|
let _mdRenderer = null;
|
|
111
113
|
let _mdParser;
|
|
112
114
|
function markdownFallback(text) {
|
|
@@ -130,7 +132,7 @@ export function md(text) {
|
|
|
130
132
|
_mdRenderer = renderer;
|
|
131
133
|
_mdParser = parser;
|
|
132
134
|
}
|
|
133
|
-
const key = text || '';
|
|
135
|
+
const key = String(text || '');
|
|
134
136
|
if (key.length > OUTPUT_LIMITS.input) return `<pre class="cxp-card-pre cxp-output-raw"><code>${esc(boundedText(key))}</code></pre>`;
|
|
135
137
|
const cached = _mdCache.get(key);
|
|
136
138
|
if (cached !== undefined) return cached;
|
|
@@ -144,10 +146,6 @@ export function md(text) {
|
|
|
144
146
|
// A page with no DOM to sanitise in, or a sanitiser that fails, gets escaped text: never raw HTML.
|
|
145
147
|
try { html = sanitizeHtmlString(html); }
|
|
146
148
|
catch { html = markdownFallback(key); }
|
|
147
|
-
if (_mdCache.size >= _MD_CACHE_LIMIT) {
|
|
148
|
-
const firstKey = _mdCache.keys().next().value;
|
|
149
|
-
if (firstKey !== undefined) _mdCache.delete(firstKey);
|
|
150
|
-
}
|
|
151
149
|
_mdCache.set(key, html);
|
|
152
150
|
return html;
|
|
153
151
|
}
|
|
@@ -3264,7 +3262,10 @@ export function resolveHistoryRenderSource(thread, fallbackItems = null, storedS
|
|
|
3264
3262
|
// `authoritative`: the thread's turns are its whole history (a revert reply),
|
|
3265
3263
|
// so the saved rendering is never used in their place, even when none remain.
|
|
3266
3264
|
export function renderHistory(thread, fallbackItems = null, { authoritative = false } = {}) {
|
|
3267
|
-
|
|
3265
|
+
// A save still pending for what is on screen is written before the transcript is rebuilt; with nothing
|
|
3266
|
+
// pending there is nothing to keep (no forced serialisation of the whole transcript on every open).
|
|
3267
|
+
const pendingTab = _ctx.boundTab;
|
|
3268
|
+
if (pendingTab?.snapshotTimer || pendingTab?.snapshotIdleHandle) _ctx.flushThreadSnapshotSave(pendingTab);
|
|
3268
3269
|
const preservedTokenUsage = thread?.id && _ctx.threadId && thread.id === _ctx.threadId
|
|
3269
3270
|
? _ctx.threadTokenUsage
|
|
3270
3271
|
: null;
|
|
@@ -3341,7 +3342,7 @@ export function renderHistory(thread, fallbackItems = null, { authoritative = fa
|
|
|
3341
3342
|
});
|
|
3342
3343
|
_ctx.hideEmpty();
|
|
3343
3344
|
_ctx.scrollEnd(true);
|
|
3344
|
-
_ctx.
|
|
3345
|
+
_ctx.scheduleThreadSnapshotSave(_ctx.boundTab);
|
|
3345
3346
|
renderPostPlanActions(_ctx.boundTab);
|
|
3346
3347
|
renderPostCompactionActions(_ctx.boundTab);
|
|
3347
3348
|
}
|
|
@@ -417,7 +417,7 @@ export function renderUserInputRequest(requestId, params, protocol = {}) {
|
|
|
417
417
|
<span class="cxp-ask-option-desc">${esc(optionDesc)}</span>
|
|
418
418
|
`;
|
|
419
419
|
const retainedValue = answers[answerKey]?.answers?.[0];
|
|
420
|
-
if (!isOtherOption &&
|
|
420
|
+
if (!isOtherOption && (answers[answerKey]?.answers || []).includes(optionLabel)) btn.classList.add('selected');
|
|
421
421
|
btn.addEventListener('click', async () => {
|
|
422
422
|
if (isChangelogAsk && /edit first/i.test(optionLabel)) {
|
|
423
423
|
const allButtons = Array.from(optionsEl.querySelectorAll('.cxp-ask-option'));
|
|
@@ -434,6 +434,16 @@ export function renderUserInputRequest(requestId, params, protocol = {}) {
|
|
|
434
434
|
if (!opened) _ctx.restoreChangelogButtons(allButtons);
|
|
435
435
|
return;
|
|
436
436
|
}
|
|
437
|
+
if (question.multiSelect && !isOtherOption) {
|
|
438
|
+
otherWrap.hidden = true;
|
|
439
|
+
const selected = new Set(answers[answerKey]?.answers || []);
|
|
440
|
+
if (selected.has(optionLabel)) selected.delete(optionLabel); else selected.add(optionLabel);
|
|
441
|
+
const labels = new Set(explicitOptions.map((o) => typeof o === 'string' ? o : o.label));
|
|
442
|
+
const values = [...selected].filter((label) => labels.has(label));
|
|
443
|
+
if (values.length) answers[answerKey] = { answers: values }; else delete answers[answerKey];
|
|
444
|
+
optionsEl.querySelectorAll('.cxp-ask-option').forEach((node) => node.classList.toggle('selected', values.includes(node.querySelector('.cxp-ask-option-label')?.textContent)));
|
|
445
|
+
persistEditingDraft(); syncSubmit(); return;
|
|
446
|
+
}
|
|
437
447
|
optionsEl.querySelectorAll('.cxp-ask-option').forEach((node) => node.classList.remove('selected'));
|
|
438
448
|
btn.classList.add('selected');
|
|
439
449
|
if (isOtherOption) {
|
|
@@ -125,6 +125,10 @@ export function injectStyles() {
|
|
|
125
125
|
opacity: 1;
|
|
126
126
|
overflow: visible;
|
|
127
127
|
}
|
|
128
|
+
/* Windows: the shell is 92% opaque, so the blur behind it barely shows, yet Chromium recomputes it on every
|
|
129
|
+
repaint inside the panel (a transcript rendering, a stream). There it goes; the colour stays. */
|
|
130
|
+
html.platform-win32 .codex-panel,
|
|
131
|
+
html.desktop-titlebar--win32 .codex-panel { backdrop-filter: none; -webkit-backdrop-filter: none; }
|
|
128
132
|
/* A floating terminal is promoted above the panel when focused. Raise the
|
|
129
133
|
whole stacking context while a footer menu is open so it stays visible. */
|
|
130
134
|
.codex-panel:has(.cxp-dropdown.open) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { answerWorkerQuestion } from '../worker-questions.js';
|
|
1
2
|
import { cxpText, cxpSessionLabel } from './cdx-copy.js';
|
|
3
|
+
import { codexWantsRolloutPreview, rolloutPreviewApplies } from './cdx-preview.js';
|
|
2
4
|
import { mountCodexSettings } from './cdx-settings.js';
|
|
3
5
|
import { t } from '../i18n.js';
|
|
4
6
|
import { getCliState, isCliInstalling } from '../cli-status.js';
|
|
@@ -14,8 +16,8 @@ import { accountName, accountSummary, accountSwitchPlan, accountText, serverAcco
|
|
|
14
16
|
import { mountAccountMenu } from '../account-menu.js';
|
|
15
17
|
import '../account-i18n.js';
|
|
16
18
|
import { restoreMcpAppCards, teardownMcpApps, retireMcpAppNode, stripMcpAppRuntime } from './cdx-mcp-app-host.js';
|
|
17
|
-
import { renderTurnDiff, completeTurnOutputs } from './cdx-render.js';
|
|
18
|
-
import { hydrateOutputControls } from './cdx-output.js';
|
|
19
|
+
import { renderTurnDiff, completeTurnOutputs, clearMarkdownCache } from './cdx-render.js';
|
|
20
|
+
import { hydrateOutputControls, disposeOutputBlocks } from './cdx-output.js';
|
|
19
21
|
import { sanitizeStoredHtml } from '../assistant/asst-sanitize.js';
|
|
20
22
|
import {
|
|
21
23
|
beginPlanTurn, capturePlanItem, finishPlanTurn, getPlanMarkdown, syncPlanMirrors, recordPlanTurn,
|
|
@@ -205,6 +207,13 @@ function autosizeInput() { _onAutosizeInput?.(); }
|
|
|
205
207
|
function emit(...args) { _onEmit?.(...args); }
|
|
206
208
|
|
|
207
209
|
function sendSocket(msg) {
|
|
210
|
+
if (_boundTab?.automationRunId && msg?.type === 'server_request_response') {
|
|
211
|
+
const tab = _boundTab;
|
|
212
|
+
answerWorkerQuestion(tab.automationRunId, msg.requestId, { behavior: msg.error || ['cancel', 'decline'].includes(msg.result?.action) || ['cancel', 'decline'].includes(msg.result?.decision) ? 'deny' : 'allow', result: msg.result, error: msg.error })
|
|
213
|
+
.then((result) => withTab(tab, () => handleServerRequestResponseResult({ responseToken: msg.responseToken, ok: true, status: result.stale ? 'stale' : 'answered' })),
|
|
214
|
+
(error) => withTab(tab, () => handleServerRequestResponseResult({ responseToken: msg.responseToken, ok: false, status: tab._workerQuestions?.has(String(msg.requestId)) ? 'delivery_failed' : 'stale', retryable: true, error: error.message })));
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
208
217
|
const packet = msg && _boundTab?.id
|
|
209
218
|
? {
|
|
210
219
|
...msg,
|
|
@@ -277,6 +286,16 @@ function closeAutomationHistorySocket(tab) {
|
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
288
|
|
|
289
|
+
// The rollout read before Codex's app-server is up (server.js sendEarlyCodexRolloutPreview): shown only for
|
|
290
|
+
// the thread this tab shows, and only while nothing else (a stored snapshot) is on screen. The real history
|
|
291
|
+
// replaces it.
|
|
292
|
+
function paintRolloutPreview(tab, msg) {
|
|
293
|
+
const shownThread = String(_boundTab?.threadId || _threadId || '');
|
|
294
|
+
if (!rolloutPreviewApplies(msg, shownThread, _transcriptSourceType)) return;
|
|
295
|
+
renderHistory(msg.thread, msg.fallbackItems || null);
|
|
296
|
+
placeWorkerEnd(tab);
|
|
297
|
+
}
|
|
298
|
+
|
|
280
299
|
function buildWsCallbacks(tab, options = {}) {
|
|
281
300
|
return {
|
|
282
301
|
onConnecting() {
|
|
@@ -309,6 +328,8 @@ function buildWsCallbacks(tab, options = {}) {
|
|
|
309
328
|
userVerification: true,
|
|
310
329
|
windowId: windowId,
|
|
311
330
|
threadId: _threadId || null,
|
|
331
|
+
// The early rollout preview only for the tab on screen (server.js sendEarlyCodexRolloutPreview).
|
|
332
|
+
preview: codexWantsRolloutPreview(tab, _boundTab, _threadId),
|
|
312
333
|
cwd: _project || null,
|
|
313
334
|
accountId: _accountId || 'default',
|
|
314
335
|
...codexRuntimeSelection(tab),
|
|
@@ -450,6 +471,7 @@ function buildWsCallbacks(tab, options = {}) {
|
|
|
450
471
|
// which rebuilds the transcript: they go back onto it afterwards.
|
|
451
472
|
const heldRequests = heldServerRequests();
|
|
452
473
|
if (msg.thread) {
|
|
474
|
+
if (msg.preview) { paintRolloutPreview(tab, msg); return; }
|
|
453
475
|
if (_boundTab) {
|
|
454
476
|
_boundTab.expectedThreadId = null;
|
|
455
477
|
_boundTab.expectedThreadRequestId = null;
|
|
@@ -757,6 +779,7 @@ let _projectThreads = parseStoredJson(STOR.projectThreads, {});
|
|
|
757
779
|
// async; getThreadSnapshot returns null pre-hydration and renderHistory falls
|
|
758
780
|
// back to SDK/JSONL items.
|
|
759
781
|
let _threadSnapshots = {};
|
|
782
|
+
let _threadSnapshotsHydrated = false;
|
|
760
783
|
const _threadSnapshotsReady = fetchBlobNamespace('codex-snapshots')
|
|
761
784
|
.then((map) => {
|
|
762
785
|
const valid = {};
|
|
@@ -772,7 +795,8 @@ const _threadSnapshotsReady = fetchBlobNamespace('codex-snapshots')
|
|
|
772
795
|
_threadSnapshots = { ...valid, ..._threadSnapshots };
|
|
773
796
|
try { if (storage.getItem(STOR.threadSnapshots)) storage.removeItem(STOR.threadSnapshots); } catch {}
|
|
774
797
|
})
|
|
775
|
-
.catch(() => {})
|
|
798
|
+
.catch(() => {})
|
|
799
|
+
.finally(() => { _threadSnapshotsHydrated = true; });
|
|
776
800
|
let _tabs = [];
|
|
777
801
|
let _activeTabIdx = -1;
|
|
778
802
|
let _boundTab = null;
|
|
@@ -2631,6 +2655,9 @@ function disposeDiscardedTab(tab) {
|
|
|
2631
2655
|
clearTimeout(tab.snapshotTimer);
|
|
2632
2656
|
tab.snapshotTimer = null;
|
|
2633
2657
|
}
|
|
2658
|
+
tab.scrollController?.destroy();
|
|
2659
|
+
tab.scrollController = null;
|
|
2660
|
+
disposeOutputBlocks(tab.messagesEl);
|
|
2634
2661
|
tab.messagesEl?.remove();
|
|
2635
2662
|
tab.pillEl?.remove();
|
|
2636
2663
|
}
|
|
@@ -2719,9 +2746,37 @@ export function renderProjects() {
|
|
|
2719
2746
|
ddPopulate(projectDropdown, projectItemsForValue(_project), _project);
|
|
2720
2747
|
}
|
|
2721
2748
|
|
|
2749
|
+
function paintInitialThreadSnapshot(tab) {
|
|
2750
|
+
if (!tab || tab.closed || !tab.threadId || tab.transcriptSourceType || tab.automationRunId) return false;
|
|
2751
|
+
const expectedThreadId = String(tab.threadId);
|
|
2752
|
+
let painted = false;
|
|
2753
|
+
withTab(tab, () => {
|
|
2754
|
+
const snap = getThreadSnapshot(expectedThreadId);
|
|
2755
|
+
if (!snap) return;
|
|
2756
|
+
painted = renderStoredTranscript(snap, 'Continue this Codex thread or start a fresh one.');
|
|
2757
|
+
if (painted) {
|
|
2758
|
+
updateThreadTokenUsage(tab.threadTokenUsage || snap.threadTokenUsage || null);
|
|
2759
|
+
renderPostPlanActions(tab);
|
|
2760
|
+
renderPostCompactionActions(tab);
|
|
2761
|
+
placeWorkerEnd(tab);
|
|
2762
|
+
}
|
|
2763
|
+
});
|
|
2764
|
+
if (!painted && !_threadSnapshotsHydrated && !tab._pendingSnapshotPaint) {
|
|
2765
|
+
tab._pendingSnapshotPaint = true;
|
|
2766
|
+
_threadSnapshotsReady.finally(() => {
|
|
2767
|
+
tab._pendingSnapshotPaint = false;
|
|
2768
|
+
if (!tab.closed && tab.threadId === expectedThreadId && !tab.transcriptSourceType) {
|
|
2769
|
+
paintInitialThreadSnapshot(tab);
|
|
2770
|
+
}
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
return painted;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2722
2776
|
export function updateActiveTabView() {
|
|
2723
2777
|
const tab = activeTab();
|
|
2724
2778
|
if (!_getPanelEl() || !tab) return;
|
|
2779
|
+
paintInitialThreadSnapshot(tab);
|
|
2725
2780
|
bindTabState(tab);
|
|
2726
2781
|
_onActiveTabChanged?.(tab);
|
|
2727
2782
|
_tabs.forEach((entry) => {
|
|
@@ -2945,6 +3000,8 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
|
|
|
2945
3000
|
tab.closed = true;
|
|
2946
3001
|
tab.titleRequestController?.abort();
|
|
2947
3002
|
tab.titleRequestController = null;
|
|
3003
|
+
tab.authView?.dispose();
|
|
3004
|
+
tab.terminalAuthView?.dispose();
|
|
2948
3005
|
if (tab.titleReassertTimer) clearTimeout(tab.titleReassertTimer);
|
|
2949
3006
|
flushThreadSnapshotSave(tab);
|
|
2950
3007
|
withTab(tab, () => {
|
|
@@ -2967,11 +3024,16 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
|
|
|
2967
3024
|
}
|
|
2968
3025
|
tab.scrollController?.destroy();
|
|
2969
3026
|
tab.scrollController = null;
|
|
3027
|
+
disposeOutputBlocks(tab.messagesEl);
|
|
2970
3028
|
if (tab.messagesEl) retireMcpAppNode(tab.messagesEl, () => tab.messagesEl.remove());
|
|
2971
3029
|
tab.pillEl?.remove();
|
|
2972
3030
|
_tabs.splice(idx, 1);
|
|
2973
3031
|
if (!_tabs.length) {
|
|
2974
3032
|
_activeTabIdx = -1;
|
|
3033
|
+
// The module binding otherwise keeps the last closed transcript, its item
|
|
3034
|
+
// and request maps, and every closure on their DOM alive indefinitely.
|
|
3035
|
+
bindTabState(null);
|
|
3036
|
+
clearMarkdownCache();
|
|
2975
3037
|
saveTabs();
|
|
2976
3038
|
renderPills();
|
|
2977
3039
|
_setVisible(false);
|
|
@@ -3165,7 +3227,13 @@ export function restoreTabs() {
|
|
|
3165
3227
|
if (tab && !tab.automationRunId && (saved.running || saved.startingThread || saved.needsYou)) wereWorking.push(tab);
|
|
3166
3228
|
}
|
|
3167
3229
|
if (!_tabs.length) throw new Error('No restorable Codex tabs');
|
|
3168
|
-
|
|
3230
|
+
const pendingTabId = globalThis.__synabunPendingTab?.provider === 'codex'
|
|
3231
|
+
? String(globalThis.__synabunPendingTab.tabId || '')
|
|
3232
|
+
: '';
|
|
3233
|
+
const pendingIdx = pendingTabId ? _tabs.findIndex((entry) => String(entry.id) === pendingTabId) : -1;
|
|
3234
|
+
_activeTabIdx = pendingIdx >= 0
|
|
3235
|
+
? pendingIdx
|
|
3236
|
+
: Math.min(Math.max(Number(data.activeIdx) || 0, 0), _tabs.length - 1);
|
|
3169
3237
|
pruneDiscardableTabs({ keepTab: activeTab(), persist: false });
|
|
3170
3238
|
if (!_tabs.length) throw new Error('No restorable Codex tabs');
|
|
3171
3239
|
updateActiveTabView();
|
|
@@ -4597,6 +4665,7 @@ export function promptNameNewSession() {
|
|
|
4597
4665
|
try {
|
|
4598
4666
|
const statusResponse = await fetch(`/api/git/status?path=${encodeURIComponent(chosenProject)}`);
|
|
4599
4667
|
const status = statusResponse.ok ? await statusResponse.json() : null;
|
|
4668
|
+
if (!status?.ok || !status.isGit || status.partial || status.stale || status.timedOut) throw new Error(status?.error || 'Git status is incomplete. Refresh before switching branches.');
|
|
4600
4669
|
const shouldCheckout = !status?.changes?.length || window.confirm(
|
|
4601
4670
|
`Switch from ${branchOriginal} to ${chosenBranch} with ${status.changes.length} uncommitted change${status.changes.length === 1 ? '' : 's'}? Git will preserve them when possible.`,
|
|
4602
4671
|
);
|
|
@@ -6551,8 +6620,9 @@ function setModelForTab(tab, model) {
|
|
|
6551
6620
|
function setEffortForTab(tab, effort) {
|
|
6552
6621
|
if (!tab) return false;
|
|
6553
6622
|
const normalized = String(effort || '').trim().toLowerCase();
|
|
6554
|
-
const nextEffort = normalized === 'default' || normalized === 'none' ? 'off' : normalized;
|
|
6555
6623
|
const supported = effortOptionsForTab(tab);
|
|
6624
|
+
// 'none' is a real level on models that advertise it; elsewhere it means the default.
|
|
6625
|
+
const nextEffort = normalized === 'default' || (normalized === 'none' && !supported.some((option) => option.id === 'none')) ? 'off' : normalized;
|
|
6556
6626
|
if (!supported.some((option) => option.id === nextEffort)) {
|
|
6557
6627
|
withTab(tab, () => appendSystem(`Unsupported reasoning effort for ${tab.model || 'this model'}: ${effort}. Use ${supported.map((option) => option.id).join(', ')}.`, 'error'));
|
|
6558
6628
|
return false;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — one CLI's install state, for a side panel (no DOM)
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What a panel needs to show for its CLI and to act on it: the latest answer of
|
|
6
|
+
// /api/system/tool-versions (cli-status.js), an install this page started or
|
|
7
|
+
// follows (POST /api/cli/install/:tool, or one another window started), a
|
|
8
|
+
// "failed to start" a session reported, Add to PATH, Re-check. One controller
|
|
9
|
+
// per panel; `onChange(view)` hears every change, `view()` is
|
|
10
|
+
// cli-install-view.js cliBannerView() plus `checking` (a Re-check is under way)
|
|
11
|
+
// and `nudging` (a held-back send asked for attention a moment ago).
|
|
12
|
+
//
|
|
13
|
+
// ui-cli-banner.js paints it as the Claude, Codex and OpenCode panels' banner;
|
|
14
|
+
// the Antigravity panel paints it as its setup card (agy/agy-setup.js).
|
|
15
|
+
|
|
16
|
+
import { subscribeCliStatus, recheckCliStatus, getCliDocUrl, getCliInstallCommand, getCliLabel, installCli, followCliInstall, getCliInstallJobs, addCliToPath } from './cli-status.js';
|
|
17
|
+
import { cliBannerView, cliInstallText, cliStateOf, reduceInstallJob, INSTALLABLE_CLIS } from './cli-install-view.js';
|
|
18
|
+
|
|
19
|
+
/** How long the note about where an install put the CLI stays, when it has one. */
|
|
20
|
+
export const DONE_NOTICE_MS = 12000;
|
|
21
|
+
export const NUDGE_MS = 700;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {object} o
|
|
25
|
+
* @param {string} o.toolKey 'claude-code' | 'codex' | 'opencode' | 'antigravity'
|
|
26
|
+
* @param {(view: object) => void} [o.onChange]
|
|
27
|
+
* @param {number} [o.doneNoticeMs]
|
|
28
|
+
* @param {number} [o.nudgeMs]
|
|
29
|
+
*/
|
|
30
|
+
export function createCliInstallController({ toolKey, onChange = () => {}, doneNoticeMs = DONE_NOTICE_MS, nudgeMs = NUDGE_MS } = {}) {
|
|
31
|
+
let info = null; // this CLI in the latest answer of /api/system/tool-versions (null: none yet)
|
|
32
|
+
let forced = null; // { reason }: a session could not start it, and the server was not asked again yet
|
|
33
|
+
let job = null; // the install under way or just ended (cli-install-view.js reduceInstallJob)
|
|
34
|
+
let installing = false;
|
|
35
|
+
let addingPath = false;
|
|
36
|
+
let pathError = '';
|
|
37
|
+
let checking = false;
|
|
38
|
+
let looked = false; // asked whether the server is already installing it since it went missing
|
|
39
|
+
let doneTimer = null;
|
|
40
|
+
let nudgeTimer = null;
|
|
41
|
+
let destroyed = false;
|
|
42
|
+
|
|
43
|
+
const view = () => ({
|
|
44
|
+
...cliBannerView({
|
|
45
|
+
toolKey,
|
|
46
|
+
label: getCliLabel(toolKey),
|
|
47
|
+
info,
|
|
48
|
+
forced,
|
|
49
|
+
job,
|
|
50
|
+
addingPath, pathError,
|
|
51
|
+
guideUrl: getCliDocUrl(toolKey),
|
|
52
|
+
installCommand: getCliInstallCommand(toolKey),
|
|
53
|
+
}, { t: cliInstallText }),
|
|
54
|
+
checking,
|
|
55
|
+
nudging: !!nudgeTimer,
|
|
56
|
+
job,
|
|
57
|
+
info,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const changed = () => {
|
|
61
|
+
if (destroyed) return;
|
|
62
|
+
try { onChange(view()); } catch (error) { console.error('[cli-install] onChange:', error); }
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function clearDone() {
|
|
66
|
+
clearTimeout(doneTimer);
|
|
67
|
+
doneTimer = null;
|
|
68
|
+
if (job?.phase === 'done') job = null;
|
|
69
|
+
changed();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function apply(event) {
|
|
73
|
+
if (!job && event?.type !== 'start') job = reduceInstallJob(null, { type: 'start' });
|
|
74
|
+
job = reduceInstallJob(job, event);
|
|
75
|
+
if (event?.type === 'done' && event.ok && event.status && typeof event.status === 'object') {
|
|
76
|
+
info = event.status;
|
|
77
|
+
forced = null;
|
|
78
|
+
}
|
|
79
|
+
changed();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function settle(final) {
|
|
83
|
+
if (job?.phase === 'running' && final) job = reduceInstallJob(job, final);
|
|
84
|
+
if (job?.phase === 'done') {
|
|
85
|
+
clearTimeout(doneTimer);
|
|
86
|
+
const where = job.path;
|
|
87
|
+
if (where && (where.onPath === false || where.added === true)) doneTimer = setTimeout(clearDone, doneNoticeMs);
|
|
88
|
+
else job = null;
|
|
89
|
+
}
|
|
90
|
+
changed();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function install() {
|
|
94
|
+
if (installing || !INSTALLABLE_CLIS.includes(toolKey)) return null;
|
|
95
|
+
installing = true;
|
|
96
|
+
clearTimeout(doneTimer);
|
|
97
|
+
job = reduceInstallJob(null, { type: 'start' });
|
|
98
|
+
changed();
|
|
99
|
+
let final = null;
|
|
100
|
+
try {
|
|
101
|
+
final = await installCli(toolKey, { onEvent: apply });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
final = { type: 'done', ok: false, code: 'internal', error: error?.message || null };
|
|
104
|
+
} finally {
|
|
105
|
+
installing = false;
|
|
106
|
+
settle(final);
|
|
107
|
+
}
|
|
108
|
+
return final;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function addPath() {
|
|
112
|
+
if (addingPath) return;
|
|
113
|
+
addingPath = true; pathError = ''; changed();
|
|
114
|
+
try {
|
|
115
|
+
const result = await addCliToPath(toolKey);
|
|
116
|
+
info = { ...info, pathStatus: { dir: result.dir, onPath: true, canAdd: false } };
|
|
117
|
+
job = reduceInstallJob(null, { type: 'done', ok: true, status: info, path: { ...result, added: true } });
|
|
118
|
+
settle();
|
|
119
|
+
} catch (error) { pathError = error.message; }
|
|
120
|
+
finally { addingPath = false; changed(); }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// An install the server is already running (another window started it, or this page was reloaded during
|
|
124
|
+
// one): followed here as if this page had started it.
|
|
125
|
+
async function followRunning() {
|
|
126
|
+
if (installing) return;
|
|
127
|
+
const jobs = await getCliInstallJobs();
|
|
128
|
+
if (destroyed || installing || jobs?.[toolKey]?.state !== 'running') return;
|
|
129
|
+
installing = true;
|
|
130
|
+
job = reduceInstallJob(null, { type: 'start' });
|
|
131
|
+
changed();
|
|
132
|
+
let final = null;
|
|
133
|
+
try {
|
|
134
|
+
final = await followCliInstall(toolKey, { onEvent: apply });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
final = { type: 'done', ok: false, code: 'connection', error: error?.message || null };
|
|
137
|
+
} finally {
|
|
138
|
+
installing = false;
|
|
139
|
+
settle(final);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function recheck() {
|
|
144
|
+
if (checking) return info;
|
|
145
|
+
checking = true;
|
|
146
|
+
// Asking again starts over: an earlier failure is not repeated over a fresh answer.
|
|
147
|
+
if (job?.phase === 'failed') job = null;
|
|
148
|
+
changed();
|
|
149
|
+
try {
|
|
150
|
+
const next = await recheckCliStatus(toolKey);
|
|
151
|
+
info = next || info;
|
|
152
|
+
if (cliStateOf(info) === 'installed' || cliStateOf(info) === 'broken') forced = null;
|
|
153
|
+
} catch { /* the view stays as it is */ }
|
|
154
|
+
finally {
|
|
155
|
+
checking = false;
|
|
156
|
+
changed();
|
|
157
|
+
}
|
|
158
|
+
return info;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const unsubscribe = subscribeCliStatus(toolKey, (next) => {
|
|
162
|
+
info = next || null;
|
|
163
|
+
const state = cliStateOf(info);
|
|
164
|
+
if (state === 'installed') {
|
|
165
|
+
forced = null;
|
|
166
|
+
if (job?.phase === 'failed') job = null;
|
|
167
|
+
}
|
|
168
|
+
if (state === 'missing') {
|
|
169
|
+
if (!looked) { looked = true; followRunning().catch(() => {}); }
|
|
170
|
+
} else {
|
|
171
|
+
looked = false;
|
|
172
|
+
}
|
|
173
|
+
changed();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
view,
|
|
178
|
+
/** Tell `onChange` again (the panel was shown, or rebuilt). */
|
|
179
|
+
refresh: changed,
|
|
180
|
+
/** Sending is held back: the CLI is missing, broken, failed to install, or being installed. */
|
|
181
|
+
blocked: () => view().blocked,
|
|
182
|
+
/** A send was held back: `nudging` is true for a moment. */
|
|
183
|
+
nudge() {
|
|
184
|
+
if (!view().show) return;
|
|
185
|
+
clearTimeout(nudgeTimer);
|
|
186
|
+
nudgeTimer = setTimeout(() => { nudgeTimer = null; changed(); }, nudgeMs);
|
|
187
|
+
changed();
|
|
188
|
+
},
|
|
189
|
+
/**
|
|
190
|
+
* A session could not start the CLI ("CLI not found", a spawn error): "failed to start" with its words at
|
|
191
|
+
* once, then the server is asked again; its answer decides what stays.
|
|
192
|
+
*/
|
|
193
|
+
flagFailure(reason = null) {
|
|
194
|
+
forced = { reason: reason ? String(reason).slice(0, 300) : null };
|
|
195
|
+
changed();
|
|
196
|
+
recheckCliStatus(toolKey)
|
|
197
|
+
.then((next) => {
|
|
198
|
+
info = next || info;
|
|
199
|
+
const state = cliStateOf(info);
|
|
200
|
+
if (state === 'installed' || state === 'broken') forced = null;
|
|
201
|
+
changed();
|
|
202
|
+
})
|
|
203
|
+
.catch(() => {});
|
|
204
|
+
},
|
|
205
|
+
install,
|
|
206
|
+
recheck,
|
|
207
|
+
addPath,
|
|
208
|
+
clearDone,
|
|
209
|
+
/** Whether an install runs from this page now. */
|
|
210
|
+
installing: () => installing,
|
|
211
|
+
destroy() {
|
|
212
|
+
destroyed = true;
|
|
213
|
+
unsubscribe();
|
|
214
|
+
clearTimeout(doneTimer);
|
|
215
|
+
clearTimeout(nudgeTimer);
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// SynaBun Neural Interface — the CLI banner of the side panels: what it says
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
//
|
|
5
|
-
// What the Claude Code, Codex and OpenCode panels show above their transcript
|
|
5
|
+
// What the Claude Code, Codex and OpenCode panels show above their transcript (and the Antigravity panel's setup
|
|
6
|
+
// card words its install steps with)
|
|
6
7
|
// for one CLI: nothing while it is installed, "not installed" with a one-click
|
|
7
8
|
// Install (the vendor's own installer, run by the server: POST
|
|
8
9
|
// /api/cli/install/:tool) and the command to run by hand, the progress of an
|
|
@@ -35,6 +36,7 @@ export const CLI_INSTALL_COPY = Object.freeze({
|
|
|
35
36
|
stepPathAdded: 'Added {dir} to your PATH',
|
|
36
37
|
bodyMissing: 'Install it with one click from {vendor}\'s official installer, or run:',
|
|
37
38
|
bodyMissingManual: 'Install it with this command, then re-check:',
|
|
39
|
+
bodyMissingGuide: 'Follow its install guide, then re-check.',
|
|
38
40
|
bodyBroken: '{label} was found at {path}, but it could not be started:',
|
|
39
41
|
bodyBrokenVersion: '{label} {version} was found at {path}, but it could not be started:',
|
|
40
42
|
bodyBrokenLatched: '{label} is installed, but it could not be started:',
|
|
@@ -80,11 +82,11 @@ export const CLI_VENDORS = Object.freeze({
|
|
|
80
82
|
'claude-code': 'Anthropic',
|
|
81
83
|
codex: 'OpenAI',
|
|
82
84
|
opencode: 'OpenCode',
|
|
83
|
-
|
|
85
|
+
antigravity: 'Google',
|
|
84
86
|
});
|
|
85
87
|
|
|
86
88
|
/** The CLIs a server can install with one click (POST /api/cli/install/:tool). */
|
|
87
|
-
export const INSTALLABLE_CLIS = Object.freeze(['claude-code', 'codex', 'opencode']);
|
|
89
|
+
export const INSTALLABLE_CLIS = Object.freeze(['claude-code', 'codex', 'opencode', 'antigravity']);
|
|
88
90
|
|
|
89
91
|
/** The steps an install reports (`{ type: 'step' }`), in the order they can come. */
|
|
90
92
|
export const INSTALL_STEPS = Object.freeze(['start', 'download', 'verify', 'run', 'check', 'path']);
|
|
@@ -371,9 +373,10 @@ export function cliBannerView({ toolKey, label = '', info = null, forced = null,
|
|
|
371
373
|
recheck: true,
|
|
372
374
|
});
|
|
373
375
|
}
|
|
376
|
+
// No command at all (none from the server, and none on the page either): the guide only.
|
|
374
377
|
const text = offer
|
|
375
378
|
? t('bodyMissing', { vendor: CLI_VENDORS[toolKey] || name, label: name })
|
|
376
|
-
: t('bodyMissingManual');
|
|
379
|
+
: t(command ? 'bodyMissingManual' : 'bodyMissingGuide');
|
|
377
380
|
return Object.assign(view, {
|
|
378
381
|
show: true, blocked: true, kind: 'missing', tone: 'warn', icon: '!',
|
|
379
382
|
title: t('titleMissing', { label: name }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════
|
|
2
2
|
// SynaBun — CLI Status (shared)
|
|
3
|
-
// Detects whether Claude / Codex / OpenCode CLIs are installed by
|
|
3
|
+
// Detects whether Claude / Codex / OpenCode / Antigravity CLIs are installed by
|
|
4
4
|
// querying /api/system/tool-versions. Caches the response, fans out
|
|
5
5
|
// updates to subscribers, and auto-polls (every 30s) while at least
|
|
6
6
|
// one subscribed tool is not installed (missing, or found but broken).
|
|
@@ -17,33 +17,34 @@ import { cliStateOf, cliInstallOffer, INSTALLABLE_CLIS } from './cli-install-vie
|
|
|
17
17
|
|
|
18
18
|
export { cliStateOf, cliInstallOffer };
|
|
19
19
|
|
|
20
|
-
const TOOL_KEYS = ['claude-code', 'codex', 'opencode', '
|
|
20
|
+
const TOOL_KEYS = ['claude-code', 'codex', 'opencode', 'antigravity'];
|
|
21
21
|
|
|
22
22
|
// The vendors' install pages. The server's `guideUrl` wins when it sends one.
|
|
23
23
|
const CLI_DOC_URLS = {
|
|
24
24
|
'claude-code': 'https://code.claude.com/docs/en/setup',
|
|
25
25
|
'codex': 'https://learn.chatgpt.com/docs/codex/cli',
|
|
26
26
|
'opencode': 'https://opencode.ai/v2/docs',
|
|
27
|
-
'
|
|
27
|
+
'antigravity': 'https://antigravity.google/docs/getting-started',
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// Fallback only. The server computes the command that fits the machine (the
|
|
31
31
|
// vendor's recommended installer for its system, or the package manager the
|
|
32
|
-
// other CLIs came from: buildInstallCommand in lib/cli-update-planner.js
|
|
33
|
-
//
|
|
34
|
-
//
|
|
32
|
+
// other CLIs came from: buildInstallCommand in lib/cli-update-planner.js; for
|
|
33
|
+
// the Antigravity CLI, the command of the install its button runs there, which
|
|
34
|
+
// is Homebrew's cask on a Mac that has Homebrew) and returns it as
|
|
35
|
+
// `installCommand`. These apply only before the first tool-versions response.
|
|
35
36
|
const CLI_INSTALL_CMDS = {
|
|
36
37
|
'claude-code': { posix: 'curl -fsSL https://claude.ai/install.sh | bash', windows: 'irm https://claude.ai/install.ps1 | iex' },
|
|
37
38
|
'codex': { posix: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh', windows: 'powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"' },
|
|
38
39
|
'opencode': { posix: 'curl -fsSL https://opencode.ai/v2/install | bash', windows: 'winget install --id Anomaly.OpenCode --exact' },
|
|
39
|
-
'
|
|
40
|
+
'antigravity': { posix: 'curl -fsSL https://antigravity.google/cli/install.sh | bash', windows: 'irm https://antigravity.google/cli/install.ps1 | iex' },
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
const CLI_LABELS = {
|
|
43
44
|
'claude-code': 'Claude Code',
|
|
44
45
|
'codex': 'Codex',
|
|
45
46
|
'opencode': 'OpenCode',
|
|
46
|
-
'
|
|
47
|
+
'antigravity': 'Antigravity CLI',
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const POLL_INTERVAL_MS = 30000;
|
|
@@ -66,6 +66,7 @@ export const KEYS = {
|
|
|
66
66
|
ACTIVE_WORKSPACE: 'neural-active-workspace', // sessionStorage
|
|
67
67
|
GRID_SNAP: 'neural-grid-snap',
|
|
68
68
|
TOPRIGHT_TOOLS_HIDDEN: 'neural-topright-tools-hidden', // the workspace tools toggle is off ('true')
|
|
69
|
+
WHITEBOARD_TOOLBAR_HIDDEN: 'neural-whiteboard-toolbar-hidden', // the whiteboard rail (#wb-toolbar) is hidden behind the logo button ('true')
|
|
69
70
|
CATEGORIES_VISIBLE: 'neural-categories-visible',
|
|
70
71
|
WHITEBOARD: 'neural-whiteboard',
|
|
71
72
|
WHITEBOARD_RULESET_ALERT_DISMISSED: 'neural-whiteboard-ruleset-alert-dismissed',
|
|
@@ -127,7 +128,7 @@ export const DEFAULT_KEYBINDS = {
|
|
|
127
128
|
'toggle-assistant': 'Ctrl+a',
|
|
128
129
|
'launch-claude': '1',
|
|
129
130
|
'launch-codex': '2',
|
|
130
|
-
'launch-
|
|
131
|
+
'launch-antigravity': '3',
|
|
131
132
|
'launch-opencode': '4',
|
|
132
133
|
'launch-browser': 'b',
|
|
133
134
|
'launch-youtube': 'y',
|
|
@@ -154,7 +155,7 @@ export const KEYBIND_META = {
|
|
|
154
155
|
'toggle-assistant-panel': { label: 'Toggle Assistant Sidepanel', group: 'Launch App', icon: 'assistant' },
|
|
155
156
|
'launch-claude': { label: 'Claude Code', group: 'Launch App', icon: 'claude' },
|
|
156
157
|
'launch-codex': { label: 'Codex CLI', group: 'Launch App', icon: 'codex' },
|
|
157
|
-
'launch-
|
|
158
|
+
'launch-antigravity': { label: 'Antigravity CLI', group: 'Launch App', icon: 'antigravity' },
|
|
158
159
|
'launch-opencode': { label: 'OpenCode', group: 'Launch App', icon: 'opencode' },
|
|
159
160
|
'launch-browser': { label: 'Browser', group: 'Launch App', icon: 'browser' },
|
|
160
161
|
'launch-youtube': { label: 'YouTube', group: 'Launch App', icon: 'youtube' },
|